912ac3c5f190e5b468e55bef8e8449c9da039896
[platform/upstream/multipath-tools.git] / multipathd / uxlsnr.c
1 /*
2  * Original author : tridge@samba.org, January 2002
3  *
4  * Copyright (c) 2005 Christophe Varoqui
5  * Copyright (c) 2005 Benjamin Marzinski, Redhat
6  */
7
8 /*
9  * A simple domain socket listener
10  */
11 #define _GNU_SOURCE
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdarg.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <sys/ioctl.h>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22 #include <poll.h>
23 #include <sys/time.h>
24 #include <signal.h>
25 #include <stdbool.h>
26 #include <sys/inotify.h>
27 #include <sys/eventfd.h>
28 #include "checkers.h"
29 #include "debug.h"
30 #include "vector.h"
31 #include "structs.h"
32 #include "structs_vec.h"
33 #include "uxsock.h"
34 #include "defaults.h"
35 #include "config.h"
36 #include "mpath_cmd.h"
37 #include "time-util.h"
38 #include "util.h"
39
40 #include "main.h"
41 #include "cli.h"
42 #include "uxlsnr.h"
43 #include "strbuf.h"
44
45 /* state of client connection */
46 enum {
47         CLT_RECV,
48         CLT_PARSE,
49         CLT_LOCKED_WORK,
50         CLT_WORK,
51         CLT_SEND,
52 };
53
54 struct client {
55         struct list_head node;
56         struct timespec expires;
57         int state;
58         int fd;
59         vector cmdvec;
60         /* NUL byte at end */
61         char cmd[_MAX_CMD_LEN + 1];
62         struct strbuf reply;
63         struct handler *handler;
64         size_t cmd_len, len;
65         int error;
66         bool is_root;
67 };
68
69 /* Indices for array of poll fds */
70 enum {
71         POLLFD_UX = 0,
72         POLLFD_NOTIFY,
73         POLLFD_IDLE,
74         POLLFDS_BASE,
75 };
76
77 #define POLLFD_CHUNK (4096 / sizeof(struct pollfd))
78 /* Minimum mumber of pollfds to reserve for clients */
79 #define MIN_POLLS (POLLFD_CHUNK - POLLFDS_BASE)
80 /*
81  * Max number of client connections allowed
82  * During coldplug, there may be a large number of "multipath -u"
83  * processes connecting.
84  */
85 #define MAX_CLIENTS (16384 - POLLFDS_BASE)
86
87 /* Compile-time error if POLLFD_CHUNK is too small */
88 static __attribute__((unused)) char ___a[-(MIN_POLLS <= 0)];
89
90 static LIST_HEAD(clients);
91 static struct pollfd *polls;
92 static int notify_fd = -1;
93 static int idle_fd = -1;
94 static char *watch_config_dir;
95
96 static bool _socket_client_is_root(int fd)
97 {
98         socklen_t len = 0;
99         struct ucred uc;
100
101         len = sizeof(struct ucred);
102         if ((fd >= 0) &&
103             (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) &&
104             (uc.uid == 0))
105                         return true;
106
107         /* Treat error as not root client */
108         return false;
109 }
110
111 /*
112  * handle a new client joining
113  */
114 static void new_client(int ux_sock)
115 {
116         struct client *c;
117         struct sockaddr addr;
118         socklen_t len = sizeof(addr);
119         int fd;
120
121         fd = accept(ux_sock, &addr, &len);
122
123         if (fd == -1)
124                 return;
125
126         c = (struct client *)calloc(1, sizeof(*c));
127         if (!c) {
128                 close(fd);
129                 return;
130         }
131         INIT_LIST_HEAD(&c->node);
132         c->fd = fd;
133         c->state = CLT_RECV;
134         c->is_root = _socket_client_is_root(c->fd);
135
136         /* put it in our linked list */
137         list_add_tail(&c->node, &clients);
138 }
139
140 /*
141  * kill off a dead client
142  */
143 static void dead_client(struct client *c)
144 {
145         int fd = c->fd;
146         list_del_init(&c->node);
147         c->fd = -1;
148         reset_strbuf(&c->reply);
149         if (c->cmdvec)
150                 free_keys(c->cmdvec);
151         free(c);
152         close(fd);
153 }
154
155 static void free_polls (void)
156 {
157         if (polls)
158                 free(polls);
159         polls = NULL;
160 }
161
162 void uxsock_cleanup(void *arg)
163 {
164         struct client *client_loop;
165         struct client *client_tmp;
166         long ux_sock = (long)arg;
167
168         close(ux_sock);
169         close(notify_fd);
170         free(watch_config_dir);
171
172         list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
173                 dead_client(client_loop);
174         }
175
176         cli_exit();
177         free_polls();
178 }
179
180 void wakeup_cleanup(void *arg)
181 {
182         struct mutex_lock *lck = arg;
183         int fd = idle_fd;
184
185         idle_fd = -1;
186         set_wakeup_fn(lck, NULL);
187         if (fd != -1)
188                 close(fd);
189 }
190
191 struct watch_descriptors {
192         int conf_wd;
193         int dir_wd;
194 };
195
196 /* failing to set the watch descriptor is o.k. we just miss a warning
197  * message */
198 static void reset_watch(int notify_fd, struct watch_descriptors *wds,
199                         unsigned int *sequence_nr)
200 {
201         struct config *conf;
202         int dir_reset = 0;
203         int conf_reset = 0;
204
205         if (notify_fd == -1)
206                 return;
207
208         conf = get_multipath_config();
209         /* instead of repeatedly try to reset the inotify watch if
210          * the config directory or multipath.conf isn't there, just
211          * do it once per reconfigure */
212         if (*sequence_nr != conf->sequence_nr) {
213                 *sequence_nr = conf->sequence_nr;
214                 if (wds->conf_wd == -1)
215                         conf_reset = 1;
216                 if (!watch_config_dir || !conf->config_dir ||
217                     strcmp(watch_config_dir, conf->config_dir)) {
218                         dir_reset = 1;
219                         if (watch_config_dir)
220                                 free(watch_config_dir);
221                         if (conf->config_dir)
222                                 watch_config_dir = strdup(conf->config_dir);
223                         else
224                                 watch_config_dir = NULL;
225                 } else if (wds->dir_wd == -1)
226                         dir_reset = 1;
227         }
228         put_multipath_config(conf);
229
230         if (dir_reset) {
231                 if (wds->dir_wd != -1) {
232                         inotify_rm_watch(notify_fd, wds->dir_wd);
233                         wds->dir_wd = -1;
234                 }
235                 if (watch_config_dir) {
236                         wds->dir_wd = inotify_add_watch(notify_fd,
237                                                         watch_config_dir,
238                                                         IN_CLOSE_WRITE |
239                                                         IN_DELETE | IN_ONLYDIR);
240                         if (wds->dir_wd == -1)
241                                 condlog(3, "didn't set up notifications on %s: %m", watch_config_dir);
242                 }
243         }
244         if (conf_reset) {
245                 wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE,
246                                                  IN_CLOSE_WRITE);
247                 if (wds->conf_wd == -1)
248                         condlog(3, "didn't set up notifications on /etc/multipath.conf: %m");
249         }
250         return;
251 }
252
253 static void handle_inotify(int fd, struct watch_descriptors *wds)
254 {
255         char buff[1024]
256                 __attribute__ ((aligned(__alignof__(struct inotify_event))));
257         const struct inotify_event *event;
258         ssize_t len;
259         char *ptr;
260         int got_notify = 0;
261
262         for (;;) {
263                 len = read(fd, buff, sizeof(buff));
264                 if (len <= 0) {
265                         if (len < 0 && errno != EAGAIN) {
266                                 condlog(3, "error reading from inotify_fd");
267                                 if (wds->conf_wd != -1)
268                                         inotify_rm_watch(fd, wds->conf_wd);
269                                 if (wds->dir_wd != -1)
270                                         inotify_rm_watch(fd, wds->dir_wd);
271                                 wds->conf_wd = wds->dir_wd = -1;
272                         }
273                         break;
274                 }
275
276                 got_notify = 1;
277                 for (ptr = buff; ptr < buff + len;
278                      ptr += sizeof(struct inotify_event) + event->len) {
279                         event = (const struct inotify_event *) ptr;
280
281                         if (event->mask & IN_IGNORED) {
282                                 /* multipathd.conf may have been overwritten.
283                                  * Try once to reset the notification */
284                                 if (wds->conf_wd == event->wd)
285                                         wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE, IN_CLOSE_WRITE);
286                                 else if (wds->dir_wd == event->wd)
287                                         wds->dir_wd = -1;
288                         }
289                 }
290         }
291         if (got_notify)
292                 condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
293 }
294
295 static const struct timespec ts_zero = { .tv_sec = 0, };
296 static const struct timespec ts_max = { .tv_sec = LONG_MAX, .tv_nsec = 999999999 };
297
298 static struct timespec *get_soonest_timeout(struct timespec *ts)
299 {
300         struct timespec ts_min = ts_max, now;
301         bool any = false;
302         struct client *c;
303
304         list_for_each_entry(c, &clients, node) {
305                 if (timespeccmp(&c->expires, &ts_zero) != 0 &&
306                     timespeccmp(&c->expires, &ts_min) < 0) {
307                         ts_min = c->expires;
308                         any = true;
309                 }
310         }
311
312         if (!any)
313                 return NULL;
314
315         get_monotonic_time(&now);
316         timespecsub(&ts_min, &now, ts);
317         if (timespeccmp(ts, &ts_zero) < 0)
318                 *ts = ts_zero;
319
320         condlog(4, "%s: next client expires in %ld.%03lds", __func__,
321                 (long)ts->tv_sec, ts->tv_nsec / 1000000);
322         return ts;
323 }
324
325 static bool need_vecs_lock(void)
326 {
327         struct client *c;
328
329         list_for_each_entry(c, &clients, node) {
330                 if (c->state == CLT_LOCKED_WORK)
331                         return true;
332         }
333         return false;
334 }
335
336 static int parse_cmd(struct client *c)
337 {
338         int r;
339
340         r = get_cmdvec(c->cmd, &c->cmdvec);
341
342         if (r)
343                 return -r;
344
345         c->handler = find_handler_for_cmdvec(c->cmdvec);
346
347         if (!c->handler || !c->handler->fn)
348                 return -EINVAL;
349
350         return r;
351 }
352
353 static int execute_handler(struct client *c, struct vectors *vecs)
354 {
355
356         if (!c->handler || !c->handler->fn)
357                 return -EINVAL;
358
359         return c->handler->fn(c->cmdvec, &c->reply, vecs);
360 }
361
362 static void wakeup_listener(void)
363 {
364         uint64_t one = 1;
365
366         if (idle_fd != -1 &&
367             write(idle_fd, &one, sizeof(one)) != sizeof(one))
368                 condlog(1, "%s: failed", __func__);
369 }
370
371 static void drain_idle_fd(int fd)
372 {
373         uint64_t val;
374         int rc;
375
376         rc = read(fd, &val, sizeof(val));
377         condlog(4, "%s: %d, %"PRIu64, __func__, rc, val);
378 }
379
380 void default_reply(struct client *c, int r)
381 {
382         switch(r) {
383         case -EINVAL:
384         case -ESRCH:
385         case -ENOMEM:
386                 /* return codes from get_cmdvec() */
387                 genhelp_handler(c->cmd, -r, &c->reply);
388                 break;
389         case -EPERM:
390                 append_strbuf_str(&c->reply,
391                                   "permission deny: need to be root\n");
392                 break;
393         case -ETIMEDOUT:
394                 append_strbuf_str(&c->reply, "timeout\n");
395                 break;
396         case 0:
397                 append_strbuf_str(&c->reply, "ok\n");
398                 break;
399         default:
400                 /* cli_handler functions return 1 on unspecified error */
401                 append_strbuf_str(&c->reply, "fail\n");
402                 break;
403         }
404 }
405
406 static void set_client_state(struct client *c, int state)
407 {
408         switch(state)
409         {
410         case CLT_RECV:
411                 reset_strbuf(&c->reply);
412                 memset(c->cmd, '\0', sizeof(c->cmd));
413                 c->expires = ts_zero;
414                 c->error = 0;
415                 /* fallthrough */
416         case CLT_SEND:
417                 /* reuse these fields for next data transfer */
418                 c->len = c->cmd_len = 0;
419                 break;
420         default:
421                 break;
422         }
423         c->state = state;
424 }
425
426 enum {
427         STM_CONT,
428         STM_BREAK,
429 };
430
431 static int client_state_machine(struct client *c, struct vectors *vecs,
432                                 short revents)
433 {
434         ssize_t n;
435
436         condlog(4, "%s: cli[%d] poll=%x state=%d cmd=\"%s\" repl \"%s\"", __func__,
437                 c->fd, revents, c->state, c->cmd, get_strbuf_str(&c->reply));
438
439         switch (c->state) {
440         case CLT_RECV:
441                 if (!(revents & POLLIN))
442                         return STM_BREAK;
443                 if (c->cmd_len == 0) {
444                         size_t len;
445                         /*
446                          * We got POLLIN; assume that at least the length can
447                          * be read immediately.
448                          */
449                         get_monotonic_time(&c->expires);
450                         c->expires.tv_sec += uxsock_timeout / 1000;
451                         c->expires.tv_nsec += (uxsock_timeout % 1000) * 1000000;
452                         normalize_timespec(&c->expires);
453                         n = recv(c->fd, &len, sizeof(len), 0);
454                         if (n < (ssize_t)sizeof(len)) {
455                                 condlog(1, "%s: cli[%d]: failed to receive reply len: %zd",
456                                         __func__, c->fd, n);
457                                 c->error = -ECONNRESET;
458                         } else if (len <= 0 || len > _MAX_CMD_LEN) {
459                                 condlog(1, "%s: cli[%d]: invalid command length (%zu bytes)",
460                                         __func__, c->fd, len);
461                                 c->error = -ECONNRESET;
462                         } else {
463                                 c->cmd_len = len;
464                                 condlog(4, "%s: cli[%d]: connected", __func__, c->fd);
465                         }
466                         /* poll for data */
467                         return STM_BREAK;
468                 } else if (c->len < c->cmd_len) {
469                         n = recv(c->fd, c->cmd + c->len, c->cmd_len - c->len, 0);
470                         if (n <= 0 && errno != EINTR && errno != EAGAIN) {
471                                 condlog(1, "%s: cli[%d]: error in recv: %m",
472                                         __func__, c->fd);
473                                 c->error = -ECONNRESET;
474                                 return STM_BREAK;
475                         }
476                         c->len += n;
477                         if (c->len < c->cmd_len)
478                                 /* continue polling */
479                                 return STM_BREAK;
480                 }
481                 condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd);
482                 set_client_state(c, CLT_PARSE);
483                 return STM_CONT;
484
485         case CLT_PARSE:
486                 c->error = parse_cmd(c);
487                 if (!c->error) {
488                         /* Permission check */
489                         struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
490
491                         if (!c->is_root && kw->code != LIST) {
492                                 c->error = -EPERM;
493                                 condlog(0, "%s: cli[%d]: unauthorized cmd \"%s\"",
494                                         __func__, c->fd, c->cmd);
495                         }
496                 }
497                 if (c->error)
498                         set_client_state(c, CLT_SEND);
499                 else if (c->handler->locked)
500                         set_client_state(c, CLT_LOCKED_WORK);
501                 else
502                         set_client_state(c, CLT_WORK);
503                 return STM_CONT;
504
505         case CLT_LOCKED_WORK:
506                 if (trylock(&vecs->lock) == 0) {
507                         /* don't use cleanup_lock(), lest we wakeup ourselves */
508                         pthread_cleanup_push_cast(__unlock, &vecs->lock);
509                         c->error = execute_handler(c, vecs);
510                         pthread_cleanup_pop(1);
511                         condlog(4, "%s: cli[%d] grabbed lock", __func__, c->fd);
512                         free_keys(c->cmdvec);
513                         c->cmdvec = NULL;
514                         set_client_state(c, CLT_SEND);
515                         /* Wait for POLLOUT */
516                         return STM_BREAK;
517                 } else {
518                         condlog(4, "%s: cli[%d] waiting for lock", __func__, c->fd);
519                         return STM_BREAK;
520                 }
521
522         case CLT_WORK:
523                 c->error = execute_handler(c, vecs);
524                 free_keys(c->cmdvec);
525                 c->cmdvec = NULL;
526                 set_client_state(c, CLT_SEND);
527                 /* Wait for POLLOUT */
528                 return STM_BREAK;
529
530         case CLT_SEND:
531                 if (get_strbuf_len(&c->reply) == 0)
532                         default_reply(c, c->error);
533
534                 if (c->cmd_len == 0) {
535                         size_t len = get_strbuf_len(&c->reply) + 1;
536
537                         if (send(c->fd, &len, sizeof(len), MSG_NOSIGNAL)
538                             != sizeof(len))
539                                 c->error = -ECONNRESET;
540                         c->cmd_len = len;
541                         return STM_BREAK;
542                 }
543
544                 if (c->len < c->cmd_len) {
545                         const char *buf = get_strbuf_str(&c->reply);
546
547                         n = send(c->fd, buf + c->len, c->cmd_len, MSG_NOSIGNAL);
548                         if (n == -1) {
549                                 if (!(errno == EAGAIN || errno == EINTR))
550                                         c->error = -ECONNRESET;
551                         } else
552                                 c->len += n;
553                 }
554
555                 if (c->len >= c->cmd_len) {
556                         condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd, c->cmd_len);
557                         set_client_state(c, CLT_RECV);
558                 }
559                 return STM_BREAK;
560
561         default:
562                 return STM_BREAK;
563         }
564 }
565
566 static void check_timeout(struct client *c)
567 {
568         struct timespec now;
569
570         if (timespeccmp(&c->expires, &ts_zero) == 0)
571                 return;
572
573         get_monotonic_time(&now);
574         if (timespeccmp(&c->expires, &now) > 0)
575                 return;
576
577         condlog(2, "%s: cli[%d]: timed out at %ld.%03ld", __func__,
578                 c->fd, (long)c->expires.tv_sec, c->expires.tv_nsec / 1000000);
579
580         c->error = -ETIMEDOUT;
581         set_client_state(c, CLT_SEND);
582 }
583
584 static void handle_client(struct client *c, struct vectors *vecs, short revents)
585 {
586         if (revents & (POLLHUP|POLLERR)) {
587                 c->error = -ECONNRESET;
588                 return;
589         }
590
591         check_timeout(c);
592         while (client_state_machine(c, vecs, revents) == STM_CONT);
593 }
594
595 /*
596  * entry point
597  */
598 void *uxsock_listen(long ux_sock, void *trigger_data)
599 {
600         sigset_t mask;
601         int max_pfds = MIN_POLLS + POLLFDS_BASE;
602         /* conf->sequence_nr will be 1 when uxsock_listen is first called */
603         unsigned int sequence_nr = 0;
604         struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1 };
605         struct vectors *vecs = trigger_data;
606
607         condlog(3, "uxsock: startup listener");
608         polls = calloc(1, max_pfds * sizeof(*polls));
609         if (!polls) {
610                 condlog(0, "uxsock: failed to allocate poll fds");
611                 exit_daemon();
612                 return NULL;
613         }
614         notify_fd = inotify_init1(IN_NONBLOCK);
615         if (notify_fd == -1) /* it's fine if notifications fail */
616                 condlog(3, "failed to start up configuration notifications");
617
618         pthread_cleanup_push(wakeup_cleanup, &vecs->lock);
619         idle_fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
620         if (idle_fd == -1) {
621                 condlog(1, "failed to create idle fd");
622                 exit_daemon();
623         } else
624                 set_wakeup_fn(&vecs->lock, wakeup_listener);
625
626         sigfillset(&mask);
627         sigdelset(&mask, SIGINT);
628         sigdelset(&mask, SIGTERM);
629         sigdelset(&mask, SIGHUP);
630         sigdelset(&mask, SIGUSR1);
631         while (1) {
632                 struct client *c, *tmp;
633                 int i, n_pfds, poll_count, num_clients;
634                 struct timespec __timeout, *timeout;
635
636                 /* setup for a poll */
637                 num_clients = 0;
638                 list_for_each_entry(c, &clients, node) {
639                         num_clients++;
640                 }
641                 if (num_clients + POLLFDS_BASE > max_pfds) {
642                         struct pollfd *new;
643                         int n_new = max_pfds + POLLFD_CHUNK;
644
645                         new = realloc(polls, n_new * sizeof(*polls));
646                         if (new) {
647                                 max_pfds = n_new;
648                                 polls = new;
649                         } else {
650                                 condlog(1, "%s: realloc failure, %d clients not served",
651                                         __func__,
652                                         num_clients + POLLFDS_BASE - max_pfds);
653                                 num_clients = max_pfds - POLLFDS_BASE;
654                         }
655                 }
656                 if (num_clients < MAX_CLIENTS) {
657                         polls[POLLFD_UX].fd = ux_sock;
658                         polls[POLLFD_UX].events = POLLIN;
659                 } else {
660                         /*
661                          * New clients can't connect, num_clients won't grow
662                          * to MAX_CLIENTS or higher
663                          */
664                         condlog(1, "%s: max client connections reached, pausing polling",
665                                 __func__);
666                         polls[POLLFD_UX].fd = -1;
667                 }
668
669                 reset_watch(notify_fd, &wds, &sequence_nr);
670                 polls[POLLFD_NOTIFY].fd = notify_fd;
671                 if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
672                         polls[POLLFD_NOTIFY].events = 0;
673                 else
674                         polls[POLLFD_NOTIFY].events = POLLIN;
675
676                 polls[POLLFD_IDLE].fd = idle_fd;
677                 if (need_vecs_lock())
678                         polls[POLLFD_IDLE].events = POLLIN;
679                 else
680                         polls[POLLFD_IDLE].events = 0;
681
682                 /* setup the clients */
683                 i = POLLFDS_BASE;
684                 list_for_each_entry(c, &clients, node) {
685                         switch(c->state) {
686                         case CLT_RECV:
687                                 polls[i].events = POLLIN;
688                                 break;
689                         case CLT_SEND:
690                                 polls[i].events = POLLOUT;
691                                 break;
692                         default:
693                                 /* don't poll for this client */
694                                 continue;
695                         }
696                         polls[i].fd = c->fd;
697                         i++;
698                         if (i >= max_pfds)
699                                 break;
700                 }
701                 n_pfds = i;
702                 timeout = get_soonest_timeout(&__timeout);
703
704                 /* most of our life is spent in this call */
705                 poll_count = ppoll(polls, n_pfds, timeout, &mask);
706
707                 handle_signals(false);
708                 if (poll_count == -1) {
709                         if (errno == EINTR) {
710                                 handle_signals(true);
711                                 continue;
712                         }
713
714                         /* something went badly wrong! */
715                         condlog(0, "uxsock: poll failed with %d", errno);
716                         exit_daemon();
717                         break;
718                 }
719
720                 if (polls[POLLFD_IDLE].fd != -1 &&
721                     polls[POLLFD_IDLE].revents & POLLIN)
722                         drain_idle_fd(idle_fd);
723
724                 /* see if a client needs handling */
725                 list_for_each_entry_safe(c, tmp, &clients, node) {
726                         short revents = 0;
727
728                         for (i = POLLFDS_BASE; i < n_pfds; i++) {
729                                 if (polls[i].fd == c->fd) {
730                                         revents = polls[i].revents;
731                                         break;
732                                 }
733                         }
734
735                         handle_client(c, trigger_data, revents);
736
737                         if (c->error == -ECONNRESET) {
738                                 condlog(4, "cli[%d]: disconnected", c->fd);
739                                 dead_client(c);
740                                 if (i < n_pfds)
741                                         polls[i].fd = -1;
742                         }
743                 }
744                 /* see if we got a non-fatal signal */
745                 handle_signals(true);
746
747                 /* see if we got a new client */
748                 if (polls[POLLFD_UX].revents & POLLIN) {
749                         new_client(ux_sock);
750                 }
751
752                 /* handle inotify events on config files */
753                 if (polls[POLLFD_NOTIFY].revents & POLLIN)
754                         handle_inotify(notify_fd, &wds);
755         }
756
757         pthread_cleanup_pop(1);
758         return NULL;
759 }