• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* MIT License
2  *
3  * Copyright (c) 2024 Brad House
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * SPDX-License-Identifier: MIT
25  */
26 #include "ares_private.h"
27 #include "ares_event.h"
28 
29 #if defined(__ANDROID__) && defined(CARES_THREADS)
30 
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)31 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
32                                         ares_event_thread_t     *e)
33 {
34   (void)configchg;
35   (void)e;
36   /* No ability */
37   return ARES_ENOTIMP;
38 }
39 
ares_event_configchg_destroy(ares_event_configchg_t * configchg)40 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
41 {
42   /* No-op */
43   (void)configchg;
44 }
45 
46 #elif defined(__linux__) && defined(CARES_THREADS)
47 
48 #  include <sys/inotify.h>
49 
50 struct ares_event_configchg {
51   int                  inotify_fd;
52   ares_event_thread_t *e;
53 };
54 
ares_event_configchg_destroy(ares_event_configchg_t * configchg)55 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
56 {
57   if (configchg == NULL) {
58     return; /* LCOV_EXCL_LINE: DefensiveCoding */
59   }
60 
61   /* Tell event system to stop monitoring for changes.  This will cause the
62    * cleanup to be called */
63   ares_event_update(NULL, configchg->e, ARES_EVENT_FLAG_NONE, NULL,
64                     configchg->inotify_fd, NULL, NULL, NULL);
65 }
66 
ares_event_configchg_free(void * data)67 static void ares_event_configchg_free(void *data)
68 {
69   ares_event_configchg_t *configchg = data;
70   if (configchg == NULL) {
71     return; /* LCOV_EXCL_LINE: DefensiveCoding */
72   }
73 
74   if (configchg->inotify_fd >= 0) {
75     close(configchg->inotify_fd);
76     configchg->inotify_fd = -1;
77   }
78 
79   ares_free(configchg);
80 }
81 
ares_event_configchg_cb(ares_event_thread_t * e,ares_socket_t fd,void * data,ares_event_flags_t flags)82 static void ares_event_configchg_cb(ares_event_thread_t *e, ares_socket_t fd,
83                                     void *data, ares_event_flags_t flags)
84 {
85   const ares_event_configchg_t *configchg = data;
86 
87   /* Some systems cannot read integer variables if they are not
88    * properly aligned. On other systems, incorrect alignment may
89    * decrease performance. Hence, the buffer used for reading from
90    * the inotify file descriptor should have the same alignment as
91    * struct inotify_event. */
92   unsigned char                 buf[4096]
93     __attribute__((aligned(__alignof__(struct inotify_event))));
94   const struct inotify_event *event;
95   ssize_t                     len;
96   ares_bool_t                 triggered = ARES_FALSE;
97 
98   (void)fd;
99   (void)flags;
100 
101   while (1) {
102     const unsigned char *ptr;
103 
104     len = read(configchg->inotify_fd, buf, sizeof(buf));
105     if (len <= 0) {
106       break;
107     }
108 
109     /* Loop over all events in the buffer. Says kernel will check the buffer
110      * size provided, so I assume it won't ever return partial events. */
111     for (ptr  = buf; ptr < buf + len;
112          ptr += sizeof(struct inotify_event) + event->len) {
113       event = (const struct inotify_event *)((const void *)ptr);
114 
115       if (event->len == 0 || ares_strlen(event->name) == 0) {
116         continue;
117       }
118 
119       if (ares_strcaseeq(event->name, "resolv.conf") ||
120           ares_strcaseeq(event->name, "nsswitch.conf")) {
121         triggered = ARES_TRUE;
122       }
123     }
124   }
125 
126   /* Only process after all events are read.  No need to process more often as
127    * we don't want to reload the config back to back */
128   if (triggered) {
129     ares_reinit(e->channel);
130   }
131 }
132 
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)133 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
134                                         ares_event_thread_t     *e)
135 {
136   ares_status_t           status = ARES_SUCCESS;
137   ares_event_configchg_t *c;
138 
139   (void)e;
140 
141   /* Not used by this implementation */
142   *configchg = NULL;
143 
144   c = ares_malloc_zero(sizeof(*c));
145   if (c == NULL) {
146     return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
147   }
148 
149   c->e          = e;
150   c->inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
151   if (c->inotify_fd == -1) {
152     status = ARES_ESERVFAIL; /* LCOV_EXCL_LINE: UntestablePath */
153     goto done;               /* LCOV_EXCL_LINE: UntestablePath */
154   }
155 
156   /* We need to monitor /etc/resolv.conf, /etc/nsswitch.conf */
157   if (inotify_add_watch(c->inotify_fd, "/etc",
158                         IN_CREATE | IN_MODIFY | IN_MOVED_TO | IN_ONLYDIR) ==
159       -1) {
160     status = ARES_ESERVFAIL; /* LCOV_EXCL_LINE: UntestablePath */
161     goto done;               /* LCOV_EXCL_LINE: UntestablePath */
162   }
163 
164   status =
165     ares_event_update(NULL, e, ARES_EVENT_FLAG_READ, ares_event_configchg_cb,
166                       c->inotify_fd, c, ares_event_configchg_free, NULL);
167 
168 done:
169   if (status != ARES_SUCCESS) {
170     ares_event_configchg_free(c);
171   } else {
172     *configchg = c;
173   }
174   return status;
175 }
176 
177 #elif defined(USE_WINSOCK) && defined(CARES_THREADS)
178 
179 #  include <winsock2.h>
180 #  include <iphlpapi.h>
181 #  include <stdio.h>
182 #  include <windows.h>
183 
184 struct ares_event_configchg {
185   HANDLE               ifchg_hnd;
186   HKEY                 regip4;
187   HANDLE               regip4_event;
188   HANDLE               regip4_wait;
189   HKEY                 regip6;
190   HANDLE               regip6_event;
191   HANDLE               regip6_wait;
192   ares_event_thread_t *e;
193 };
194 
ares_event_configchg_destroy(ares_event_configchg_t * configchg)195 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
196 {
197   if (configchg == NULL) {
198     return;
199   }
200 
201 #  ifdef HAVE_NOTIFYIPINTERFACECHANGE
202   if (configchg->ifchg_hnd != NULL) {
203     CancelMibChangeNotify2(configchg->ifchg_hnd);
204     configchg->ifchg_hnd = NULL;
205   }
206 #  endif
207 
208 #  ifdef HAVE_REGISTERWAITFORSINGLEOBJECT
209   if (configchg->regip4_wait != NULL) {
210     UnregisterWait(configchg->regip4_wait);
211     configchg->regip4_wait = NULL;
212   }
213 
214   if (configchg->regip6_wait != NULL) {
215     UnregisterWait(configchg->regip6_wait);
216     configchg->regip6_wait = NULL;
217   }
218 
219   if (configchg->regip4 != NULL) {
220     RegCloseKey(configchg->regip4);
221     configchg->regip4 = NULL;
222   }
223 
224   if (configchg->regip6 != NULL) {
225     RegCloseKey(configchg->regip6);
226     configchg->regip6 = NULL;
227   }
228 
229   if (configchg->regip4_event != NULL) {
230     CloseHandle(configchg->regip4_event);
231     configchg->regip4_event = NULL;
232   }
233 
234   if (configchg->regip6_event != NULL) {
235     CloseHandle(configchg->regip6_event);
236     configchg->regip6_event = NULL;
237   }
238 #  endif
239 
240   ares_free(configchg);
241 }
242 
243 
244 #  ifdef HAVE_NOTIFYIPINTERFACECHANGE
245 static void NETIOAPI_API_
ares_event_configchg_ip_cb(PVOID CallerContext,PMIB_IPINTERFACE_ROW Row,MIB_NOTIFICATION_TYPE NotificationType)246   ares_event_configchg_ip_cb(PVOID CallerContext, PMIB_IPINTERFACE_ROW Row,
247                              MIB_NOTIFICATION_TYPE NotificationType)
248 {
249   ares_event_configchg_t *configchg = CallerContext;
250   (void)Row;
251   (void)NotificationType;
252   ares_reinit(configchg->e->channel);
253 }
254 #  endif
255 
256 static ares_bool_t
ares_event_configchg_regnotify(ares_event_configchg_t * configchg)257   ares_event_configchg_regnotify(ares_event_configchg_t *configchg)
258 {
259 #  ifdef HAVE_REGISTERWAITFORSINGLEOBJECT
260 #    if defined(__WATCOMC__) && !defined(REG_NOTIFY_THREAD_AGNOSTIC)
261 #      define REG_NOTIFY_THREAD_AGNOSTIC 0x10000000L
262 #    endif
263   DWORD flags = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET |
264                 REG_NOTIFY_THREAD_AGNOSTIC;
265 
266   if (RegNotifyChangeKeyValue(configchg->regip4, TRUE, flags,
267                               configchg->regip4_event, TRUE) != ERROR_SUCCESS) {
268     return ARES_FALSE;
269   }
270 
271   if (RegNotifyChangeKeyValue(configchg->regip6, TRUE, flags,
272                               configchg->regip6_event, TRUE) != ERROR_SUCCESS) {
273     return ARES_FALSE;
274   }
275 #  else
276   (void)configchg;
277 #  endif
278   return ARES_TRUE;
279 }
280 
ares_event_configchg_reg_cb(PVOID lpParameter,BOOLEAN TimerOrWaitFired)281 static VOID CALLBACK ares_event_configchg_reg_cb(PVOID   lpParameter,
282                                                  BOOLEAN TimerOrWaitFired)
283 {
284   ares_event_configchg_t *configchg = lpParameter;
285   (void)TimerOrWaitFired;
286 
287   ares_reinit(configchg->e->channel);
288 
289   /* Re-arm, as its single-shot.  However, we don't know which one needs to
290    * be re-armed, so we just do both */
291   ares_event_configchg_regnotify(configchg);
292 }
293 
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)294 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
295                                         ares_event_thread_t     *e)
296 {
297   ares_status_t           status = ARES_SUCCESS;
298   ares_event_configchg_t *c      = NULL;
299 
300   c = ares_malloc_zero(sizeof(**configchg));
301   if (c == NULL) {
302     return ARES_ENOMEM;
303   }
304 
305   c->e = e;
306 
307 #  ifdef HAVE_NOTIFYIPINTERFACECHANGE
308   /* NOTE: If a user goes into the control panel and changes the network
309    *       adapter DNS addresses manually, this will NOT trigger a notification.
310    *       We've also tried listening on NotifyUnicastIpAddressChange(), but
311    *       that didn't get triggered either.
312    */
313   if (NotifyIpInterfaceChange(AF_UNSPEC, ares_event_configchg_ip_cb, c, FALSE,
314                               &c->ifchg_hnd) != NO_ERROR) {
315     status = ARES_ESERVFAIL;
316     goto done;
317   }
318 #  endif
319 
320 #  ifdef HAVE_REGISTERWAITFORSINGLEOBJECT
321   /* Monitor HKLM\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces
322    * and HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
323    * for changes via RegNotifyChangeKeyValue() */
324   if (RegOpenKeyExW(
325         HKEY_LOCAL_MACHINE,
326         L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces",
327         0, KEY_NOTIFY, &c->regip4) != ERROR_SUCCESS) {
328     status = ARES_ESERVFAIL;
329     goto done;
330   }
331 
332   if (RegOpenKeyExW(
333         HKEY_LOCAL_MACHINE,
334         L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces",
335         0, KEY_NOTIFY, &c->regip6) != ERROR_SUCCESS) {
336     status = ARES_ESERVFAIL;
337     goto done;
338   }
339 
340   c->regip4_event = CreateEvent(NULL, TRUE, FALSE, NULL);
341   if (c->regip4_event == NULL) {
342     status = ARES_ESERVFAIL;
343     goto done;
344   }
345 
346   c->regip6_event = CreateEvent(NULL, TRUE, FALSE, NULL);
347   if (c->regip6_event == NULL) {
348     status = ARES_ESERVFAIL;
349     goto done;
350   }
351 
352   if (!RegisterWaitForSingleObject(&c->regip4_wait, c->regip4_event,
353                                    ares_event_configchg_reg_cb, c, INFINITE,
354                                    WT_EXECUTEDEFAULT)) {
355     status = ARES_ESERVFAIL;
356     goto done;
357   }
358 
359   if (!RegisterWaitForSingleObject(&c->regip6_wait, c->regip6_event,
360                                    ares_event_configchg_reg_cb, c, INFINITE,
361                                    WT_EXECUTEDEFAULT)) {
362     status = ARES_ESERVFAIL;
363     goto done;
364   }
365 #  endif
366 
367   if (!ares_event_configchg_regnotify(c)) {
368     status = ARES_ESERVFAIL;
369     goto done;
370   }
371 
372 done:
373   if (status != ARES_SUCCESS) {
374     ares_event_configchg_destroy(c);
375   } else {
376     *configchg = c;
377   }
378 
379   return status;
380 }
381 
382 #elif defined(__APPLE__) && defined(CARES_THREADS)
383 
384 #  include <sys/types.h>
385 #  include <unistd.h>
386 #  include <notify.h>
387 #  include <dlfcn.h>
388 #  include <fcntl.h>
389 
390 struct ares_event_configchg {
391   int fd;
392   int token;
393 };
394 
ares_event_configchg_destroy(ares_event_configchg_t * configchg)395 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
396 {
397   (void)configchg;
398 
399   /* Cleanup happens automatically */
400 }
401 
ares_event_configchg_free(void * data)402 static void ares_event_configchg_free(void *data)
403 {
404   ares_event_configchg_t *configchg = data;
405   if (configchg == NULL) {
406     return;
407   }
408 
409   if (configchg->fd >= 0) {
410     notify_cancel(configchg->token);
411     /* automatically closes fd */
412     configchg->fd = -1;
413   }
414 
415   ares_free(configchg);
416 }
417 
ares_event_configchg_cb(ares_event_thread_t * e,ares_socket_t fd,void * data,ares_event_flags_t flags)418 static void ares_event_configchg_cb(ares_event_thread_t *e, ares_socket_t fd,
419                                     void *data, ares_event_flags_t flags)
420 {
421   ares_event_configchg_t *configchg = data;
422   ares_bool_t             triggered = ARES_FALSE;
423 
424   (void)fd;
425   (void)flags;
426 
427   while (1) {
428     int     t = 0;
429     ssize_t len;
430 
431     len = read(configchg->fd, &t, sizeof(t));
432 
433     if (len < (ssize_t)sizeof(t)) {
434       break;
435     }
436 
437     /* Token is read in network byte order (yeah, docs don't mention this) */
438     t = (int)ntohl(t);
439 
440     if (t != configchg->token) {
441       continue;
442     }
443 
444     triggered = ARES_TRUE;
445   }
446 
447   /* Only process after all events are read.  No need to process more often as
448    * we don't want to reload the config back to back */
449   if (triggered) {
450     ares_reinit(e->channel);
451   }
452 }
453 
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)454 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
455                                         ares_event_thread_t     *e)
456 {
457   ares_status_t status                               = ARES_SUCCESS;
458   void         *handle                               = NULL;
459   const char *(*pdns_configuration_notify_key)(void) = NULL;
460   const char *notify_key                             = NULL;
461   int         flags;
462   size_t      i;
463   const char *searchlibs[] = {
464     "/usr/lib/libSystem.dylib",
465     "/System/Library/Frameworks/SystemConfiguration.framework/"
466     "SystemConfiguration",
467     NULL
468   };
469 
470   *configchg = ares_malloc_zero(sizeof(**configchg));
471   if (*configchg == NULL) {
472     return ARES_ENOMEM;
473   }
474 
475   /* Load symbol as it isn't normally public */
476   for (i = 0; searchlibs[i] != NULL; i++) {
477     handle = dlopen(searchlibs[i], RTLD_LAZY);
478     if (handle == NULL) {
479       /* Fail, loop! */
480       continue;
481     }
482 
483     pdns_configuration_notify_key =
484       (const char *(*)(void))dlsym(handle, "dns_configuration_notify_key");
485     if (pdns_configuration_notify_key != NULL) {
486       break;
487     }
488 
489     /* Fail, loop! */
490     dlclose(handle);
491     handle = NULL;
492   }
493 
494   if (pdns_configuration_notify_key == NULL) {
495     status = ARES_ESERVFAIL;
496     goto done;
497   }
498 
499   notify_key = pdns_configuration_notify_key();
500   if (notify_key == NULL) {
501     status = ARES_ESERVFAIL;
502     goto done;
503   }
504 
505   if (notify_register_file_descriptor(notify_key, &(*configchg)->fd, 0,
506                                       &(*configchg)->token) !=
507       NOTIFY_STATUS_OK) {
508     status = ARES_ESERVFAIL;
509     goto done;
510   }
511 
512   /* Set file descriptor to non-blocking */
513   flags = fcntl((*configchg)->fd, F_GETFL, 0);
514   fcntl((*configchg)->fd, F_SETFL, flags | O_NONBLOCK);
515 
516   /* Register file descriptor with event subsystem */
517   status = ares_event_update(NULL, e, ARES_EVENT_FLAG_READ,
518                              ares_event_configchg_cb, (*configchg)->fd,
519                              *configchg, ares_event_configchg_free, NULL);
520 
521 done:
522   if (status != ARES_SUCCESS) {
523     ares_event_configchg_free(*configchg);
524     *configchg = NULL;
525   }
526 
527   if (handle) {
528     dlclose(handle);
529   }
530 
531   return status;
532 }
533 
534 #elif defined(HAVE_STAT) && !defined(_WIN32) && defined(CARES_THREADS)
535 #  ifdef HAVE_SYS_TYPES_H
536 #    include <sys/types.h>
537 #  endif
538 #  ifdef HAVE_SYS_STAT_H
539 #    include <sys/stat.h>
540 #  endif
541 
542 typedef struct {
543   size_t size;
544   time_t mtime;
545 } fileinfo_t;
546 
547 struct ares_event_configchg {
548   ares_bool_t          isup;
549   ares_thread_t       *thread;
550   ares_htable_strvp_t *filestat;
551   ares_thread_mutex_t *lock;
552   ares_thread_cond_t  *wake;
553   const char          *resolvconf_path;
554   ares_event_thread_t *e;
555 };
556 
config_change_check(ares_htable_strvp_t * filestat,const char * resolvconf_path)557 static ares_status_t config_change_check(ares_htable_strvp_t *filestat,
558                                          const char          *resolvconf_path)
559 {
560   size_t      i;
561   const char *configfiles[16];
562   ares_bool_t changed = ARES_FALSE;
563   size_t      cnt = 0;
564 
565   memset(configfiles, 0, sizeof(configfiles));
566 
567   configfiles[cnt++] = resolvconf_path;
568   configfiles[cnt++] = "/etc/nsswitch.conf";
569 #ifdef _AIX
570   configfiles[cnt++] = "/etc/netsvc.conf";
571 #endif
572 #ifdef __osf /* Tru64 */
573   configfiles[cnt++] = "/etc/svc.conf";
574 #endif
575 #ifdef __QNX__
576   configfiles[cnt++] = "/etc/net.cfg";
577 #endif
578   configfiles[cnt++] = NULL;
579 
580   for (i = 0; configfiles[i] != NULL; i++) {
581     fileinfo_t *fi = ares_htable_strvp_get_direct(filestat, configfiles[i]);
582     struct stat st;
583 
584     if (stat(configfiles[i], &st) == 0) {
585       if (fi == NULL) {
586         fi = ares_malloc_zero(sizeof(*fi));
587         if (fi == NULL) {
588           return ARES_ENOMEM;
589         }
590         if (!ares_htable_strvp_insert(filestat, configfiles[i], fi)) {
591           ares_free(fi);
592           return ARES_ENOMEM;
593         }
594       }
595       if (fi->size != (size_t)st.st_size || fi->mtime != (time_t)st.st_mtime) {
596         changed = ARES_TRUE;
597       }
598       fi->size  = (size_t)st.st_size;
599       fi->mtime = (time_t)st.st_mtime;
600     } else if (fi != NULL) {
601       /* File no longer exists, remove */
602       ares_htable_strvp_remove(filestat, configfiles[i]);
603       changed = ARES_TRUE;
604     }
605   }
606 
607   if (changed) {
608     return ARES_SUCCESS;
609   }
610   return ARES_ENOTFOUND;
611 }
612 
ares_event_configchg_thread(void * arg)613 static void *ares_event_configchg_thread(void *arg)
614 {
615   ares_event_configchg_t *c = arg;
616 
617   ares_thread_mutex_lock(c->lock);
618   while (c->isup) {
619     ares_status_t status;
620 
621     if (ares_thread_cond_timedwait(c->wake, c->lock, 30000) != ARES_ETIMEOUT) {
622       continue;
623     }
624 
625     /* make sure status didn't change even though we got a timeout */
626     if (!c->isup) {
627       break;
628     }
629 
630     status = config_change_check(c->filestat, c->resolvconf_path);
631     if (status == ARES_SUCCESS) {
632       ares_reinit(c->e->channel);
633     }
634   }
635 
636   ares_thread_mutex_unlock(c->lock);
637   return NULL;
638 }
639 
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)640 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
641                                         ares_event_thread_t     *e)
642 {
643   ares_status_t           status = ARES_SUCCESS;
644   ares_event_configchg_t *c      = NULL;
645 
646   *configchg = NULL;
647 
648   c = ares_malloc_zero(sizeof(*c));
649   if (c == NULL) {
650     status = ARES_ENOMEM;
651     goto done;
652   }
653 
654   c->e = e;
655 
656   c->filestat = ares_htable_strvp_create(ares_free);
657   if (c->filestat == NULL) {
658     status = ARES_ENOMEM;
659     goto done;
660   }
661 
662   c->wake = ares_thread_cond_create();
663   if (c->wake == NULL) {
664     status = ARES_ENOMEM;
665     goto done;
666   }
667 
668   c->lock = ares_thread_mutex_create();
669   if (c->lock == NULL) {
670     status = ARES_ENOMEM;
671     goto done;
672   }
673 
674   c->resolvconf_path = c->e->channel->resolvconf_path;
675   if (c->resolvconf_path == NULL) {
676     c->resolvconf_path = PATH_RESOLV_CONF;
677   }
678 
679   status = config_change_check(c->filestat, c->resolvconf_path);
680   if (status == ARES_ENOMEM) {
681     goto done;
682   }
683 
684   c->isup = ARES_TRUE;
685   status  = ares_thread_create(&c->thread, ares_event_configchg_thread, c);
686 
687 done:
688   if (status != ARES_SUCCESS) {
689     ares_event_configchg_destroy(c);
690   } else {
691     *configchg = c;
692   }
693   return status;
694 }
695 
ares_event_configchg_destroy(ares_event_configchg_t * configchg)696 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
697 {
698   if (configchg == NULL) {
699     return;
700   }
701 
702   if (configchg->lock) {
703     ares_thread_mutex_lock(configchg->lock);
704   }
705 
706   configchg->isup = ARES_FALSE;
707   if (configchg->wake) {
708     ares_thread_cond_signal(configchg->wake);
709   }
710 
711   if (configchg->lock) {
712     ares_thread_mutex_unlock(configchg->lock);
713   }
714 
715   if (configchg->thread) {
716     void *rv = NULL;
717     ares_thread_join(configchg->thread, &rv);
718   }
719 
720   ares_thread_mutex_destroy(configchg->lock);
721   ares_thread_cond_destroy(configchg->wake);
722   ares_htable_strvp_destroy(configchg->filestat);
723   ares_free(configchg);
724 }
725 
726 #else
727 
ares_event_configchg_init(ares_event_configchg_t ** configchg,ares_event_thread_t * e)728 ares_status_t ares_event_configchg_init(ares_event_configchg_t **configchg,
729                                         ares_event_thread_t     *e)
730 {
731   (void)configchg;
732   (void)e;
733   /* No ability */
734   return ARES_ENOTIMP;
735 }
736 
ares_event_configchg_destroy(ares_event_configchg_t * configchg)737 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
738 {
739   /* No-op */
740   (void)configchg;
741 }
742 
743 #endif
744