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