Merge major BSD mutex and symbol conflict patches (bug #1816) (link patch still pending)
[asterisk/asterisk.git] / utils.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Utility functions
5  *
6  * Copyright (C) 2004, Digium
7  *
8  * This program is free software, distributed under the terms of
9  * the GNU General Public License
10  */
11
12 #include <ctype.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <pthread.h> 
16 #include <asterisk/lock.h>
17 #include <asterisk/utils.h>
18
19 #if defined(__FreeBSD__) || defined(__OpenBSD__)
20
21 /* duh? ERANGE value copied from web... */
22 #define ERANGE 34
23 #undef gethostbyname
24
25 AST_MUTEX_DEFINE_STATIC(__mutex);
26
27 static int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
28                             size_t buflen, struct hostent **result, 
29                             int *h_errnop) 
30 {
31         int hsave;
32         struct hostent *ph;
33         ast_mutex_lock(&__mutex); /* begin critical area */
34         hsave = h_errno;
35
36         ph = gethostbyname(name);
37         *h_errnop = h_errno; /* copy h_errno to *h_herrnop */
38         if (ph == NULL) {
39                 *result = NULL;
40         } else {
41                 char **p, **q;
42                 char *pbuf;
43                 int nbytes=0;
44                 int naddr=0, naliases=0;
45                 /* determine if we have enough space in buf */
46
47                 /* count how many addresses */
48                 for (p = ph->h_addr_list; *p != 0; p++) {
49                         nbytes += ph->h_length; /* addresses */
50                         nbytes += sizeof(*p); /* pointers */
51                         naddr++;
52                 }
53                 nbytes += sizeof(*p); /* one more for the terminating NULL */
54
55                 /* count how many aliases, and total length of strings */
56                 for (p = ph->h_aliases; *p != 0; p++) {
57                         nbytes += (strlen(*p)+1); /* aliases */
58                         nbytes += sizeof(*p);  /* pointers */
59                         naliases++;
60                 }
61                 nbytes += sizeof(*p); /* one more for the terminating NULL */
62
63                 /* here nbytes is the number of bytes required in buffer */
64                 /* as a terminator must be there, the minimum value is ph->h_length */
65                 if(nbytes > buflen) {
66                         *result = NULL;
67                         ast_mutex_unlock(&__mutex); /* end critical area */
68                         return ERANGE; /* not enough space in buf!! */
69                 }
70
71                 /* There is enough space. Now we need to do a deep copy! */
72                 /* Allocation in buffer:
73                         from [0] to [(naddr-1) * sizeof(*p)]:
74                         pointers to addresses
75                         at [naddr * sizeof(*p)]:
76                         NULL
77                         from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
78                         pointers to aliases
79                         at [(naddr+naliases+1) * sizeof(*p)]:
80                         NULL
81                         then naddr addresses (fixed length), and naliases aliases (asciiz).
82                 */
83
84                 *ret = *ph;   /* copy whole structure (not its address!) */
85
86                 /* copy addresses */
87                 q = (char **)buf; /* pointer to pointers area (type: char **) */
88                 ret->h_addr_list = q; /* update pointer to address list */
89                 pbuf = buf + ((naddr+naliases+2)*sizeof(*p)); /* skip that area */
90                 for (p = ph->h_addr_list; *p != 0; p++) {
91                         memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
92                         *q++ = pbuf; /* the pointer is the one inside buf... */
93                         pbuf += ph->h_length; /* advance pbuf */
94                 }
95                 *q++ = NULL; /* address list terminator */
96
97                 /* copy aliases */
98                 ret->h_aliases = q; /* update pointer to aliases list */
99                 for (p = ph->h_aliases; *p != 0; p++) {
100                         strcpy(pbuf, *p); /* copy alias strings */
101                         *q++ = pbuf; /* the pointer is the one inside buf... */
102                         pbuf += strlen(*p); /* advance pbuf */
103                         *pbuf++ = 0; /* string terminator */
104                 }
105                 *q++ = NULL; /* terminator */
106
107                 strcpy(pbuf, ph->h_name); /* copy alias strings */
108                 ret->h_name = pbuf;
109                 pbuf += strlen(ph->h_name); /* advance pbuf */
110                 *pbuf++ = 0; /* string terminator */
111
112                 *result = ret;  /* and let *result point to structure */
113
114         }
115         h_errno = hsave;  /* restore h_errno */
116         ast_mutex_unlock(&__mutex); /* end critical area */
117
118         return (*result == NULL); /* return 0 on success, non-zero on error */
119 }
120
121
122 #endif
123
124 struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
125 {
126         int res;
127         int herrno;
128         const char *s;
129         struct hostent *result = NULL;
130         /* Although it is perfectly legitimate to lookup a pure integer, for
131            the sake of the sanity of people who like to name their peers as
132            integers, we break with tradition and refuse to look up a
133            pure integer */
134         s = host;
135         while(s && *s) {
136                 if (!isdigit(*s))
137                         break;
138                 s++;
139         }
140         if (!s || !*s)
141                 return NULL;
142         res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
143
144         if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
145                 return NULL;
146         return &hp->hp;
147 }
148
149
150 /* This is a regression test for recursive mutexes.
151    test_for_thread_safety() will return 0 if recursive mutex locks are
152    working properly, and non-zero if they are not working properly. */
153
154 AST_MUTEX_DEFINE_STATIC(test_lock);
155 AST_MUTEX_DEFINE_STATIC(test_lock2);
156 static pthread_t test_thread; 
157 static int lock_count = 0;
158 static int test_errors = 0;
159
160 static void *test_thread_body(void *data) 
161
162   ast_mutex_lock(&test_lock);
163   lock_count += 10;
164   if(lock_count != 10) test_errors++;
165   ast_mutex_lock(&test_lock);
166   lock_count += 10;
167   if(lock_count != 20) test_errors++;
168   ast_mutex_lock(&test_lock2);
169   ast_mutex_unlock(&test_lock);
170   lock_count -= 10;
171   if(lock_count != 10) test_errors++;
172   ast_mutex_unlock(&test_lock);
173   lock_count -= 10;
174   ast_mutex_unlock(&test_lock2);
175   if(lock_count != 0) test_errors++;
176   return NULL;
177
178
179 int test_for_thread_safety(void)
180
181   ast_mutex_lock(&test_lock2);
182   ast_mutex_lock(&test_lock);
183   lock_count += 1;
184   ast_mutex_lock(&test_lock);
185   lock_count += 1;
186   pthread_create(&test_thread, NULL, test_thread_body, NULL); 
187   pthread_yield();
188   usleep(100);
189   if(lock_count != 2) test_errors++;
190   ast_mutex_unlock(&test_lock);
191   lock_count -= 1;
192   pthread_yield(); 
193   usleep(100); 
194   if(lock_count != 1) test_errors++;
195   ast_mutex_unlock(&test_lock);
196   lock_count -= 1;
197   if(lock_count != 0) test_errors++;
198   ast_mutex_unlock(&test_lock2);
199   pthread_yield();
200   usleep(100);
201   if(lock_count != 0) test_errors++;
202   pthread_join(test_thread, NULL);
203   return(test_errors);          /* return 0 on success. */
204 }