074e4d3f5ef4abf1c1a626d09ed167f04d88ffee
[olsrd.git] / lib / dyn_gw / src / olsrd_dyn_gw.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004, Andreas Tonnesen(andreto@olsr.org)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /*
43  * -Threaded ping code added by Jens Nachtigall
44  * -HNA4 checking by bjoern riemer
45  */
46
47 #include <arpa/inet.h>
48
49 #include "olsr_types.h"
50 #include "olsrd_dyn_gw.h"
51 #include "olsr.h"
52 #include "defs.h"
53 #include "ipcalc.h"
54 #include "scheduler.h"
55 #include "log.h"
56 #include "routing_table.h"
57 #include "olsr_cfg.h"
58
59 #include <stdio.h>
60 #include <string.h>
61 #include <stdlib.h>
62 #include <sys/time.h>
63 #include <net/route.h>
64 #include <unistd.h>
65 #include <errno.h>
66 #include <time.h>
67 #ifndef _WIN32
68 #include <pthread.h>
69 #else /* _WIN32 */
70 #define WIN32_LEAN_AND_MEAN
71 #include <windows.h>
72 #undef interface
73
74 #define close(x) closesocket(x)
75
76 typedef HANDLE pthread_mutex_t;
77 typedef HANDLE pthread_t;
78
79 int pthread_create(HANDLE * Hand, void *Attr, void *(*Func) (void *), void *Arg);
80 int pthread_kill(HANDLE Hand, int Sig);
81 int pthread_mutex_init(HANDLE * Hand, void *Attr);
82 int pthread_mutex_lock(HANDLE * Hand);
83 int pthread_mutex_unlock(HANDLE * Hand);
84
85 struct ThreadPara {
86   void *(*Func) (void *);
87   void *Arg;
88 };
89 #endif /* _WIN32 */
90
91 static int hna_check_interval   = DEFAULT_HNA_CHECK_INTERVAL;
92 /* set default interval, in case none is given in the config file */
93 static int ping_check_interval = DEFAULT_PING_CHECK_INTERVAL;
94
95 /* list to store the Ping IP addresses given in the config file */
96 struct ping_list {
97   char *ping_address;
98   struct ping_list *next;
99 };
100
101 static struct ping_list *add_to_ping_list(const char *, struct ping_list *);
102
103 struct hna_list {
104   union olsr_ip_addr   hna_addr;
105   uint8_t              hna_prefixlen;
106   bool                 hna_added;
107   bool                 checked;
108   bool                 active;
109   struct hna_list *    next;
110 };
111
112 static struct hna_list *add_to_hna_list(struct hna_list *, union olsr_ip_addr *hna_addr, uint8_t hna_prefixlen);
113
114 struct hna_group {
115   struct hna_list *    hna_list;
116   struct ping_list *   ping_hosts;
117   bool                 probe_ok;
118   struct hna_group *   next;
119 };
120
121 bool hna_ping_check     = false;
122 static struct hna_group * hna_groups = NULL;
123
124 static struct hna_group *add_to_hna_group(struct hna_group *);
125
126 static void looped_checks(void *) __attribute__ ((noreturn));
127
128 static bool check_gw(union olsr_ip_addr *, uint8_t, struct ping_list *);
129
130 static int ping_is_possible(struct ping_list *);
131
132 /* Event function to register with the scheduler */
133 static void olsr_event_doing_hna(void *);
134
135 struct hna_list* find_hna(uint32_t src_addr, uint32_t src_mask);
136
137 char *get_ip_str(uint32_t address, char *s, size_t maxlen);
138 int update_routing(void);
139
140 /**
141  * read config file parameters
142  */
143 static int
144 set_plugin_ping(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused)))
145 {
146   union olsr_ip_addr foo_addr;
147
148   if (inet_pton(olsr_cnf->ip_version, value, &foo_addr) <= 0) {
149     OLSR_PRINTF(0, "Illegal IP address \"%s\"", value);
150     return 1;
151   }
152
153   if (hna_groups == NULL) { 
154     hna_groups = add_to_hna_group(hna_groups);
155     if (hna_groups == NULL)
156       return 1;
157   } else {
158     if (hna_groups->hna_list != NULL) {
159       hna_groups = add_to_hna_group(hna_groups);
160     }
161   }
162
163   hna_groups->ping_hosts = add_to_ping_list(value, hna_groups->ping_hosts);
164   hna_ping_check = true;
165   
166   return 0;
167 }
168
169 static int
170 set_plugin_hna(const char *value, void *data __attribute__ ((unused)), set_plugin_parameter_addon addon __attribute__ ((unused)))
171 {
172   union olsr_ip_addr temp_addr;
173   union olsr_ip_addr temp_mask;
174   char s_addr[128];
175   char s_mask[128];
176   
177   //Example: 192.168.1.0  255.255.255.0
178   int i = sscanf(value, "%127s %127s", s_addr, s_mask);
179   if (i != 2) {
180     OLSR_PRINTF(0, "Cannot get IP address and netmask from \"%s\"", value);
181     return 1;
182   }
183
184   if (inet_pton(olsr_cnf->ip_version, s_addr, &temp_addr) <= 0) {
185     OLSR_PRINTF(0, "Illegal IP address \"%s\"", s_addr);
186     return 1;
187   }
188
189   if (inet_pton(olsr_cnf->ip_version, s_mask, &temp_mask) <= 0) {
190     OLSR_PRINTF(0, "Illegal netmask \"%s\"", s_mask);
191     return 1;
192   }
193
194   if (hna_groups == NULL)
195   {
196     hna_groups = add_to_hna_group(hna_groups);
197     if (hna_groups == NULL) {
198       return 1;
199     }
200   }
201         
202   hna_groups->hna_list = add_to_hna_list(hna_groups->hna_list, &temp_addr, olsr_netmask_to_prefix(&temp_mask));
203   if (hna_groups->hna_list == NULL) {
204     return 1;
205   }
206   return 0;
207 }
208
209 static const struct olsrd_plugin_parameters plugin_parameters[] = {
210   {.name = "interval",      .set_plugin_parameter = &set_plugin_int,  .data = &ping_check_interval  },
211   {.name = "pinginterval",  .set_plugin_parameter = &set_plugin_int,  .data = &ping_check_interval  },
212   {.name = "checkinterval", .set_plugin_parameter = &set_plugin_int,  .data = &hna_check_interval   },
213   {.name = "ping",          .set_plugin_parameter = &set_plugin_ping, .data = NULL                  },
214   {.name = "hna",           .set_plugin_parameter = &set_plugin_hna,  .data = NULL                  },
215 };
216
217 void
218 olsrd_get_plugin_parameters(const struct olsrd_plugin_parameters **params, int *size)
219 {
220   *params = plugin_parameters;
221   *size = sizeof(plugin_parameters) / sizeof(*plugin_parameters);
222 }
223
224 /**
225  *Do initialization here
226  *
227  *
228  *This function is called by the my_init
229  *function in uolsrd_plugin.c
230  *It is ran _after_ register_olsr_param
231  */
232 int
233 olsrd_plugin_init(void)
234 {
235   pthread_t ping_thread;
236
237   if (hna_groups == NULL) {
238     hna_groups = add_to_hna_group(hna_groups);
239     if (hna_groups == NULL)
240       return 1;
241   }
242         
243   // Add a default gateway if the top entry was just a ping address
244   if (hna_groups->hna_list == NULL) {
245     union olsr_ip_addr temp_addr;
246     union olsr_ip_addr temp_mask;
247     
248     temp_addr.v4.s_addr = INET_ADDR;
249     temp_mask.v4.s_addr = INET_MASK;
250     hna_groups->hna_list = add_to_hna_list(hna_groups->hna_list, &temp_addr, olsr_netmask_to_prefix(&temp_mask));
251     if (hna_groups->hna_list == NULL) {
252       return 1;
253     }
254   }
255         
256   // Prepare all routing information
257   update_routing();
258   
259   if (hna_ping_check) {
260     pthread_create(&ping_thread, NULL, (void *(*)(void *))looped_checks, NULL);
261   } else {
262     struct hna_group *grp;
263     for (grp = hna_groups; grp; grp = grp->next) {
264       grp->probe_ok = true;
265     }
266   }
267
268   // Print the current configuration
269   {
270     struct hna_group *grp;
271     int i = 0;
272     for (grp = hna_groups; grp; grp = grp->next, ++i) {
273       struct hna_list *lst;
274       struct ping_list *png;
275             
276       olsr_printf(1, "Group %d:\n", i);
277       for (lst = grp->hna_list; lst; lst = lst->next) {
278         char addr[INET_ADDRSTRLEN];
279         olsr_printf(1, "  HNA %s\n", get_ip_str(lst->hna_addr.v4.s_addr, addr, INET_ADDRSTRLEN));
280       }
281       for (png = grp->ping_hosts; png; png = png->next) {
282         olsr_printf(1, "  PING %s\n", png->ping_address);
283       }
284     }
285   }
286
287   /* Register the GW check */
288   olsr_start_timer(hna_check_interval, 0, OLSR_TIMER_PERIODIC, &olsr_event_doing_hna, NULL, 0);
289   return 1;
290 }
291
292 /**
293  * Scheduled event to update the hna table,
294  * called from olsrd main thread to keep the hna table thread-safe
295  */
296 static void
297 olsr_event_doing_hna(void *foo __attribute__ ((unused)))
298 {
299   struct hna_group* grp;
300   struct hna_list *li;
301
302   update_routing();
303   
304   for (grp = hna_groups; grp; grp = grp->next) {
305     for (li = grp->hna_list; li; li = li->next) {
306       if (!li->hna_added) {
307         if (grp->probe_ok && li->active) {
308           olsr_printf(1, "Adding OLSR local HNA entry\n");
309           ip_prefix_list_add(&olsr_cnf->hna_entries, &li->hna_addr, li->hna_prefixlen);
310           li->hna_added = true;
311         }
312       } else {
313         if (!grp->probe_ok || !li->active) {
314           while (ip_prefix_list_remove(&olsr_cnf->hna_entries, &li->hna_addr, li->hna_prefixlen)) {
315             olsr_printf(1, "Removing OLSR local HNA entry\n");
316           }
317           li->hna_added = false;
318         }
319       }
320     }
321   }
322 }
323
324 /**
325  * the threaded function which happens within an endless loop,
326  * reiterated every "Interval" sec (as given in the config or
327  * the default value)
328  */
329 static void
330 looped_checks(void *foo __attribute__ ((unused)))
331 {
332   for (;;) {
333     struct hna_group *grp;
334     struct hna_list *li;
335     struct timespec remainder_spec;
336     /* the time to wait in "Interval" sec (see connfig), default=5sec */
337     struct timespec sleeptime_spec = { ping_check_interval, 0L };
338
339     for (grp = hna_groups; grp; grp = grp->next) {
340       for (li = grp->hna_list; li; li = li->next) {
341       
342                 // If this HNA is not active skip to the next one
343         if (!li->active)
344           continue;
345           
346         /* check for gw in table entry and if Ping IPs are given also do pings */
347         grp->probe_ok = check_gw(&li->hna_addr, li->hna_prefixlen, grp->ping_hosts);
348         if (grp->probe_ok)
349           break;        // Valid host found so we can bail out of the inner loop here
350       }
351     }
352
353     while (nanosleep(&sleeptime_spec, &remainder_spec) < 0)
354       sleeptime_spec = remainder_spec;
355   }
356   // return NULL;
357 }
358
359 /* -------------------------------------------------------------------------
360  * Function   : find_hna
361  * Description: Lookup an HNA that matches the specified parameters
362  * Input      : src_addr - IP address of the HNA to find
363  *              src_mask - Address mask of the HNA to find
364  * Output     : none
365  * Return     : The HNA specified or NULL when HNA not found
366  * Data Used  : none
367  * ------------------------------------------------------------------------- */
368 struct hna_list*
369 find_hna(uint32_t src_addr, uint32_t src_mask)
370 {
371   struct hna_group * grp;
372   struct hna_list *li;
373   union olsr_ip_addr mask;
374
375   for (grp = hna_groups; grp; grp = grp->next) {
376     for (li = grp->hna_list; li; li = li->next) {
377       olsr_prefix_to_netmask(&mask, li->hna_prefixlen);
378       if (li->hna_addr.v4.s_addr == src_addr && mask.v4.s_addr == src_mask) {
379         return li;
380       }
381     }
382   }
383   return NULL;
384 }
385
386 /* -------------------------------------------------------------------------
387  * Function   : get_ip_str
388  * Description: Convert the specified address to an IPv4 compatible string
389  * Input      : address - IPv4 address to convert to string 
390  *              s       - string buffer to contain the resulting string
391  *              maxlen  - maximum length of the string buffer 
392  * Output     : none
393  * Return     : Pointer to the string buffer containing the result
394  * Data Used  : none
395  * ------------------------------------------------------------------------- */
396 char *
397 get_ip_str(uint32_t address, char *s, size_t maxlen)
398 {
399   struct sockaddr_in v4;
400   
401   v4.sin_addr.s_addr = address;
402   inet_ntop(AF_INET, &v4.sin_addr, s, maxlen);
403
404   return s;
405 }
406
407 /* -------------------------------------------------------------------------
408  * Function   : update_routing
409  * Description: Mark the HNAs in the HNA list(s) corresponding to the results
410  *              found in the routing table. HNAs that are found in the routing
411  *              table will be marked as 'active', otherwise they'll remain
412  *              inactive.    
413  * Input      : nothing
414  * Output     : none
415  * Return     : -1 if an error occurred, 0 otherwise
416  * Data Used  : none
417  * ------------------------------------------------------------------------- */
418 int 
419 update_routing(void)
420 {
421   char buf[1024], iface[16];
422   uint32_t gate_addr, dest_addr, netmask;
423   unsigned int iflags;
424   int metric, refcnt, use;
425   struct hna_group *grp;
426   struct hna_list *li;
427   
428   FILE *fp = fopen(PROCENTRY_ROUTE, "r");
429   if (!fp) {
430     perror(PROCENTRY_ROUTE);
431     olsr_printf(1, "INET (IPv4) not configured in this system.\n");
432     return -1;
433   }
434
435   // Phase 1: reset the 'checked' flag, during the check of the routing table we 
436   // will (re)discover whether the HNA is valid or not.
437   for (grp = hna_groups; grp; grp = grp->next) {
438     for (li = grp->hna_list; li; li = li->next) {
439       li->checked = false;
440     }
441   }
442
443   /*
444      olsr_printf(1, "Genmask         Destination     Gateway         "
445      "Flags Metric Ref    Use Iface\n");
446    */
447   while (fgets(buf, sizeof(buf), fp)) {
448     struct hna_list *hna;
449     char s_addr[INET_ADDRSTRLEN], s_mask[INET_ADDRSTRLEN];
450     
451     int num = sscanf(buf, 
452                      "%15s %128X %128X %X %d %d %d %128X \n",
453                      iface, 
454                      &dest_addr,
455                      &gate_addr,
456                      &iflags, 
457                      &refcnt,
458                      &use,
459                      &metric,
460                      &netmask);
461     if (num < 8)
462       continue;
463
464     get_ip_str(dest_addr, s_addr, INET_ADDRSTRLEN);
465     get_ip_str(netmask, s_mask, INET_ADDRSTRLEN);
466     
467     hna = find_hna(dest_addr, netmask);
468     if (hna == NULL) {  // Entry not found, try the next one
469       continue;
470     }
471     
472     if ((iflags & RTF_UP) && (metric != olsr_cnf->fib_metric_default)) {
473       hna->checked = true;
474     }
475   }
476   fclose(fp);
477   
478   // Phase 2: now copy the 'checked' flag to the 'active' flag.
479   // The total check is a 2-phase process so the ping check loop won't be 
480   // disturbed too badly.
481   for (grp = hna_groups; grp; grp = grp->next) {
482     for (li = grp->hna_list; li; li = li->next) {
483       li->active = li->checked;
484     }
485   }
486         
487   return 0;
488 }
489
490 /* -------------------------------------------------------------------------
491  * Function   : check_gw
492  * Description: Check the specified gateway(s) by sending a ping
493  * Input      : addr      - the address of the HNA to which the ping is related
494  *              prefixlen - the length of the prefix for this HNA 
495  *              the_ping_list - list with related ping hosts
496  * Output     : none
497  * Return     : true if the ping host could be reached, false otherwise
498  * Data Used  : none
499  * ------------------------------------------------------------------------- */
500 static bool
501 check_gw(union olsr_ip_addr *addr, uint8_t prefixlen, struct ping_list *the_ping_list)
502 {
503   bool retval = false;
504   union olsr_ip_addr mask;
505
506   olsr_prefix_to_netmask(&mask, prefixlen);
507   
508   /* don't ping, if there was no "Ping" IP addr in the config file */
509   if (the_ping_list != NULL) {
510     /*validate the found inet gw by pinging */
511     if (ping_is_possible(the_ping_list)) {
512       olsr_printf(1, "HNA[%08x/%08x](ping is possible) detected in routing table.\n", addr->v4.s_addr, mask.v4.s_addr);
513       retval = true;
514     }
515   } else {
516     olsr_printf(1, "HNA[%08x/%08x] detected in routing table.\n", addr->v4.s_addr, mask.v4.s_addr);
517     retval = true;
518   }
519
520   if (retval == false) {
521     /* And we cast here since we get warnings on Win32 */
522     olsr_printf(1, "HNA[%08x/%08x] is invalid\n", (unsigned int)addr->v4.s_addr, (unsigned int)mask.v4.s_addr);
523   }
524   return retval;
525 }
526
527 /* -------------------------------------------------------------------------
528  * Function   : ping_is_possible
529  * Description: Ping the specified host(s)
530  * Input      : the_ping_list - the list of hosts to ping
531  * Output     : none
532  * Return     : 1 if any host responded, 0 otherwise
533  * Data Used  : none
534  * ------------------------------------------------------------------------- */
535 static int
536 ping_is_possible(struct ping_list *the_ping_list)
537 {
538   struct ping_list *list;
539   for (list = the_ping_list; list; list = list->next) {
540     char ping_command[50];
541     snprintf(ping_command, sizeof(ping_command), "ping -c 1 -q %s", list->ping_address);
542     olsr_printf(1, "\nDo ping on %s ...\n", list->ping_address);
543     if (system(ping_command) == 0) {
544       olsr_printf(1, "...OK\n\n");
545       return 1;
546     }
547     olsr_printf(1, "...FAILED\n\n");
548   }
549   return 0;
550 }
551
552 /* -------------------------------------------------------------------------
553  * Function   : add_to_ping_list
554  * Description: Add a new ping host to the list of ping hosts
555  * Input      : ping_address - the address of the ping host
556  *              the_ping_list - the list of ping hosts 
557  * Output     : none
558  * Return     : a pointer to the newly added ping host, i.e. start of the list
559  * Data Used  : none
560  * ------------------------------------------------------------------------- */
561 /* add the valid IPs to the head of the list */
562 static struct ping_list *
563 add_to_ping_list(const char *ping_address, struct ping_list *the_ping_list)
564 {
565   struct ping_list *new = calloc(1, sizeof(struct ping_list));
566   if (!new) {
567     fprintf(stderr, "DYN GW: Out of memory!\n");
568     olsr_syslog(OLSR_LOG_ERR, "DYN GW: Out of memory!\n");
569     exit(0);
570   }
571   new->ping_address = strdup(ping_address);
572   new->next = the_ping_list;
573   return new;
574 }
575
576 /* -------------------------------------------------------------------------
577  * Function   : add_to_hna_list
578  * Description: Add a new HNA entry to the list of HNA entries
579  * Input      : list_root - the start of the list with HNA entries
580  *              hna_addr  - the address of the new HNA entry
581  *              prefixlen - the prefix-length of the new HNA entry 
582  * Output     : none
583  * Return     : a pointer to the newly added HNA entry, i.e. start of the list
584  * Data Used  : none
585  * ------------------------------------------------------------------------- */
586 static struct hna_list *
587 add_to_hna_list(struct hna_list *list_root, union olsr_ip_addr *hna_addr, uint8_t hna_prefixlen)
588 {
589   struct hna_list *new = calloc(1, sizeof(struct hna_list));
590   if (new == NULL) {
591     fprintf(stderr, "DYN GW: Out of memory!\n");
592     olsr_syslog(OLSR_LOG_ERR, "DYN GW: Out of memory!\n");
593     exit(0);
594   }
595
596   new->hna_addr.v4 = hna_addr->v4;
597   new->hna_prefixlen = hna_prefixlen;
598   new->hna_added = false;
599   new->next = list_root;
600   return new;
601 }
602
603 /* -------------------------------------------------------------------------
604  * Function   : add_to_hna_group
605  * Description: Add a new HNA group to the list of HNA groups
606  * Input      : list_root - the start of the list with HNA groups
607  * Output     : none
608  * Return     : a pointer to the newly added HNA group, i.e. start of the list
609  * Data Used  : none
610  * ------------------------------------------------------------------------- */
611 static struct hna_group *
612 add_to_hna_group(struct hna_group *list_root)
613 {
614   struct hna_group *new = calloc(1, sizeof(struct hna_group));
615   if (new == NULL) {
616     fprintf(stderr, "DYN GW: Out of memory!\n");
617     olsr_syslog(OLSR_LOG_ERR, "DYN GW: Out of memory!\n");
618     exit(0);
619   }
620         
621   new->next =  list_root;
622   return new;
623 }
624
625
626 #ifdef _WIN32
627
628 /*
629  * Windows pthread compat stuff
630  */
631 static unsigned long __stdcall
632 ThreadWrapper(void *Para)
633 {
634   struct ThreadPara *Cast;
635   void *(*Func) (void *);
636   void *Arg;
637
638   Cast = (struct ThreadPara *)Para;
639
640   Func = Cast->Func;
641   Arg = Cast->Arg;
642
643   HeapFree(GetProcessHeap(), 0, Para);
644
645   Func(Arg);
646
647   return 0;
648 }
649
650 int
651 pthread_create(HANDLE * Hand, void *Attr __attribute__ ((unused)), void *(*Func) (void *), void *Arg)
652 {
653   struct ThreadPara *Para;
654   unsigned long ThreadId;
655
656   Para = HeapAlloc(GetProcessHeap(), 0, sizeof(struct ThreadPara));
657
658   if (Para == NULL)
659     return -1;
660
661   Para->Func = Func;
662   Para->Arg = Arg;
663
664   *Hand = CreateThread(NULL, 0, ThreadWrapper, Para, 0, &ThreadId);
665
666   if (*Hand == NULL)
667     return -1;
668
669   return 0;
670 }
671
672 int
673 pthread_kill(HANDLE Hand, int Sig __attribute__ ((unused)))
674 {
675   if (!TerminateThread(Hand, 0))
676     return -1;
677
678   return 0;
679 }
680
681 int
682 pthread_mutex_init(HANDLE * Hand, void *Attr __attribute__ ((unused)))
683 {
684   *Hand = CreateMutex(NULL, FALSE, NULL);
685
686   if (*Hand == NULL)
687     return -1;
688
689   return 0;
690 }
691
692 int
693 pthread_mutex_lock(HANDLE * Hand)
694 {
695   if (WaitForSingleObject(*Hand, INFINITE) == WAIT_FAILED)
696     return -1;
697
698   return 0;
699 }
700
701 int
702 pthread_mutex_unlock(HANDLE * Hand)
703 {
704   if (!ReleaseMutex(*Hand))
705     return -1;
706
707   return 0;
708 }
709
710 #endif /* _WIN32 */
711
712 /*
713  * Local Variables:
714  * c-basic-offset: 2
715  * indent-tabs-mode: nil
716  * End:
717  */