chiark / gitweb /
add simple event loop
[elogind.git] / socket.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <sys/poll.h>
9
10 #include "name.h"
11 #include "socket.h"
12 #include "log.h"
13
14 static const NameActiveState state_table[_SOCKET_STATE_MAX] = {
15         [SOCKET_DEAD] = NAME_INACTIVE,
16         [SOCKET_START_PRE] = NAME_ACTIVATING,
17         [SOCKET_START_POST] = NAME_ACTIVATING,
18         [SOCKET_LISTENING] = NAME_ACTIVE,
19         [SOCKET_RUNNING] = NAME_ACTIVE,
20         [SOCKET_STOP_PRE] = NAME_DEACTIVATING,
21         [SOCKET_STOP_POST] = NAME_DEACTIVATING,
22         [SOCKET_MAINTAINANCE] = NAME_INACTIVE,
23 };
24
25 static int socket_load(Name *n) {
26         Socket *s = SOCKET(n);
27
28         exec_context_defaults(&s->exec_context);
29         s->backlog = SOMAXCONN;
30
31         return name_load_fragment_and_dropin(n);
32 }
33
34 static const char* listen_lookup(int type) {
35
36         if (type == SOCK_STREAM)
37                 return "ListenStream";
38         else if (type == SOCK_DGRAM)
39                 return "ListenDatagram";
40         else if (type == SOCK_SEQPACKET)
41                 return "ListenSequentialPacket";
42
43         assert_not_reached("Unkown socket type");
44         return NULL;
45 }
46
47 static void socket_dump(Name *n, FILE *f, const char *prefix) {
48
49         static const char* const state_table[_SOCKET_STATE_MAX] = {
50                 [SOCKET_DEAD] = "dead",
51                 [SOCKET_START_PRE] = "start-pre",
52                 [SOCKET_START_POST] = "start-post",
53                 [SOCKET_LISTENING] = "listening",
54                 [SOCKET_RUNNING] = "running",
55                 [SOCKET_STOP_PRE] = "stop-pre",
56                 [SOCKET_STOP_POST] = "stop-post",
57                 [SOCKET_MAINTAINANCE] = "maintainance"
58         };
59
60         static const char* const command_table[_SOCKET_EXEC_MAX] = {
61                 [SOCKET_EXEC_START_PRE] = "StartPre",
62                 [SOCKET_EXEC_START_POST] = "StartPost",
63                 [SOCKET_EXEC_STOP_PRE] = "StopPre",
64                 [SOCKET_EXEC_STOP_POST] = "StopPost"
65         };
66
67         SocketExecCommand c;
68         Socket *s = SOCKET(n);
69         SocketPort *p;
70
71         assert(s);
72
73         fprintf(f,
74                 "%sSocket State: %s\n"
75                 "%sBindIPv6Only: %s\n"
76                 "%sBacklog: %u\n",
77                 prefix, state_table[s->state],
78                 prefix, yes_no(s->bind_ipv6_only),
79                 prefix, s->backlog);
80
81         LIST_FOREACH(p, s->ports) {
82
83                 if (p->type == SOCKET_SOCKET) {
84                         const char *t;
85                         int r;
86                         char *k;
87
88                         if ((r = socket_address_print(&p->address, &k)) < 0)
89                                 t = strerror(-r);
90                         else
91                                 t = k;
92
93                         fprintf(f, "%s%s: %s\n", prefix, listen_lookup(p->address.type), k);
94                         free(k);
95                 } else
96                         fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
97         }
98
99         exec_context_dump(&s->exec_context, f, prefix);
100
101         for (c = 0; c < _SOCKET_EXEC_MAX; c++) {
102                 ExecCommand *i;
103
104                 LIST_FOREACH(i, s->exec_command[c])
105                         fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path);
106         }
107 }
108
109 static void socket_set_state(Socket *s, SocketState state) {
110         SocketState old_state;
111         assert(s);
112
113         old_state = s->state;
114         s->state = state;
115
116         name_notify(NAME(s), state_table[old_state], state_table[s->state]);
117 }
118
119 static void close_fds(Socket *s) {
120         SocketPort *p;
121
122         assert(s);
123
124         LIST_FOREACH(p, s->ports) {
125                 if (p->fd < 0)
126                         continue;
127
128                 name_unwatch_fd(NAME(s), p->fd);
129                 assert_se(close_nointr(p->fd) >= 0);
130
131                 p->fd = -1;
132         }
133 }
134
135 static int socket_start(Name *n) {
136         Socket *s = SOCKET(n);
137         SocketPort *p;
138         int r;
139
140         assert(s);
141
142         if (s->state == SOCKET_START_PRE ||
143             s->state == SOCKET_START_POST)
144                 return 0;
145
146         if (s->state == SOCKET_LISTENING ||
147             s->state == SOCKET_RUNNING)
148                 return -EALREADY;
149
150         if (s->state == SOCKET_STOP_PRE ||
151             s->state == SOCKET_STOP_POST)
152                 return -EAGAIN;
153
154         assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE);
155
156         LIST_FOREACH(p, s->ports) {
157
158                 assert(p->fd < 0);
159
160                 if (p->type == SOCKET_SOCKET) {
161
162                         if ((r = socket_address_listen(&p->address, s->backlog, s->bind_ipv6_only, &p->fd)) < 0)
163                                 goto rollback;
164
165                 } else {
166                         struct stat st;
167                         assert(p->type == SOCKET_FIFO);
168
169                         if (mkfifo(p->path, 0666 & ~s->exec_context.umask) < 0 && errno != EEXIST) {
170                                 r = -errno;
171                                 goto rollback;
172                         }
173
174                         if ((p->fd = open(p->path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
175                                 r = -errno;
176                                 goto rollback;
177                         }
178
179                         if (fstat(p->fd, &st) < 0) {
180                                 r = -errno;
181                                 goto rollback;
182                         }
183
184                         /* FIXME verify user, access mode */
185
186                         if (!S_ISFIFO(st.st_mode)) {
187                                 r = -EEXIST;
188                                 goto rollback;
189                         }
190                 }
191
192                 if ((r = name_watch_fd(n, p->fd, POLLIN)) < 0)
193                         goto rollback;
194         }
195
196         socket_set_state(s, SOCKET_LISTENING);
197
198         return 0;
199
200 rollback:
201         close_fds(s);
202
203         socket_set_state(s, SOCKET_MAINTAINANCE);
204
205         return r;
206 }
207
208 static int socket_stop(Name *n) {
209         Socket *s = SOCKET(n);
210
211         assert(s);
212
213         if (s->state == SOCKET_START_PRE ||
214             s->state == SOCKET_START_POST)
215                 return -EAGAIN;
216
217         if (s->state == SOCKET_DEAD ||
218             s->state == SOCKET_MAINTAINANCE)
219                 return -EALREADY;
220
221         if (s->state == SOCKET_STOP_PRE ||
222             s->state == SOCKET_STOP_POST)
223                 return 0;
224
225         assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING);
226
227         close_fds(s);
228
229         socket_set_state(s, SOCKET_DEAD);
230
231         return 0;
232 }
233
234 static NameActiveState socket_active_state(Name *n) {
235         assert(n);
236
237         return state_table[SOCKET(n)->state];
238 }
239
240 static void socket_fd_event(Name *n, int fd, uint32_t events) {
241         Socket *s = SOCKET(n);
242
243         assert(n);
244
245         if (events != POLLIN)
246                 goto fail;
247
248         log_info("POLLIN on %s", name_id(n));
249
250         return;
251
252 fail:
253         close_fds(s);
254         socket_set_state(s, SOCKET_MAINTAINANCE);
255 }
256
257 static void socket_free_hook(Name *n) {
258         SocketExecCommand c;
259         Socket *s = SOCKET(n);
260         SocketPort *p;
261
262         assert(s);
263
264         while ((p = s->ports)) {
265                 LIST_REMOVE(SocketPort, s->ports, p);
266
267                 if (p->fd >= 0)
268                         close_nointr(p->fd);
269                 free(p->path);
270                 free(p);
271         }
272
273         exec_context_free(&s->exec_context);
274
275         for (c = 0; c < _SOCKET_EXEC_MAX; c++)
276                 exec_command_free_list(s->exec_command[c]);
277
278         if (s->service)
279                 s->service->socket = NULL;
280 }
281
282 const NameVTable socket_vtable = {
283         .suffix = ".socket",
284
285         .load = socket_load,
286         .dump = socket_dump,
287
288         .start = socket_start,
289         .stop = socket_stop,
290         .reload = NULL,
291
292         .active_state = socket_active_state,
293
294         .fd_event = socket_fd_event,
295
296         .free_hook = socket_free_hook
297 };