04cbb7c71e81004900611182e0c0a5cb44a45742
[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 "checkers.h"
27 #include "memory.h"
28 #include "debug.h"
29 #include "vector.h"
30 #include "structs.h"
31 #include "structs_vec.h"
32 #include "uxsock.h"
33 #include "defaults.h"
34 #include "config.h"
35 #include "mpath_cmd.h"
36 #include "time-util.h"
37
38 #include "main.h"
39 #include "cli.h"
40 #include "uxlsnr.h"
41
42 struct timespec sleep_time = {5, 0};
43
44 struct client {
45         struct list_head node;
46         int fd;
47 };
48
49 #define MIN_POLLS 1023
50
51 LIST_HEAD(clients);
52 pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
53 struct pollfd *polls;
54
55 static bool _socket_client_is_root(int fd);
56
57 static bool _socket_client_is_root(int fd)
58 {
59         socklen_t len = 0;
60         struct ucred uc;
61
62         len = sizeof(struct ucred);
63         if ((fd >= 0) &&
64             (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) &&
65             (uc.uid == 0))
66                         return true;
67
68         /* Treat error as not root client */
69         return false;
70 }
71
72 /*
73  * handle a new client joining
74  */
75 static void new_client(int ux_sock)
76 {
77         struct client *c;
78         struct sockaddr addr;
79         socklen_t len = sizeof(addr);
80         int fd;
81
82         fd = accept(ux_sock, &addr, &len);
83
84         if (fd == -1)
85                 return;
86
87         c = (struct client *)MALLOC(sizeof(*c));
88         if (!c) {
89                 close(fd);
90                 return;
91         }
92         memset(c, 0, sizeof(*c));
93         INIT_LIST_HEAD(&c->node);
94         c->fd = fd;
95
96         /* put it in our linked list */
97         pthread_mutex_lock(&client_lock);
98         list_add_tail(&c->node, &clients);
99         pthread_mutex_unlock(&client_lock);
100 }
101
102 /*
103  * kill off a dead client
104  */
105 static void _dead_client(struct client *c)
106 {
107         int fd = c->fd;
108         list_del_init(&c->node);
109         c->fd = -1;
110         FREE(c);
111         close(fd);
112 }
113
114 static void dead_client(struct client *c)
115 {
116         pthread_cleanup_push(cleanup_lock, &client_lock);
117         pthread_mutex_lock(&client_lock);
118         _dead_client(c);
119         pthread_cleanup_pop(1);
120 }
121
122 void free_polls (void)
123 {
124         if (polls)
125                 FREE(polls);
126 }
127
128 void check_timeout(struct timespec start_time, char *inbuf,
129                    unsigned int timeout)
130 {
131         struct timespec diff_time, end_time;
132
133         if (start_time.tv_sec &&
134             clock_gettime(CLOCK_MONOTONIC, &end_time) == 0) {
135                 unsigned long msecs;
136
137                 timespecsub(&end_time, &start_time, &diff_time);
138                 msecs = diff_time.tv_sec * 1000 +
139                         diff_time.tv_nsec / (1000 * 1000);
140                 if (msecs > timeout)
141                         condlog(2, "cli cmd '%s' timeout reached "
142                                 "after %lu.%06lu secs", inbuf,
143                                 diff_time.tv_sec, diff_time.tv_nsec / 1000);
144         }
145 }
146
147 void uxsock_cleanup(void *arg)
148 {
149         struct client *client_loop;
150         struct client *client_tmp;
151         long ux_sock = (long)arg;
152
153         close(ux_sock);
154
155         pthread_mutex_lock(&client_lock);
156         list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
157                 _dead_client(client_loop);
158         }
159         pthread_mutex_unlock(&client_lock);
160
161         cli_exit();
162         free_polls();
163 }
164
165 /*
166  * entry point
167  */
168 void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
169                      void * trigger_data)
170 {
171         int rlen;
172         char *inbuf;
173         char *reply;
174         sigset_t mask;
175         int old_clients = MIN_POLLS;
176
177         condlog(3, "uxsock: startup listener");
178         polls = (struct pollfd *)MALLOC((MIN_POLLS + 1) * sizeof(struct pollfd));
179         if (!polls) {
180                 condlog(0, "uxsock: failed to allocate poll fds");
181                 exit_daemon();
182         }
183         sigfillset(&mask);
184         sigdelset(&mask, SIGINT);
185         sigdelset(&mask, SIGTERM);
186         sigdelset(&mask, SIGHUP);
187         sigdelset(&mask, SIGUSR1);
188         while (1) {
189                 struct client *c, *tmp;
190                 int i, poll_count, num_clients;
191
192                 /* setup for a poll */
193                 pthread_mutex_lock(&client_lock);
194                 num_clients = 0;
195                 list_for_each_entry(c, &clients, node) {
196                         num_clients++;
197                 }
198                 if (num_clients != old_clients) {
199                         struct pollfd *new;
200                         if (num_clients <= MIN_POLLS && old_clients > MIN_POLLS) {
201                                 new = REALLOC(polls, (1 + MIN_POLLS) *
202                                                 sizeof(struct pollfd));
203                         } else if (num_clients <= MIN_POLLS && old_clients <= MIN_POLLS) {
204                                 new = polls;
205                         } else {
206                                 new = REALLOC(polls, (1+num_clients) *
207                                                 sizeof(struct pollfd));
208                         }
209                         if (!new) {
210                                 pthread_mutex_unlock(&client_lock);
211                                 condlog(0, "%s: failed to realloc %d poll fds",
212                                         "uxsock", 1 + num_clients);
213                                 sched_yield();
214                                 continue;
215                         }
216                         old_clients = num_clients;
217                         polls = new;
218                 }
219                 polls[0].fd = ux_sock;
220                 polls[0].events = POLLIN;
221
222                 /* setup the clients */
223                 i = 1;
224                 list_for_each_entry(c, &clients, node) {
225                         polls[i].fd = c->fd;
226                         polls[i].events = POLLIN;
227                         i++;
228                 }
229                 pthread_mutex_unlock(&client_lock);
230
231                 /* most of our life is spent in this call */
232                 poll_count = ppoll(polls, i, &sleep_time, &mask);
233
234                 handle_signals(false);
235                 if (poll_count == -1) {
236                         if (errno == EINTR) {
237                                 handle_signals(true);
238                                 continue;
239                         }
240
241                         /* something went badly wrong! */
242                         condlog(0, "uxsock: poll failed with %d", errno);
243                         exit_daemon();
244                         break;
245                 }
246
247                 if (poll_count == 0) {
248                         handle_signals(true);
249                         continue;
250                 }
251
252                 /*
253                  * Client connection. We shouldn't answer while we're
254                  * configuring - nothing may be configured yet.
255                  * But we can't wait forever either, because this thread
256                  * must handle signals. So wait a short while only.
257                  */
258                 if (wait_for_state_change_if(DAEMON_CONFIGURE, 10)
259                     == DAEMON_CONFIGURE) {
260                         handle_signals(false);
261                         continue;
262                 }
263
264                 /* see if a client wants to speak to us */
265                 for (i = 1; i < num_clients + 1; i++) {
266                         if (polls[i].revents & POLLIN) {
267                                 struct timespec start_time;
268
269                                 c = NULL;
270                                 pthread_mutex_lock(&client_lock);
271                                 list_for_each_entry(tmp, &clients, node) {
272                                         if (tmp->fd == polls[i].fd) {
273                                                 c = tmp;
274                                                 break;
275                                         }
276                                 }
277                                 pthread_mutex_unlock(&client_lock);
278                                 if (!c) {
279                                         condlog(4, "cli%d: new fd %d",
280                                                 i, polls[i].fd);
281                                         continue;
282                                 }
283                                 if (clock_gettime(CLOCK_MONOTONIC, &start_time)
284                                     != 0)
285                                         start_time.tv_sec = 0;
286                                 if (recv_packet_from_client(c->fd, &inbuf,
287                                                             uxsock_timeout)
288                                     != 0) {
289                                         dead_client(c);
290                                         continue;
291                                 }
292                                 if (!inbuf) {
293                                         condlog(4, "recv_packet_from_client "
294                                                 "get null request");
295                                         continue;
296                                 }
297                                 condlog(4, "cli[%d]: Got request [%s]",
298                                         i, inbuf);
299                                 uxsock_trigger(inbuf, &reply, &rlen,
300                                                _socket_client_is_root(c->fd),
301                                                trigger_data);
302                                 if (reply) {
303                                         if (send_packet(c->fd,
304                                                         reply) != 0) {
305                                                 dead_client(c);
306                                         } else {
307                                                 condlog(4, "cli[%d]: "
308                                                         "Reply [%d bytes]",
309                                                         i, rlen);
310                                         }
311                                         FREE(reply);
312                                         reply = NULL;
313                                 }
314                                 check_timeout(start_time, inbuf,
315                                               uxsock_timeout);
316                                 FREE(inbuf);
317                         }
318                 }
319                 /* see if we got a non-fatal signal */
320                 handle_signals(true);
321
322                 /* see if we got a new client */
323                 if (polls[0].revents & POLLIN) {
324                         new_client(ux_sock);
325                 }
326         }
327
328         return NULL;
329 }