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