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