]> git.ipfire.org Git - thirdparty/systemd.git/blame - socket.c
add simple event loop
[thirdparty/systemd.git] / socket.c
CommitLineData
5cb5a6ff
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
2
83c60c9f
LP
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <unistd.h>
6#include <errno.h>
7#include <fcntl.h>
9152c765 8#include <sys/poll.h>
83c60c9f 9
5cb5a6ff
LP
10#include "name.h"
11#include "socket.h"
83c60c9f
LP
12#include "log.h"
13
14static 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};
5cb5a6ff
LP
24
25static int socket_load(Name *n) {
26 Socket *s = SOCKET(n);
27
28 exec_context_defaults(&s->exec_context);
542563ba 29 s->backlog = SOMAXCONN;
5cb5a6ff
LP
30
31 return name_load_fragment_and_dropin(n);
32}
33
542563ba
LP
34static 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
5cb5a6ff
LP
47static 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);
542563ba 69 SocketPort *p;
5cb5a6ff
LP
70
71 assert(s);
72
5cb5a6ff
LP
73 fprintf(f,
74 "%sSocket State: %s\n"
542563ba
LP
75 "%sBindIPv6Only: %s\n"
76 "%sBacklog: %u\n",
5cb5a6ff 77 prefix, state_table[s->state],
542563ba
LP
78 prefix, yes_no(s->bind_ipv6_only),
79 prefix, s->backlog);
80
81 LIST_FOREACH(p, s->ports) {
5cb5a6ff 82
542563ba
LP
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 }
5cb5a6ff
LP
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
83c60c9f
LP
109static 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
119static 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
9152c765
LP
128 name_unwatch_fd(NAME(s), p->fd);
129 assert_se(close_nointr(p->fd) >= 0);
130
83c60c9f
LP
131 p->fd = -1;
132 }
133}
134
542563ba 135static int socket_start(Name *n) {
83c60c9f
LP
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 }
9152c765
LP
191
192 if ((r = name_watch_fd(n, p->fd, POLLIN)) < 0)
193 goto rollback;
83c60c9f
LP
194 }
195
196 socket_set_state(s, SOCKET_LISTENING);
197
542563ba 198 return 0;
83c60c9f
LP
199
200rollback:
201 close_fds(s);
202
203 socket_set_state(s, SOCKET_MAINTAINANCE);
204
205 return r;
542563ba
LP
206}
207
208static int socket_stop(Name *n) {
83c60c9f
LP
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
542563ba
LP
231 return 0;
232}
233
5cb5a6ff 234static NameActiveState socket_active_state(Name *n) {
83c60c9f 235 assert(n);
5cb5a6ff 236
83c60c9f 237 return state_table[SOCKET(n)->state];
5cb5a6ff
LP
238}
239
9152c765
LP
240static 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
252fail:
253 close_fds(s);
254 socket_set_state(s, SOCKET_MAINTAINANCE);
255}
256
5cb5a6ff 257static void socket_free_hook(Name *n) {
5cb5a6ff
LP
258 SocketExecCommand c;
259 Socket *s = SOCKET(n);
542563ba 260 SocketPort *p;
5cb5a6ff
LP
261
262 assert(s);
263
542563ba
LP
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 }
5cb5a6ff
LP
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
282const NameVTable socket_vtable = {
283 .suffix = ".socket",
284
285 .load = socket_load,
286 .dump = socket_dump,
287
542563ba
LP
288 .start = socket_start,
289 .stop = socket_stop,
5cb5a6ff
LP
290 .reload = NULL,
291
292 .active_state = socket_active_state,
293
9152c765
LP
294 .fd_event = socket_fd_event,
295
5cb5a6ff
LP
296 .free_hook = socket_free_hook
297};