f20982fd13bb7975d3b23b99b82d02effc79ffed
[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         int fd;
43         struct client *next, *prev;
44 };
45
46 static struct client *clients;
47 static unsigned num_clients;
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         /* put it in our linked list */
68         c = (struct client *)MALLOC(sizeof(*c));
69         memset(c, 0, sizeof(*c));
70         c->fd = fd;
71         c->next = clients;
72         if (c->next) c->next->prev = c;
73         clients = c;
74         num_clients++;
75 }
76
77 /*
78  * kill off a dead client
79  */
80 static void dead_client(struct client *c)
81 {
82         close(c->fd);
83         if (c->prev) c->prev->next = c->next;
84         if (c->next) c->next->prev = c->prev;
85         if (c == clients) clients = c->next;
86         FREE(c);
87         num_clients--;
88 }
89
90 void free_polls (void)
91 {
92         if (polls)
93                 FREE(polls);
94 }
95
96 void check_timeout(struct timeval start_time, char *inbuf,
97                    unsigned int timeout)
98 {
99         struct timeval diff_time, end_time;
100
101         if (start_time.tv_sec && gettimeofday(&end_time, NULL) == 0) {
102                 timersub(&end_time, &start_time, &diff_time);
103                 unsigned long msecs;
104
105                 msecs = diff_time.tv_sec * 1000 +
106                         diff_time.tv_usec / 1000;
107                 if (msecs > timeout)
108                         condlog(2, "cli cmd '%s' timeout reached "
109                                 "after %lu.%06lu secs", inbuf,
110                                 diff_time.tv_sec, diff_time.tv_usec);
111         }
112 }
113
114 void uxsock_cleanup(void *arg)
115 {
116         cli_exit();
117         free_polls();
118 }
119
120 /*
121  * entry point
122  */
123 void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *),
124                         void * trigger_data)
125 {
126         int ux_sock;
127         size_t len;
128         int rlen, timeout;
129         char *inbuf;
130         char *reply;
131         sigset_t mask;
132
133         ux_sock = ux_socket_listen(DEFAULT_SOCKET);
134
135         if (ux_sock == -1) {
136                 condlog(1, "could not create uxsock: %d", errno);
137                 return NULL;
138         }
139
140         if (!conf) {
141                 condlog(1, "configuration changed");
142                 return NULL;
143         }
144
145         timeout = conf->uxsock_timeout;
146
147         pthread_cleanup_push(uxsock_cleanup, NULL);
148
149         polls = (struct pollfd *)MALLOC(0);
150         pthread_sigmask(SIG_SETMASK, NULL, &mask);
151         sigdelset(&mask, SIGHUP);
152         sigdelset(&mask, SIGUSR1);
153         while (1) {
154                 struct client *c;
155                 int i, poll_count;
156
157                 /*
158                  * Store configuration timeout;
159                  * configuration might change during
160                  * the call to 'reconfigure'.
161                  */
162                 if (conf)
163                         timeout = conf->uxsock_timeout;
164
165                 /* setup for a poll */
166                 polls = REALLOC(polls, (1+num_clients) * sizeof(*polls));
167                 polls[0].fd = ux_sock;
168                 polls[0].events = POLLIN;
169
170                 /* setup the clients */
171                 for (i=1, c = clients; c; i++, c = c->next) {
172                         polls[i].fd = c->fd;
173                         polls[i].events = POLLIN;
174                 }
175
176                 /* most of our life is spent in this call */
177                 poll_count = ppoll(polls, i, &sleep_time, &mask);
178
179                 if (poll_count == -1) {
180                         if (errno == EINTR) {
181                                 handle_signals();
182                                 continue;
183                         }
184
185                         /* something went badly wrong! */
186                         condlog(0, "poll");
187                         pthread_exit(NULL);
188                 }
189
190                 if (poll_count == 0)
191                         continue;
192
193                 /* see if a client wants to speak to us */
194                 for (i=1, c = clients; c; i++) {
195                         struct client *next = c->next;
196
197                         if (polls[i].revents & POLLIN) {
198                                 struct timeval start_time;
199
200                                 if (gettimeofday(&start_time, NULL) != 0)
201                                         start_time.tv_sec = 0;
202
203                                 if (recv_packet(c->fd, &inbuf, &len,
204                                                 timeout) != 0) {
205                                         dead_client(c);
206                                 } else {
207                                         inbuf[len - 1] = 0;
208                                         condlog(4, "Got request [%s]", inbuf);
209                                         uxsock_trigger(inbuf, &reply, &rlen,
210                                                        trigger_data);
211                                         if (reply) {
212                                                 if (send_packet(c->fd, reply,
213                                                                 rlen) != 0) {
214                                                         dead_client(c);
215                                                 }
216                                                 condlog(4, "Reply [%d bytes]",
217                                                         rlen);
218                                                 FREE(reply);
219                                                 reply = NULL;
220                                         }
221                                         check_timeout(start_time, inbuf,
222                                                       timeout);
223                                         FREE(inbuf);
224                                 }
225                         }
226                         c = next;
227                 }
228
229                 /* see if we got a new client */
230                 if (polls[0].revents & POLLIN) {
231                         new_client(ux_sock);
232                 }
233         }
234
235         pthread_cleanup_pop(1);
236         close(ux_sock);
237         return NULL;
238 }