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