020d7a9bb0dc7aa3ada69a8172cf77c01f6f30be
[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 "checkers.h"
28 #include "memory.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
39 #include "main.h"
40 #include "cli.h"
41 #include "uxlsnr.h"
42
43 struct timespec sleep_time = {5, 0};
44
45 struct client {
46         struct list_head node;
47         int fd;
48 };
49
50 #define MIN_POLLS 1023
51
52 LIST_HEAD(clients);
53 pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
54 struct pollfd *polls;
55 static int notify_fd = -1;
56 static char *watch_config_dir;
57
58 static bool _socket_client_is_root(int fd);
59
60 static bool _socket_client_is_root(int fd)
61 {
62         socklen_t len = 0;
63         struct ucred uc;
64
65         len = sizeof(struct ucred);
66         if ((fd >= 0) &&
67             (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) &&
68             (uc.uid == 0))
69                         return true;
70
71         /* Treat error as not root client */
72         return false;
73 }
74
75 /*
76  * handle a new client joining
77  */
78 static void new_client(int ux_sock)
79 {
80         struct client *c;
81         struct sockaddr addr;
82         socklen_t len = sizeof(addr);
83         int fd;
84
85         fd = accept(ux_sock, &addr, &len);
86
87         if (fd == -1)
88                 return;
89
90         c = (struct client *)MALLOC(sizeof(*c));
91         if (!c) {
92                 close(fd);
93                 return;
94         }
95         memset(c, 0, sizeof(*c));
96         INIT_LIST_HEAD(&c->node);
97         c->fd = fd;
98
99         /* put it in our linked list */
100         pthread_mutex_lock(&client_lock);
101         list_add_tail(&c->node, &clients);
102         pthread_mutex_unlock(&client_lock);
103 }
104
105 /*
106  * kill off a dead client
107  */
108 static void _dead_client(struct client *c)
109 {
110         int fd = c->fd;
111         list_del_init(&c->node);
112         c->fd = -1;
113         FREE(c);
114         close(fd);
115 }
116
117 static void dead_client(struct client *c)
118 {
119         pthread_cleanup_push(cleanup_lock, &client_lock);
120         pthread_mutex_lock(&client_lock);
121         _dead_client(c);
122         pthread_cleanup_pop(1);
123 }
124
125 void free_polls (void)
126 {
127         if (polls)
128                 FREE(polls);
129 }
130
131 void check_timeout(struct timespec start_time, char *inbuf,
132                    unsigned int timeout)
133 {
134         struct timespec diff_time, end_time;
135
136         if (start_time.tv_sec) {
137                 unsigned long msecs;
138
139                 get_monotonic_time(&end_time);
140                 timespecsub(&end_time, &start_time, &diff_time);
141                 msecs = diff_time.tv_sec * 1000 +
142                         diff_time.tv_nsec / (1000 * 1000);
143                 if (msecs > timeout)
144                         condlog(2, "cli cmd '%s' timeout reached "
145                                 "after %lu.%06lu secs", inbuf,
146                                 diff_time.tv_sec, diff_time.tv_nsec / 1000);
147         }
148 }
149
150 void uxsock_cleanup(void *arg)
151 {
152         struct client *client_loop;
153         struct client *client_tmp;
154         long ux_sock = (long)arg;
155
156         close(ux_sock);
157         close(notify_fd);
158         free(watch_config_dir);
159
160         pthread_mutex_lock(&client_lock);
161         list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
162                 _dead_client(client_loop);
163         }
164         pthread_mutex_unlock(&client_lock);
165
166         cli_exit();
167         free_polls();
168 }
169
170 struct watch_descriptors {
171         int conf_wd;
172         int dir_wd;
173 };
174
175 /* failing to set the watch descriptor is o.k. we just miss a warning
176  * message */
177 static void reset_watch(int notify_fd, struct watch_descriptors *wds,
178                         unsigned int *sequence_nr)
179 {
180         struct config *conf;
181         int dir_reset = 0;
182         int conf_reset = 0;
183
184         if (notify_fd == -1)
185                 return;
186
187         conf = get_multipath_config();
188         /* instead of repeatedly try to reset the inotify watch if
189          * the config directory or multipath.conf isn't there, just
190          * do it once per reconfigure */
191         if (*sequence_nr != conf->sequence_nr) {
192                 *sequence_nr = conf->sequence_nr;
193                 if (wds->conf_wd == -1)
194                         conf_reset = 1;
195                 if (!watch_config_dir || !conf->config_dir ||
196                     strcmp(watch_config_dir, conf->config_dir)) {
197                         dir_reset = 1;
198                         if (watch_config_dir)
199                                 free(watch_config_dir);
200                         if (conf->config_dir)
201                                 watch_config_dir = strdup(conf->config_dir);
202                         else
203                                 watch_config_dir = NULL;
204                 } else if (wds->dir_wd == -1)
205                         dir_reset = 1;
206         }
207         put_multipath_config(conf);
208
209         if (dir_reset) {
210                 if (wds->dir_wd != -1) {
211                         inotify_rm_watch(notify_fd, wds->dir_wd);
212                         wds->dir_wd = -1;
213                 }
214                 if (watch_config_dir) {
215                         wds->dir_wd = inotify_add_watch(notify_fd,
216                                                         watch_config_dir,
217                                                         IN_CLOSE_WRITE |
218                                                         IN_DELETE | IN_ONLYDIR);
219                         if (wds->dir_wd == -1)
220                                 condlog(3, "didn't set up notifications on %s: %m", watch_config_dir);
221                 }
222         }
223         if (conf_reset) {
224                 wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE,
225                                                  IN_CLOSE_WRITE);
226                 if (wds->conf_wd == -1)
227                         condlog(3, "didn't set up notifications on /etc/multipath.conf: %m");
228         }
229         return;
230 }
231
232 static void handle_inotify(int fd, struct watch_descriptors *wds)
233 {
234         char buff[1024]
235                 __attribute__ ((aligned(__alignof__(struct inotify_event))));
236         const struct inotify_event *event;
237         ssize_t len;
238         char *ptr;
239         int got_notify = 0;
240
241         for (;;) {
242                 len = read(fd, buff, sizeof(buff));
243                 if (len <= 0) {
244                         if (len < 0 && errno != EAGAIN) {
245                                 condlog(3, "error reading from inotify_fd");
246                                 if (wds->conf_wd != -1)
247                                         inotify_rm_watch(fd, wds->conf_wd);
248                                 if (wds->dir_wd != -1)
249                                         inotify_rm_watch(fd, wds->dir_wd);
250                                 wds->conf_wd = wds->dir_wd = -1;
251                         }
252                         break;
253                 }
254
255                 got_notify = 1;
256                 for (ptr = buff; ptr < buff + len;
257                      ptr += sizeof(struct inotify_event) + event->len) {
258                         event = (const struct inotify_event *) ptr;
259
260                         if (event->mask & IN_IGNORED) {
261                                 /* multipathd.conf may have been overwritten.
262                                  * Try once to reset the notification */
263                                 if (wds->conf_wd == event->wd)
264                                         wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE, IN_CLOSE_WRITE);
265                                 else if (wds->dir_wd == event->wd)
266                                         wds->dir_wd = -1;
267                         }
268                 }
269         }
270         if (got_notify)
271                 condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
272 }
273
274 /*
275  * entry point
276  */
277 void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
278                      void * trigger_data)
279 {
280         int rlen;
281         char *inbuf;
282         char *reply;
283         sigset_t mask;
284         int old_clients = MIN_POLLS;
285         /* conf->sequence_nr will be 1 when uxsock_listen is first called */
286         unsigned int sequence_nr = 0;
287         struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1 };
288
289         condlog(3, "uxsock: startup listener");
290         polls = (struct pollfd *)MALLOC((MIN_POLLS + 2) * sizeof(struct pollfd));
291         if (!polls) {
292                 condlog(0, "uxsock: failed to allocate poll fds");
293                 exit_daemon();
294         }
295         notify_fd = inotify_init1(IN_NONBLOCK);
296         if (notify_fd == -1) /* it's fine if notifications fail */
297                 condlog(3, "failed to start up configuration notifications");
298         sigfillset(&mask);
299         sigdelset(&mask, SIGINT);
300         sigdelset(&mask, SIGTERM);
301         sigdelset(&mask, SIGHUP);
302         sigdelset(&mask, SIGUSR1);
303         while (1) {
304                 struct client *c, *tmp;
305                 int i, poll_count, num_clients;
306
307                 /* setup for a poll */
308                 pthread_mutex_lock(&client_lock);
309                 num_clients = 0;
310                 list_for_each_entry(c, &clients, node) {
311                         num_clients++;
312                 }
313                 if (num_clients != old_clients) {
314                         struct pollfd *new;
315                         if (num_clients <= MIN_POLLS && old_clients > MIN_POLLS) {
316                                 new = REALLOC(polls, (2 + MIN_POLLS) *
317                                                 sizeof(struct pollfd));
318                         } else if (num_clients <= MIN_POLLS && old_clients <= MIN_POLLS) {
319                                 new = polls;
320                         } else {
321                                 new = REALLOC(polls, (2 + num_clients) *
322                                                 sizeof(struct pollfd));
323                         }
324                         if (!new) {
325                                 pthread_mutex_unlock(&client_lock);
326                                 condlog(0, "%s: failed to realloc %d poll fds",
327                                         "uxsock", 2 + num_clients);
328                                 sched_yield();
329                                 continue;
330                         }
331                         old_clients = num_clients;
332                         polls = new;
333                 }
334                 polls[0].fd = ux_sock;
335                 polls[0].events = POLLIN;
336
337                 reset_watch(notify_fd, &wds, &sequence_nr);
338                 if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
339                         polls[1].fd = -1;
340                 else
341                         polls[1].fd = notify_fd;
342                 polls[1].events = POLLIN;
343
344                 /* setup the clients */
345                 i = 2;
346                 list_for_each_entry(c, &clients, node) {
347                         polls[i].fd = c->fd;
348                         polls[i].events = POLLIN;
349                         i++;
350                 }
351                 pthread_mutex_unlock(&client_lock);
352
353                 /* most of our life is spent in this call */
354                 poll_count = ppoll(polls, i, &sleep_time, &mask);
355
356                 handle_signals(false);
357                 if (poll_count == -1) {
358                         if (errno == EINTR) {
359                                 handle_signals(true);
360                                 continue;
361                         }
362
363                         /* something went badly wrong! */
364                         condlog(0, "uxsock: poll failed with %d", errno);
365                         exit_daemon();
366                         break;
367                 }
368
369                 if (poll_count == 0) {
370                         handle_signals(true);
371                         continue;
372                 }
373
374                 /*
375                  * Client connection. We shouldn't answer while we're
376                  * configuring - nothing may be configured yet.
377                  * But we can't wait forever either, because this thread
378                  * must handle signals. So wait a short while only.
379                  */
380                 if (wait_for_state_change_if(DAEMON_CONFIGURE, 10)
381                     == DAEMON_CONFIGURE) {
382                         handle_signals(false);
383                         continue;
384                 }
385
386                 /* see if a client wants to speak to us */
387                 for (i = 2; i < num_clients + 2; i++) {
388                         if (polls[i].revents & POLLIN) {
389                                 struct timespec start_time;
390
391                                 c = NULL;
392                                 pthread_mutex_lock(&client_lock);
393                                 list_for_each_entry(tmp, &clients, node) {
394                                         if (tmp->fd == polls[i].fd) {
395                                                 c = tmp;
396                                                 break;
397                                         }
398                                 }
399                                 pthread_mutex_unlock(&client_lock);
400                                 if (!c) {
401                                         condlog(4, "cli%d: new fd %d",
402                                                 i, polls[i].fd);
403                                         continue;
404                                 }
405                                 get_monotonic_time(&start_time);
406                                 if (recv_packet_from_client(c->fd, &inbuf,
407                                                             uxsock_timeout)
408                                     != 0) {
409                                         dead_client(c);
410                                         continue;
411                                 }
412                                 if (!inbuf) {
413                                         condlog(4, "recv_packet_from_client "
414                                                 "get null request");
415                                         continue;
416                                 }
417                                 condlog(4, "cli[%d]: Got request [%s]",
418                                         i, inbuf);
419                                 uxsock_trigger(inbuf, &reply, &rlen,
420                                                _socket_client_is_root(c->fd),
421                                                trigger_data);
422                                 if (reply) {
423                                         if (send_packet(c->fd,
424                                                         reply) != 0) {
425                                                 dead_client(c);
426                                         } else {
427                                                 condlog(4, "cli[%d]: "
428                                                         "Reply [%d bytes]",
429                                                         i, rlen);
430                                         }
431                                         FREE(reply);
432                                         reply = NULL;
433                                 }
434                                 check_timeout(start_time, inbuf,
435                                               uxsock_timeout);
436                                 FREE(inbuf);
437                         }
438                 }
439                 /* see if we got a non-fatal signal */
440                 handle_signals(true);
441
442                 /* see if we got a new client */
443                 if (polls[0].revents & POLLIN) {
444                         new_client(ux_sock);
445                 }
446
447                 /* handle inotify events on config files */
448                 if (polls[1].revents & POLLIN)
449                         handle_inotify(notify_fd, &wds);
450         }
451
452         return NULL;
453 }