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