quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ares_event_configchg.c (19584B)


      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 
     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 
     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 
     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 
     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 
     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 
    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 
    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_
    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
    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 
    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 
    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 
    395 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
    396 {
    397   (void)configchg;
    398 
    399   /* Cleanup happens automatically */
    400 }
    401 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    737 void ares_event_configchg_destroy(ares_event_configchg_t *configchg)
    738 {
    739   /* No-op */
    740   (void)configchg;
    741 }
    742 
    743 #endif