multipathd: Fix uxlsnr race condition on shutdown
[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 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <stdarg.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/un.h>
21 #include <sys/poll.h>
22
23 #include <checkers.h>
24
25 #include <memory.h>
26 #include <debug.h>
27 #include <vector.h>
28 #include <structs.h>
29 #include <uxsock.h>
30 #include <defaults.h>
31
32 #include "cli.h"
33 #include "uxlsnr.h"
34
35 #define SLEEP_TIME 5000
36
37 struct client {
38         int fd;
39         struct client *next, *prev;
40 };
41
42 static struct client *clients;
43 static unsigned num_clients;
44 struct pollfd *polls;
45
46 /*
47  * handle a new client joining
48  */
49 static void new_client(int ux_sock)
50 {
51         struct client *c;
52         struct sockaddr addr;
53         socklen_t len = sizeof(addr);
54         int fd;
55
56         fd = accept(ux_sock, &addr, &len);
57         
58         if (fd == -1)
59                 return;
60
61         /* put it in our linked list */
62         c = (struct client *)MALLOC(sizeof(*c));
63         memset(c, 0, sizeof(*c));
64         c->fd = fd;
65         c->next = clients;
66         if (c->next) c->next->prev = c;
67         clients = c;
68         num_clients++;
69 }
70
71 /*
72  * kill off a dead client
73  */
74 static void dead_client(struct client *c)
75 {
76         close(c->fd);
77         if (c->prev) c->prev->next = c->next;
78         if (c->next) c->next->prev = c->prev;
79         if (c == clients) clients = c->next;
80         FREE(c);
81         num_clients--;
82 }
83
84 void free_polls (void)
85 {
86         if (polls)
87                 FREE(polls);
88 }
89
90 void uxsock_cleanup(void *arg)
91 {
92         cli_exit();
93         free_polls();
94 }
95
96 /*
97  * entry point
98  */
99 void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *),
100                         void * trigger_data)
101 {
102         int ux_sock;
103         size_t len;
104         int rlen;
105         char *inbuf;
106         char *reply;
107
108         ux_sock = ux_socket_listen(DEFAULT_SOCKET);
109
110         if (ux_sock == -1) {
111                 condlog(0, "ux_socket_listen error");
112                 exit(1);
113         }
114
115         pthread_cleanup_push(uxsock_cleanup, NULL);
116
117         polls = (struct pollfd *)MALLOC(0);
118
119         while (1) {
120                 struct client *c;
121                 int i, poll_count;
122
123                 /* setup for a poll */
124                 polls = REALLOC(polls, (1+num_clients) * sizeof(*polls));
125                 polls[0].fd = ux_sock;
126                 polls[0].events = POLLIN;
127
128                 /* setup the clients */
129                 for (i=1, c = clients; c; i++, c = c->next) {
130                         polls[i].fd = c->fd;
131                         polls[i].events = POLLIN;
132                 }
133
134                 /* most of our life is spent in this call */
135                 poll_count = poll(polls, i, SLEEP_TIME);
136                 
137                 if (poll_count == -1) {
138                         if (errno == EINTR)
139                                 continue;
140
141                         /* something went badly wrong! */
142                         condlog(0, "poll");
143                         pthread_exit(NULL);
144                 }
145
146                 if (poll_count == 0)
147                         continue;
148
149                 /* see if a client wants to speak to us */
150                 for (i=1, c = clients; c; i++) {
151                         struct client *next = c->next;
152
153                         if (polls[i].revents & POLLIN) {
154                                 if (recv_packet(c->fd, &inbuf, &len) != 0) {
155                                         dead_client(c);
156                                 } else {
157                                         inbuf[len - 1] = 0;
158                                         condlog(4, "Got request [%s]", inbuf);
159                                         uxsock_trigger(inbuf, &reply, &rlen,
160                                                        trigger_data);
161                                         if (reply) {
162                                                 if (send_packet(c->fd, reply,
163                                                                 rlen) != 0) {
164                                                         dead_client(c);
165                                                 }
166                                                 condlog(4, "Reply [%d bytes]",
167                                                         rlen);
168                                                 FREE(reply);
169                                                 reply = NULL;
170                                         }
171                                         FREE(inbuf);
172                                 }
173                         }
174                         c = next;
175                 }
176
177                 /* see if we got a new client */
178                 if (polls[0].revents & POLLIN) {
179                         new_client(ux_sock);
180                 }
181         }
182
183         pthread_cleanup_pop(1);
184         close(ux_sock);
185         return NULL;
186 }