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