]>
Commit | Line | Data |
---|---|---|
5cb5a6ff LP |
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/ |
2 | ||
a7334b09 LP |
3 | /*** |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2010 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
83c60c9f LP |
22 | #include <sys/types.h> |
23 | #include <sys/stat.h> | |
24 | #include <unistd.h> | |
25 | #include <errno.h> | |
26 | #include <fcntl.h> | |
f94ea366 | 27 | #include <sys/epoll.h> |
034c6ed7 | 28 | #include <signal.h> |
83c60c9f | 29 | |
87f0e418 | 30 | #include "unit.h" |
5cb5a6ff | 31 | #include "socket.h" |
83c60c9f | 32 | #include "log.h" |
23a177ef LP |
33 | #include "load-dropin.h" |
34 | #include "load-fragment.h" | |
9e2f7c11 | 35 | #include "strv.h" |
83c60c9f | 36 | |
acbb0225 | 37 | static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { |
87f0e418 LP |
38 | [SOCKET_DEAD] = UNIT_INACTIVE, |
39 | [SOCKET_START_PRE] = UNIT_ACTIVATING, | |
40 | [SOCKET_START_POST] = UNIT_ACTIVATING, | |
41 | [SOCKET_LISTENING] = UNIT_ACTIVE, | |
42 | [SOCKET_RUNNING] = UNIT_ACTIVE, | |
43 | [SOCKET_STOP_PRE] = UNIT_DEACTIVATING, | |
44 | [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING, | |
45 | [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING, | |
46 | [SOCKET_STOP_POST] = UNIT_DEACTIVATING, | |
80876c20 LP |
47 | [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING, |
48 | [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING, | |
87f0e418 | 49 | [SOCKET_MAINTAINANCE] = UNIT_INACTIVE, |
83c60c9f | 50 | }; |
5cb5a6ff | 51 | |
acbb0225 LP |
52 | static const char* const state_string_table[_SOCKET_STATE_MAX] = { |
53 | [SOCKET_DEAD] = "dead", | |
54 | [SOCKET_START_PRE] = "start-pre", | |
55 | [SOCKET_START_POST] = "start-post", | |
56 | [SOCKET_LISTENING] = "listening", | |
57 | [SOCKET_RUNNING] = "running", | |
58 | [SOCKET_STOP_PRE] = "stop-pre", | |
59 | [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm", | |
60 | [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill", | |
61 | [SOCKET_STOP_POST] = "stop-post", | |
80876c20 LP |
62 | [SOCKET_FINAL_SIGTERM] = "final-sigterm", |
63 | [SOCKET_FINAL_SIGKILL] = "final-sigkill", | |
acbb0225 LP |
64 | [SOCKET_MAINTAINANCE] = "maintainance" |
65 | }; | |
66 | ||
5e94833f LP |
67 | static void socket_unwatch_control_pid(Socket *s) { |
68 | assert(s); | |
69 | ||
70 | if (s->control_pid <= 0) | |
71 | return; | |
72 | ||
73 | unit_unwatch_pid(UNIT(s), s->control_pid); | |
74 | s->control_pid = 0; | |
75 | } | |
76 | ||
87f0e418 LP |
77 | static void socket_done(Unit *u) { |
78 | Socket *s = SOCKET(u); | |
034c6ed7 LP |
79 | SocketPort *p; |
80 | ||
81 | assert(s); | |
82 | ||
83 | while ((p = s->ports)) { | |
84 | LIST_REMOVE(SocketPort, port, s->ports, p); | |
85 | ||
86 | if (p->fd >= 0) | |
87 | close_nointr(p->fd); | |
88 | free(p->path); | |
89 | free(p); | |
90 | } | |
91 | ||
92 | exec_context_done(&s->exec_context); | |
e537352b | 93 | exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); |
034c6ed7 LP |
94 | s->control_command = NULL; |
95 | ||
5e94833f | 96 | socket_unwatch_control_pid(s); |
034c6ed7 LP |
97 | |
98 | s->service = NULL; | |
99 | ||
acbb0225 | 100 | free(s->bind_to_device); |
e537352b | 101 | s->bind_to_device = NULL; |
acbb0225 LP |
102 | |
103 | unit_unwatch_timer(u, &s->timer_watch); | |
5cb5a6ff LP |
104 | } |
105 | ||
e537352b | 106 | static void socket_init(Unit *u) { |
87f0e418 | 107 | Socket *s = SOCKET(u); |
44d8db9e | 108 | |
e537352b LP |
109 | assert(u); |
110 | assert(u->meta.load_state == UNIT_STUB); | |
44d8db9e LP |
111 | |
112 | s->state = 0; | |
acbb0225 | 113 | s->timer_watch.type = WATCH_INVALID; |
44d8db9e LP |
114 | s->bind_ipv6_only = false; |
115 | s->backlog = SOMAXCONN; | |
116 | s->timeout_usec = DEFAULT_TIMEOUT_USEC; | |
b5a0699f LP |
117 | s->directory_mode = 0755; |
118 | s->socket_mode = 0666; | |
50159e6a LP |
119 | s->kill_mode = 0; |
120 | s->failure = false; | |
121 | s->control_pid = 0; | |
e537352b | 122 | s->service = NULL; |
44d8db9e | 123 | exec_context_init(&s->exec_context); |
e537352b | 124 | } |
44d8db9e | 125 | |
e537352b LP |
126 | static int socket_load(Unit *u) { |
127 | Socket *s = SOCKET(u); | |
128 | int r; | |
44d8db9e | 129 | |
e537352b LP |
130 | assert(u); |
131 | assert(u->meta.load_state == UNIT_STUB); | |
44d8db9e | 132 | |
e537352b | 133 | if ((r = unit_load_fragment_and_dropin(u)) < 0) |
23a177ef | 134 | return r; |
44d8db9e | 135 | |
23a177ef | 136 | /* This is a new unit? Then let's add in some extras */ |
e537352b | 137 | if (u->meta.load_state == UNIT_LOADED) { |
44d8db9e | 138 | |
e537352b | 139 | if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service))) |
23a177ef LP |
140 | return r; |
141 | ||
142 | if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0) | |
143 | return r; | |
44d8db9e | 144 | |
23a177ef LP |
145 | if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) |
146 | return r; | |
147 | ||
148 | if ((r = unit_add_default_cgroup(u)) < 0) | |
149 | return r; | |
150 | } | |
151 | ||
152 | return 0; | |
44d8db9e LP |
153 | } |
154 | ||
542563ba LP |
155 | static const char* listen_lookup(int type) { |
156 | ||
157 | if (type == SOCK_STREAM) | |
158 | return "ListenStream"; | |
159 | else if (type == SOCK_DGRAM) | |
160 | return "ListenDatagram"; | |
161 | else if (type == SOCK_SEQPACKET) | |
162 | return "ListenSequentialPacket"; | |
163 | ||
034c6ed7 | 164 | assert_not_reached("Unknown socket type"); |
542563ba LP |
165 | return NULL; |
166 | } | |
167 | ||
87f0e418 | 168 | static void socket_dump(Unit *u, FILE *f, const char *prefix) { |
5cb5a6ff | 169 | |
e537352b | 170 | static const char* const command_table[_SOCKET_EXEC_COMMAND_MAX] = { |
5cb5a6ff LP |
171 | [SOCKET_EXEC_START_PRE] = "StartPre", |
172 | [SOCKET_EXEC_START_POST] = "StartPost", | |
173 | [SOCKET_EXEC_STOP_PRE] = "StopPre", | |
174 | [SOCKET_EXEC_STOP_POST] = "StopPost" | |
175 | }; | |
176 | ||
177 | SocketExecCommand c; | |
87f0e418 | 178 | Socket *s = SOCKET(u); |
542563ba | 179 | SocketPort *p; |
82ba9f08 LP |
180 | const char *prefix2; |
181 | char *p2; | |
5cb5a6ff LP |
182 | |
183 | assert(s); | |
fa068367 | 184 | assert(f); |
5cb5a6ff | 185 | |
82ba9f08 LP |
186 | p2 = strappend(prefix, "\t"); |
187 | prefix2 = p2 ? p2 : prefix; | |
c43d20a0 | 188 | |
5cb5a6ff LP |
189 | fprintf(f, |
190 | "%sSocket State: %s\n" | |
542563ba | 191 | "%sBindIPv6Only: %s\n" |
b5a0699f | 192 | "%sBacklog: %u\n" |
50159e6a | 193 | "%sKillMode: %s\n" |
b5a0699f LP |
194 | "%sSocketMode: %04o\n" |
195 | "%sDirectoryMode: %04o\n", | |
acbb0225 | 196 | prefix, state_string_table[s->state], |
542563ba | 197 | prefix, yes_no(s->bind_ipv6_only), |
b5a0699f | 198 | prefix, s->backlog, |
50159e6a | 199 | prefix, kill_mode_to_string(s->kill_mode), |
b5a0699f LP |
200 | prefix, s->socket_mode, |
201 | prefix, s->directory_mode); | |
542563ba | 202 | |
70123e68 LP |
203 | if (s->control_pid > 0) |
204 | fprintf(f, | |
205 | "%sControl PID: %llu\n", | |
206 | prefix, (unsigned long long) s->control_pid); | |
207 | ||
acbb0225 LP |
208 | if (s->bind_to_device) |
209 | fprintf(f, | |
210 | "%sBindToDevice: %s\n", | |
211 | prefix, s->bind_to_device); | |
212 | ||
034c6ed7 | 213 | LIST_FOREACH(port, p, s->ports) { |
5cb5a6ff | 214 | |
542563ba LP |
215 | if (p->type == SOCKET_SOCKET) { |
216 | const char *t; | |
217 | int r; | |
218 | char *k; | |
219 | ||
220 | if ((r = socket_address_print(&p->address, &k)) < 0) | |
221 | t = strerror(-r); | |
222 | else | |
223 | t = k; | |
224 | ||
225 | fprintf(f, "%s%s: %s\n", prefix, listen_lookup(p->address.type), k); | |
226 | free(k); | |
227 | } else | |
228 | fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); | |
229 | } | |
5cb5a6ff LP |
230 | |
231 | exec_context_dump(&s->exec_context, f, prefix); | |
232 | ||
e537352b | 233 | for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) { |
c43d20a0 LP |
234 | if (!s->exec_command[c]) |
235 | continue; | |
5cb5a6ff | 236 | |
c43d20a0 LP |
237 | fprintf(f, "%s→ %s:\n", |
238 | prefix, command_table[c]); | |
239 | ||
240 | exec_command_dump_list(s->exec_command[c], f, prefix2); | |
5cb5a6ff | 241 | } |
c43d20a0 | 242 | |
82ba9f08 | 243 | free(p2); |
5cb5a6ff LP |
244 | } |
245 | ||
034c6ed7 | 246 | static void socket_close_fds(Socket *s) { |
83c60c9f LP |
247 | SocketPort *p; |
248 | ||
249 | assert(s); | |
250 | ||
034c6ed7 | 251 | LIST_FOREACH(port, p, s->ports) { |
83c60c9f LP |
252 | if (p->fd < 0) |
253 | continue; | |
254 | ||
acbb0225 | 255 | unit_unwatch_fd(UNIT(s), &p->fd_watch); |
9152c765 LP |
256 | assert_se(close_nointr(p->fd) >= 0); |
257 | ||
83c60c9f LP |
258 | p->fd = -1; |
259 | } | |
260 | } | |
261 | ||
034c6ed7 | 262 | static int socket_open_fds(Socket *s) { |
83c60c9f LP |
263 | SocketPort *p; |
264 | int r; | |
265 | ||
266 | assert(s); | |
267 | ||
034c6ed7 | 268 | LIST_FOREACH(port, p, s->ports) { |
83c60c9f | 269 | |
034c6ed7 LP |
270 | if (p->fd >= 0) |
271 | continue; | |
83c60c9f LP |
272 | |
273 | if (p->type == SOCKET_SOCKET) { | |
274 | ||
b5a0699f LP |
275 | if ((r = socket_address_listen( |
276 | &p->address, | |
277 | s->backlog, | |
278 | s->bind_ipv6_only, | |
279 | s->bind_to_device, | |
280 | s->directory_mode, | |
281 | s->socket_mode, | |
282 | &p->fd)) < 0) | |
83c60c9f LP |
283 | goto rollback; |
284 | ||
285 | } else { | |
286 | struct stat st; | |
287 | assert(p->type == SOCKET_FIFO); | |
288 | ||
8cb45bf8 LP |
289 | mkdir_parents(p->path, s->directory_mode); |
290 | ||
291 | if (mkfifo(p->path, s->socket_mode) < 0 && errno != EEXIST) { | |
83c60c9f LP |
292 | r = -errno; |
293 | goto rollback; | |
294 | } | |
295 | ||
296 | if ((p->fd = open(p->path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) { | |
297 | r = -errno; | |
298 | goto rollback; | |
299 | } | |
300 | ||
301 | if (fstat(p->fd, &st) < 0) { | |
302 | r = -errno; | |
303 | goto rollback; | |
304 | } | |
305 | ||
306 | /* FIXME verify user, access mode */ | |
307 | ||
308 | if (!S_ISFIFO(st.st_mode)) { | |
309 | r = -EEXIST; | |
310 | goto rollback; | |
311 | } | |
312 | } | |
034c6ed7 LP |
313 | } |
314 | ||
315 | return 0; | |
316 | ||
317 | rollback: | |
318 | socket_close_fds(s); | |
319 | return r; | |
320 | } | |
321 | ||
322 | static void socket_unwatch_fds(Socket *s) { | |
323 | SocketPort *p; | |
9152c765 | 324 | |
034c6ed7 LP |
325 | assert(s); |
326 | ||
327 | LIST_FOREACH(port, p, s->ports) { | |
328 | if (p->fd < 0) | |
329 | continue; | |
330 | ||
acbb0225 | 331 | unit_unwatch_fd(UNIT(s), &p->fd_watch); |
83c60c9f | 332 | } |
034c6ed7 LP |
333 | } |
334 | ||
335 | static int socket_watch_fds(Socket *s) { | |
336 | SocketPort *p; | |
337 | int r; | |
338 | ||
339 | assert(s); | |
83c60c9f | 340 | |
034c6ed7 LP |
341 | LIST_FOREACH(port, p, s->ports) { |
342 | if (p->fd < 0) | |
343 | continue; | |
344 | ||
f94ea366 | 345 | if ((r = unit_watch_fd(UNIT(s), p->fd, EPOLLIN, &p->fd_watch)) < 0) |
034c6ed7 LP |
346 | goto fail; |
347 | } | |
83c60c9f | 348 | |
542563ba | 349 | return 0; |
83c60c9f | 350 | |
034c6ed7 LP |
351 | fail: |
352 | socket_unwatch_fds(s); | |
353 | return r; | |
354 | } | |
355 | ||
356 | static void socket_set_state(Socket *s, SocketState state) { | |
357 | SocketState old_state; | |
358 | assert(s); | |
359 | ||
360 | old_state = s->state; | |
361 | s->state = state; | |
362 | ||
363 | if (state != SOCKET_START_PRE && | |
364 | state != SOCKET_START_POST && | |
365 | state != SOCKET_STOP_PRE && | |
366 | state != SOCKET_STOP_PRE_SIGTERM && | |
367 | state != SOCKET_STOP_PRE_SIGKILL && | |
368 | state != SOCKET_STOP_POST && | |
80876c20 LP |
369 | state != SOCKET_FINAL_SIGTERM && |
370 | state != SOCKET_FINAL_SIGKILL) { | |
acbb0225 | 371 | unit_unwatch_timer(UNIT(s), &s->timer_watch); |
5e94833f | 372 | socket_unwatch_control_pid(s); |
034c6ed7 | 373 | s->control_command = NULL; |
e537352b | 374 | } |
034c6ed7 LP |
375 | |
376 | if (state != SOCKET_START_POST && | |
377 | state != SOCKET_LISTENING && | |
378 | state != SOCKET_RUNNING && | |
379 | state != SOCKET_STOP_PRE && | |
380 | state != SOCKET_STOP_PRE_SIGTERM && | |
381 | state != SOCKET_STOP_PRE_SIGKILL) | |
382 | socket_close_fds(s); | |
383 | ||
384 | if (state != SOCKET_LISTENING) | |
385 | socket_unwatch_fds(s); | |
386 | ||
e537352b | 387 | if (state != old_state) |
9e2f7c11 | 388 | log_debug("%s changed %s → %s", s->meta.id, state_string_table[old_state], state_string_table[state]); |
acbb0225 LP |
389 | |
390 | unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); | |
034c6ed7 LP |
391 | } |
392 | ||
e537352b | 393 | static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { |
034c6ed7 LP |
394 | pid_t pid; |
395 | int r; | |
9e2f7c11 | 396 | char **argv; |
034c6ed7 LP |
397 | |
398 | assert(s); | |
399 | assert(c); | |
400 | assert(_pid); | |
401 | ||
e537352b LP |
402 | if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) |
403 | goto fail; | |
034c6ed7 | 404 | |
9e2f7c11 LP |
405 | if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) { |
406 | r = -ENOMEM; | |
407 | goto fail; | |
408 | } | |
409 | ||
410 | r = exec_spawn(c, | |
411 | argv, | |
412 | &s->exec_context, | |
413 | NULL, 0, | |
414 | true, | |
415 | true, | |
416 | UNIT(s)->meta.manager->confirm_spawn, | |
417 | UNIT(s)->meta.cgroup_bondings, | |
418 | &pid); | |
419 | ||
420 | strv_free(argv); | |
421 | if (r < 0) | |
034c6ed7 LP |
422 | goto fail; |
423 | ||
87f0e418 | 424 | if ((r = unit_watch_pid(UNIT(s), pid)) < 0) |
034c6ed7 LP |
425 | /* FIXME: we need to do something here */ |
426 | goto fail; | |
83c60c9f | 427 | |
034c6ed7 LP |
428 | *_pid = pid; |
429 | ||
430 | return 0; | |
431 | ||
432 | fail: | |
e537352b | 433 | unit_unwatch_timer(UNIT(s), &s->timer_watch); |
83c60c9f LP |
434 | |
435 | return r; | |
542563ba LP |
436 | } |
437 | ||
034c6ed7 LP |
438 | static void socket_enter_dead(Socket *s, bool success) { |
439 | assert(s); | |
440 | ||
441 | if (!success) | |
442 | s->failure = true; | |
443 | ||
444 | socket_set_state(s, s->failure ? SOCKET_MAINTAINANCE : SOCKET_DEAD); | |
445 | } | |
446 | ||
80876c20 LP |
447 | static void socket_enter_signal(Socket *s, SocketState state, bool success); |
448 | ||
034c6ed7 LP |
449 | static void socket_enter_stop_post(Socket *s, bool success) { |
450 | int r; | |
451 | assert(s); | |
452 | ||
453 | if (!success) | |
454 | s->failure = true; | |
455 | ||
5e94833f LP |
456 | socket_unwatch_control_pid(s); |
457 | ||
80876c20 | 458 | if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) { |
e537352b | 459 | if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) |
034c6ed7 LP |
460 | goto fail; |
461 | ||
80876c20 LP |
462 | socket_set_state(s, SOCKET_STOP_POST); |
463 | } else | |
464 | socket_enter_signal(s, SOCKET_FINAL_SIGTERM, true); | |
034c6ed7 LP |
465 | |
466 | return; | |
467 | ||
468 | fail: | |
9e2f7c11 | 469 | log_warning("%s failed to run stop-post executable: %s", s->meta.id, strerror(-r)); |
80876c20 | 470 | socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); |
034c6ed7 LP |
471 | } |
472 | ||
473 | static void socket_enter_signal(Socket *s, SocketState state, bool success) { | |
474 | int r; | |
80876c20 | 475 | bool sent = false; |
034c6ed7 LP |
476 | |
477 | assert(s); | |
478 | ||
479 | if (!success) | |
480 | s->failure = true; | |
481 | ||
80876c20 LP |
482 | if (s->kill_mode != KILL_NONE) { |
483 | int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? SIGTERM : SIGKILL; | |
034c6ed7 | 484 | |
50159e6a LP |
485 | if (s->kill_mode == KILL_CONTROL_GROUP) { |
486 | ||
487 | if ((r = cgroup_bonding_kill_list(UNIT(s)->meta.cgroup_bondings, sig)) < 0) { | |
488 | if (r != -EAGAIN && r != -ESRCH) | |
489 | goto fail; | |
490 | } else | |
491 | sent = true; | |
034c6ed7 | 492 | } |
50159e6a | 493 | |
80876c20 | 494 | if (!sent && s->control_pid > 0) |
50159e6a LP |
495 | if (kill(s->kill_mode == KILL_PROCESS ? s->control_pid : -s->control_pid, sig) < 0 && errno != ESRCH) { |
496 | r = -errno; | |
497 | goto fail; | |
498 | } | |
d6ea93e3 | 499 | } |
034c6ed7 | 500 | |
80876c20 LP |
501 | if (sent) { |
502 | if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) | |
503 | goto fail; | |
d6ea93e3 | 504 | |
80876c20 LP |
505 | socket_set_state(s, state); |
506 | } else if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) | |
507 | socket_enter_stop_post(s, true); | |
508 | else | |
034c6ed7 LP |
509 | socket_enter_dead(s, true); |
510 | ||
511 | return; | |
512 | ||
513 | fail: | |
9e2f7c11 | 514 | log_warning("%s failed to kill processes: %s", s->meta.id, strerror(-r)); |
034c6ed7 LP |
515 | |
516 | if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) | |
517 | socket_enter_stop_post(s, false); | |
518 | else | |
519 | socket_enter_dead(s, false); | |
520 | } | |
521 | ||
522 | static void socket_enter_stop_pre(Socket *s, bool success) { | |
523 | int r; | |
524 | assert(s); | |
525 | ||
526 | if (!success) | |
527 | s->failure = true; | |
528 | ||
5e94833f LP |
529 | socket_unwatch_control_pid(s); |
530 | ||
80876c20 | 531 | if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) { |
e537352b | 532 | if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) |
034c6ed7 LP |
533 | goto fail; |
534 | ||
80876c20 LP |
535 | socket_set_state(s, SOCKET_STOP_PRE); |
536 | } else | |
034c6ed7 LP |
537 | socket_enter_stop_post(s, true); |
538 | ||
539 | return; | |
540 | ||
541 | fail: | |
9e2f7c11 | 542 | log_warning("%s failed to run stop-pre executable: %s", s->meta.id, strerror(-r)); |
034c6ed7 LP |
543 | socket_enter_stop_post(s, false); |
544 | } | |
545 | ||
e9af15c3 LP |
546 | static void socket_enter_listening(Socket *s) { |
547 | int r; | |
548 | assert(s); | |
549 | ||
550 | if ((r = socket_watch_fds(s)) < 0) { | |
9e2f7c11 | 551 | log_warning("%s failed to watch sockets: %s", s->meta.id, strerror(-r)); |
e9af15c3 LP |
552 | goto fail; |
553 | } | |
554 | ||
555 | socket_set_state(s, SOCKET_LISTENING); | |
556 | return; | |
557 | ||
558 | fail: | |
559 | socket_enter_stop_pre(s, false); | |
560 | } | |
561 | ||
034c6ed7 LP |
562 | static void socket_enter_start_post(Socket *s) { |
563 | int r; | |
564 | assert(s); | |
565 | ||
e9af15c3 | 566 | if ((r = socket_open_fds(s)) < 0) { |
9e2f7c11 | 567 | log_warning("%s failed to listen on sockets: %s", s->meta.id, strerror(-r)); |
034c6ed7 LP |
568 | goto fail; |
569 | } | |
570 | ||
5e94833f LP |
571 | socket_unwatch_control_pid(s); |
572 | ||
80876c20 | 573 | if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) { |
e537352b | 574 | if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) { |
9e2f7c11 | 575 | log_warning("%s failed to run start-post executable: %s", s->meta.id, strerror(-r)); |
034c6ed7 LP |
576 | goto fail; |
577 | } | |
578 | ||
80876c20 LP |
579 | socket_set_state(s, SOCKET_START_POST); |
580 | } else | |
e9af15c3 | 581 | socket_enter_listening(s); |
034c6ed7 LP |
582 | |
583 | return; | |
584 | ||
585 | fail: | |
586 | socket_enter_stop_pre(s, false); | |
587 | } | |
588 | ||
589 | static void socket_enter_start_pre(Socket *s) { | |
590 | int r; | |
591 | assert(s); | |
592 | ||
5e94833f LP |
593 | socket_unwatch_control_pid(s); |
594 | ||
80876c20 | 595 | if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) { |
e537352b | 596 | if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) |
034c6ed7 LP |
597 | goto fail; |
598 | ||
80876c20 LP |
599 | socket_set_state(s, SOCKET_START_PRE); |
600 | } else | |
034c6ed7 LP |
601 | socket_enter_start_post(s); |
602 | ||
603 | return; | |
604 | ||
605 | fail: | |
9e2f7c11 | 606 | log_warning("%s failed to run start-pre exectuable: %s", s->meta.id, strerror(-r)); |
034c6ed7 LP |
607 | socket_enter_dead(s, false); |
608 | } | |
609 | ||
610 | static void socket_enter_running(Socket *s) { | |
611 | int r; | |
612 | ||
613 | assert(s); | |
614 | ||
87f0e418 | 615 | if ((r = manager_add_job(UNIT(s)->meta.manager, JOB_START, UNIT(s->service), JOB_REPLACE, true, NULL)) < 0) |
034c6ed7 LP |
616 | goto fail; |
617 | ||
618 | socket_set_state(s, SOCKET_RUNNING); | |
619 | return; | |
620 | ||
621 | fail: | |
9e2f7c11 | 622 | log_warning("%s failed to queue socket startup job: %s", s->meta.id, strerror(-r)); |
80876c20 | 623 | socket_enter_stop_pre(s, false); |
034c6ed7 LP |
624 | } |
625 | ||
626 | static void socket_run_next(Socket *s, bool success) { | |
627 | int r; | |
628 | ||
629 | assert(s); | |
630 | assert(s->control_command); | |
631 | assert(s->control_command->command_next); | |
632 | ||
633 | if (!success) | |
634 | s->failure = true; | |
635 | ||
5e94833f LP |
636 | socket_unwatch_control_pid(s); |
637 | ||
034c6ed7 LP |
638 | s->control_command = s->control_command->command_next; |
639 | ||
e537352b | 640 | if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) |
034c6ed7 LP |
641 | goto fail; |
642 | ||
643 | return; | |
644 | ||
645 | fail: | |
9e2f7c11 | 646 | log_warning("%s failed to run spawn next executable: %s", s->meta.id, strerror(-r)); |
80876c20 LP |
647 | |
648 | if (s->state == SOCKET_START_POST) | |
649 | socket_enter_stop_pre(s, false); | |
034c6ed7 LP |
650 | else if (s->state == SOCKET_STOP_POST) |
651 | socket_enter_dead(s, false); | |
652 | else | |
80876c20 | 653 | socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); |
034c6ed7 LP |
654 | } |
655 | ||
87f0e418 LP |
656 | static int socket_start(Unit *u) { |
657 | Socket *s = SOCKET(u); | |
83c60c9f LP |
658 | |
659 | assert(s); | |
660 | ||
034c6ed7 LP |
661 | /* We cannot fulfill this request right now, try again later |
662 | * please! */ | |
663 | if (s->state == SOCKET_STOP_PRE || | |
664 | s->state == SOCKET_STOP_PRE_SIGKILL || | |
665 | s->state == SOCKET_STOP_PRE_SIGTERM || | |
666 | s->state == SOCKET_STOP_POST || | |
80876c20 LP |
667 | s->state == SOCKET_FINAL_SIGTERM || |
668 | s->state == SOCKET_FINAL_SIGKILL) | |
034c6ed7 LP |
669 | return -EAGAIN; |
670 | ||
83c60c9f LP |
671 | if (s->state == SOCKET_START_PRE || |
672 | s->state == SOCKET_START_POST) | |
034c6ed7 | 673 | return 0; |
83c60c9f | 674 | |
034c6ed7 | 675 | /* Cannot run this without the service being around */ |
87f0e418 | 676 | if (s->service->meta.load_state != UNIT_LOADED) |
034c6ed7 | 677 | return -ENOENT; |
83c60c9f | 678 | |
e537352b LP |
679 | /* If the service is alredy actvie we cannot start the |
680 | * socket */ | |
681 | if (s->service->state != SERVICE_DEAD && | |
682 | s->service->state != SERVICE_MAINTAINANCE && | |
683 | s->service->state != SERVICE_AUTO_RESTART) | |
684 | return -EBUSY; | |
685 | ||
034c6ed7 | 686 | assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE); |
83c60c9f | 687 | |
034c6ed7 LP |
688 | s->failure = false; |
689 | socket_enter_start_pre(s); | |
690 | return 0; | |
691 | } | |
83c60c9f | 692 | |
87f0e418 LP |
693 | static int socket_stop(Unit *u) { |
694 | Socket *s = SOCKET(u); | |
034c6ed7 LP |
695 | |
696 | assert(s); | |
697 | ||
698 | /* We cannot fulfill this request right now, try again later | |
699 | * please! */ | |
700 | if (s->state == SOCKET_START_PRE || | |
701 | s->state == SOCKET_START_POST) | |
702 | return -EAGAIN; | |
83c60c9f | 703 | |
e537352b LP |
704 | /* Already on it */ |
705 | if (s->state == SOCKET_STOP_PRE || | |
706 | s->state == SOCKET_STOP_PRE_SIGTERM || | |
707 | s->state == SOCKET_STOP_PRE_SIGKILL || | |
708 | s->state == SOCKET_STOP_POST || | |
80876c20 LP |
709 | s->state == SOCKET_FINAL_SIGTERM || |
710 | s->state == SOCKET_FINAL_SIGTERM) | |
e537352b LP |
711 | return 0; |
712 | ||
034c6ed7 | 713 | assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING); |
83c60c9f | 714 | |
034c6ed7 | 715 | socket_enter_stop_pre(s, true); |
542563ba LP |
716 | return 0; |
717 | } | |
718 | ||
87f0e418 LP |
719 | static UnitActiveState socket_active_state(Unit *u) { |
720 | assert(u); | |
5cb5a6ff | 721 | |
acbb0225 | 722 | return state_translation_table[SOCKET(u)->state]; |
5cb5a6ff LP |
723 | } |
724 | ||
10a94420 LP |
725 | static const char *socket_sub_state_to_string(Unit *u) { |
726 | assert(u); | |
727 | ||
728 | return state_string_table[SOCKET(u)->state]; | |
729 | } | |
730 | ||
acbb0225 | 731 | static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { |
87f0e418 | 732 | Socket *s = SOCKET(u); |
9152c765 | 733 | |
034c6ed7 | 734 | assert(s); |
9152c765 | 735 | |
9e2f7c11 | 736 | log_debug("Incoming traffic on %s", u->meta.id); |
9152c765 | 737 | |
f94ea366 | 738 | if (events != EPOLLIN) |
034c6ed7 | 739 | socket_enter_stop_pre(s, false); |
9152c765 | 740 | |
034c6ed7 | 741 | socket_enter_running(s); |
9152c765 LP |
742 | } |
743 | ||
87f0e418 LP |
744 | static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { |
745 | Socket *s = SOCKET(u); | |
034c6ed7 | 746 | bool success; |
5cb5a6ff LP |
747 | |
748 | assert(s); | |
034c6ed7 | 749 | assert(pid >= 0); |
5cb5a6ff | 750 | |
bd982a8b | 751 | success = code == CLD_EXITED && status == 0; |
034c6ed7 | 752 | s->failure = s->failure || !success; |
542563ba | 753 | |
034c6ed7 LP |
754 | assert(s->control_pid == pid); |
755 | assert(s->control_command); | |
756 | ||
757 | exec_status_fill(&s->control_command->exec_status, pid, code, status); | |
758 | s->control_pid = 0; | |
759 | ||
9e2f7c11 | 760 | log_debug("%s control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); |
034c6ed7 | 761 | |
80876c20 | 762 | if (s->control_command->command_next && success) { |
9e2f7c11 | 763 | log_debug("%s running next command for state %s", u->meta.id, state_string_table[s->state]); |
034c6ed7 | 764 | socket_run_next(s, success); |
acbb0225 | 765 | } else { |
034c6ed7 LP |
766 | /* No further commands for this step, so let's figure |
767 | * out what to do next */ | |
5cb5a6ff | 768 | |
9e2f7c11 | 769 | log_debug("%s got final SIGCHLD for state %s", u->meta.id, state_string_table[s->state]); |
acbb0225 | 770 | |
034c6ed7 LP |
771 | switch (s->state) { |
772 | ||
773 | case SOCKET_START_PRE: | |
774 | if (success) | |
acbb0225 | 775 | socket_enter_start_post(s); |
034c6ed7 | 776 | else |
80876c20 | 777 | socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); |
034c6ed7 LP |
778 | break; |
779 | ||
780 | case SOCKET_START_POST: | |
781 | if (success) | |
e9af15c3 | 782 | socket_enter_listening(s); |
034c6ed7 LP |
783 | else |
784 | socket_enter_stop_pre(s, false); | |
785 | break; | |
786 | ||
787 | case SOCKET_STOP_PRE: | |
788 | case SOCKET_STOP_PRE_SIGTERM: | |
789 | case SOCKET_STOP_PRE_SIGKILL: | |
790 | socket_enter_stop_post(s, success); | |
791 | break; | |
792 | ||
793 | case SOCKET_STOP_POST: | |
80876c20 LP |
794 | case SOCKET_FINAL_SIGTERM: |
795 | case SOCKET_FINAL_SIGKILL: | |
034c6ed7 LP |
796 | socket_enter_dead(s, success); |
797 | break; | |
798 | ||
799 | default: | |
800 | assert_not_reached("Uh, control process died at wrong time."); | |
801 | } | |
802 | } | |
803 | } | |
5cb5a6ff | 804 | |
acbb0225 | 805 | static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) { |
87f0e418 | 806 | Socket *s = SOCKET(u); |
5cb5a6ff | 807 | |
034c6ed7 LP |
808 | assert(s); |
809 | assert(elapsed == 1); | |
acbb0225 | 810 | assert(w == &s->timer_watch); |
034c6ed7 LP |
811 | |
812 | switch (s->state) { | |
813 | ||
814 | case SOCKET_START_PRE: | |
9e2f7c11 | 815 | log_warning("%s starting timed out. Terminating.", u->meta.id); |
80876c20 LP |
816 | socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); |
817 | ||
034c6ed7 | 818 | case SOCKET_START_POST: |
9e2f7c11 | 819 | log_warning("%s starting timed out. Stopping.", u->meta.id); |
034c6ed7 LP |
820 | socket_enter_stop_pre(s, false); |
821 | break; | |
822 | ||
823 | case SOCKET_STOP_PRE: | |
9e2f7c11 | 824 | log_warning("%s stopping timed out. Terminating.", u->meta.id); |
034c6ed7 LP |
825 | socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, false); |
826 | break; | |
827 | ||
828 | case SOCKET_STOP_PRE_SIGTERM: | |
9e2f7c11 | 829 | log_warning("%s stopping timed out. Killing.", u->meta.id); |
034c6ed7 LP |
830 | socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, false); |
831 | break; | |
832 | ||
833 | case SOCKET_STOP_PRE_SIGKILL: | |
9e2f7c11 | 834 | log_warning("%s still around after SIGKILL. Ignoring.", u->meta.id); |
034c6ed7 LP |
835 | socket_enter_stop_post(s, false); |
836 | break; | |
837 | ||
838 | case SOCKET_STOP_POST: | |
9e2f7c11 | 839 | log_warning("%s stopping timed out (2). Terminating.", u->meta.id); |
80876c20 | 840 | socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); |
034c6ed7 LP |
841 | break; |
842 | ||
80876c20 | 843 | case SOCKET_FINAL_SIGTERM: |
9e2f7c11 | 844 | log_warning("%s stopping timed out (2). Killing.", u->meta.id); |
80876c20 | 845 | socket_enter_signal(s, SOCKET_FINAL_SIGKILL, false); |
034c6ed7 LP |
846 | break; |
847 | ||
80876c20 | 848 | case SOCKET_FINAL_SIGKILL: |
9e2f7c11 | 849 | log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", u->meta.id); |
034c6ed7 LP |
850 | socket_enter_dead(s, false); |
851 | break; | |
852 | ||
853 | default: | |
854 | assert_not_reached("Timeout at wrong time."); | |
855 | } | |
5cb5a6ff LP |
856 | } |
857 | ||
44d8db9e LP |
858 | int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { |
859 | int *rfds; | |
860 | unsigned rn_fds, k; | |
861 | SocketPort *p; | |
862 | ||
863 | assert(s); | |
864 | assert(fds); | |
865 | assert(n_fds); | |
866 | ||
867 | /* Called from the service code for requesting our fds */ | |
868 | ||
869 | rn_fds = 0; | |
870 | LIST_FOREACH(port, p, s->ports) | |
871 | if (p->fd >= 0) | |
872 | rn_fds++; | |
873 | ||
874 | if (!(rfds = new(int, rn_fds)) < 0) | |
875 | return -ENOMEM; | |
876 | ||
877 | k = 0; | |
878 | LIST_FOREACH(port, p, s->ports) | |
879 | if (p->fd >= 0) | |
880 | rfds[k++] = p->fd; | |
881 | ||
882 | assert(k == rn_fds); | |
883 | ||
884 | *fds = rfds; | |
885 | *n_fds = rn_fds; | |
886 | ||
887 | return 0; | |
888 | } | |
889 | ||
ceee3d82 LP |
890 | void socket_notify_service_dead(Socket *s) { |
891 | assert(s); | |
892 | ||
893 | /* The service is dead. Dang. */ | |
894 | ||
895 | if (s->state == SOCKET_RUNNING) { | |
9e2f7c11 | 896 | log_debug("%s got notified about service death.", s->meta.id); |
ceee3d82 LP |
897 | socket_enter_listening(s); |
898 | } | |
899 | } | |
900 | ||
87f0e418 | 901 | const UnitVTable socket_vtable = { |
5cb5a6ff LP |
902 | .suffix = ".socket", |
903 | ||
034c6ed7 | 904 | .init = socket_init, |
e537352b | 905 | .load = socket_load, |
034c6ed7 LP |
906 | .done = socket_done, |
907 | ||
5cb5a6ff LP |
908 | .dump = socket_dump, |
909 | ||
542563ba LP |
910 | .start = socket_start, |
911 | .stop = socket_stop, | |
5cb5a6ff LP |
912 | |
913 | .active_state = socket_active_state, | |
10a94420 | 914 | .sub_state_to_string = socket_sub_state_to_string, |
5cb5a6ff | 915 | |
9152c765 | 916 | .fd_event = socket_fd_event, |
034c6ed7 LP |
917 | .sigchld_event = socket_sigchld_event, |
918 | .timer_event = socket_timer_event | |
5cb5a6ff | 919 | }; |