]>
Commit | Line | Data |
---|---|---|
ac6e2f0d DR |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2015 Canonical | |
7 | ||
8 | Author: | |
9 | Didier Roche <didrocks@ubuntu.com> | |
10 | ||
11 | systemd is free software; you can redistribute it and/or modify it | |
12 | under the terms of the GNU Lesser General Public License as published by | |
13 | the Free Software Foundation; either version 2.1 of the License, or | |
14 | (at your option) any later version. | |
15 | ||
16 | systemd is distributed in the hope that it will be useful, but | |
17 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 | Lesser General Public License for more details. | |
20 | ||
21 | You should have received a copy of the GNU Lesser General Public License | |
22 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
23 | ***/ | |
24 | ||
25 | #include <getopt.h> | |
26 | #include <errno.h> | |
b0d92464 | 27 | #include <libintl.h> |
ac6e2f0d DR |
28 | #include <math.h> |
29 | #include <stdbool.h> | |
30 | #include <stdlib.h> | |
31 | #include <stdio.h> | |
32 | #include <sys/socket.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/un.h> | |
35 | #include <unistd.h> | |
36 | ||
d2268a20 | 37 | #include "sd-daemon.h" |
ac6e2f0d | 38 | #include "build.h" |
07f9a21b | 39 | #include "def.h" |
ac6e2f0d | 40 | #include "event-util.h" |
ac6e2f0d DR |
41 | #include "log.h" |
42 | #include "list.h" | |
43 | #include "macro.h" | |
ac6e2f0d DR |
44 | #include "socket-util.h" |
45 | #include "util.h" | |
d2268a20 | 46 | #include "fsckd.h" |
ac6e2f0d DR |
47 | |
48 | #define IDLE_TIME_SECONDS 30 | |
07f9a21b | 49 | #define PLYMOUTH_REQUEST_KEY "K\2\2\3" |
e78e0674 | 50 | #define CLIENTS_MAX 128 |
ac6e2f0d DR |
51 | |
52 | struct Manager; | |
53 | ||
54 | typedef struct Client { | |
55 | struct Manager *manager; | |
56 | int fd; | |
57 | dev_t devnum; | |
f8824a51 | 58 | |
ac6e2f0d DR |
59 | size_t cur; |
60 | size_t max; | |
61 | int pass; | |
f8824a51 | 62 | |
ac6e2f0d | 63 | double percent; |
f8824a51 | 64 | |
ac6e2f0d | 65 | size_t buflen; |
07f9a21b | 66 | bool cancelled; |
ac6e2f0d | 67 | |
d42688ef LP |
68 | sd_event_source *event_source; |
69 | ||
ac6e2f0d DR |
70 | LIST_FIELDS(struct Client, clients); |
71 | } Client; | |
72 | ||
73 | typedef struct Manager { | |
74 | sd_event *event; | |
f8824a51 LP |
75 | |
76 | LIST_HEAD(Client, clients); | |
e78e0674 | 77 | unsigned n_clients; |
f8824a51 | 78 | |
ac6e2f0d | 79 | int clear; |
57b394b5 | 80 | |
ac6e2f0d | 81 | int connection_fd; |
57b394b5 | 82 | sd_event_source *connection_event_source; |
f8824a51 | 83 | |
ac6e2f0d | 84 | FILE *console; |
1952708a | 85 | |
ac6e2f0d DR |
86 | double percent; |
87 | int numdevices; | |
70463136 | 88 | |
07f9a21b | 89 | int plymouth_fd; |
70463136 | 90 | sd_event_source *plymouth_event_source; |
07f9a21b | 91 | bool plymouth_cancel_sent; |
f8824a51 | 92 | |
07f9a21b | 93 | bool cancel_requested; |
ac6e2f0d DR |
94 | } Manager; |
95 | ||
d42688ef | 96 | static void client_free(Client *c); |
ac6e2f0d | 97 | static void manager_free(Manager *m); |
d42688ef LP |
98 | |
99 | DEFINE_TRIVIAL_CLEANUP_FUNC(Client*, client_free); | |
ac6e2f0d | 100 | DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); |
ac6e2f0d DR |
101 | |
102 | static double compute_percent(int pass, size_t cur, size_t max) { | |
103 | /* Values stolen from e2fsck */ | |
104 | ||
105 | static const double pass_table[] = { | |
106 | 0, 70, 90, 92, 95, 100 | |
107 | }; | |
108 | ||
109 | if (pass <= 0) | |
110 | return 0.0; | |
111 | ||
112 | if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0) | |
113 | return 100.0; | |
114 | ||
115 | return pass_table[pass-1] + | |
116 | (pass_table[pass] - pass_table[pass-1]) * | |
117 | (double) cur / max; | |
118 | } | |
119 | ||
0b02c7c3 | 120 | static int client_request_cancel(Client *c) { |
d2268a20 LP |
121 | FsckdMessage cancel_msg = { |
122 | .cancel = 1, | |
123 | }; | |
124 | ||
07f9a21b | 125 | ssize_t n; |
07f9a21b | 126 | |
0b02c7c3 LP |
127 | assert(c); |
128 | ||
129 | if (c->cancelled) | |
130 | return 0; | |
131 | ||
132 | n = send(c->fd, &cancel_msg, sizeof(FsckdMessage), 0); | |
d2268a20 | 133 | if (n < 0) |
0b02c7c3 | 134 | return log_warning_errno(errno, "Cannot send cancel to fsck on (%u:%u): %m", major(c->devnum), minor(c->devnum)); |
d2268a20 | 135 | if ((size_t) n < sizeof(FsckdMessage)) { |
0b02c7c3 | 136 | log_warning("Short send when sending cancel to fsck on (%u:%u).", major(c->devnum), minor(c->devnum)); |
d2268a20 LP |
137 | return -EIO; |
138 | } | |
139 | ||
0b02c7c3 LP |
140 | c->cancelled = true; |
141 | return 1; | |
07f9a21b | 142 | } |
ac6e2f0d | 143 | |
f8824a51 LP |
144 | static void client_free(Client *c) { |
145 | assert(c); | |
146 | ||
e78e0674 | 147 | if (c->manager) { |
f8824a51 | 148 | LIST_REMOVE(clients, c->manager->clients, c); |
e78e0674 LP |
149 | c->manager->n_clients--; |
150 | } | |
f8824a51 | 151 | |
d42688ef LP |
152 | sd_event_source_unref(c->event_source); |
153 | ||
f8824a51 LP |
154 | safe_close(c->fd); |
155 | free(c); | |
ac6e2f0d DR |
156 | } |
157 | ||
88b28381 | 158 | static void manager_disconnect_plymouth(Manager *m) { |
70463136 LP |
159 | assert(m); |
160 | ||
161 | m->plymouth_event_source = sd_event_source_unref(m->plymouth_event_source); | |
c011cc26 | 162 | m->plymouth_fd = safe_close(m->plymouth_fd); |
07f9a21b DR |
163 | m->plymouth_cancel_sent = false; |
164 | } | |
165 | ||
88b28381 | 166 | static int manager_plymouth_feedback_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
07f9a21b DR |
167 | Manager *m = userdata; |
168 | Client *current; | |
169 | char buffer[6]; | |
70463136 | 170 | ssize_t l; |
07f9a21b DR |
171 | |
172 | assert(m); | |
173 | ||
70463136 LP |
174 | l = read(m->plymouth_fd, buffer, sizeof(buffer)); |
175 | if (l < 0) { | |
176 | log_warning_errno(errno, "Got error while reading from plymouth: %m"); | |
88b28381 | 177 | manager_disconnect_plymouth(m); |
70463136 LP |
178 | return -errno; |
179 | } | |
180 | if (l == 0) { | |
88b28381 | 181 | manager_disconnect_plymouth(m); |
70463136 LP |
182 | return 0; |
183 | } | |
184 | ||
df4573e8 | 185 | if (l > 1 && buffer[0] == '\15') |
70463136 LP |
186 | log_error("Message update to plymouth wasn't delivered successfully"); |
187 | ||
188 | /* the only answer support type we requested is a key interruption */ | |
df4573e8 | 189 | if (l > 2 && buffer[0] == '\2' && buffer[5] == '\3') { |
70463136 LP |
190 | m->cancel_requested = true; |
191 | ||
192 | /* cancel all connected clients */ | |
193 | LIST_FOREACH(clients, current, m->clients) | |
0b02c7c3 | 194 | client_request_cancel(current); |
07f9a21b DR |
195 | } |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
88b28381 LP |
200 | static int manager_connect_plymouth(Manager *m) { |
201 | union sockaddr_union sa = PLYMOUTH_SOCKET; | |
202 | int r; | |
203 | ||
204 | /* try to connect or reconnect if sending a message */ | |
205 | if (m->plymouth_fd >= 0) | |
206 | return 0; | |
207 | ||
208 | m->plymouth_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); | |
209 | if (m->plymouth_fd < 0) | |
210 | return log_warning_errno(errno, "Connection to plymouth socket failed: %m"); | |
211 | ||
212 | if (connect(m->plymouth_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { | |
213 | r = log_warning_errno(errno, "Couldn't connect to plymouth: %m"); | |
214 | goto fail; | |
215 | } | |
216 | ||
217 | r = sd_event_add_io(m->event, &m->plymouth_event_source, m->plymouth_fd, EPOLLIN, manager_plymouth_feedback_handler, m); | |
218 | if (r < 0) { | |
219 | log_warning_errno(r, "Can't listen to plymouth socket: %m"); | |
220 | goto fail; | |
221 | } | |
222 | ||
223 | return 1; | |
224 | ||
225 | fail: | |
226 | manager_disconnect_plymouth(m); | |
227 | return r; | |
228 | } | |
229 | ||
230 | static int plymouth_send_message(int plymouth_fd, const char *message, bool update) { | |
07f9a21b | 231 | _cleanup_free_ char *packet = NULL; |
3963cea3 | 232 | int n; |
07f9a21b DR |
233 | char mode = 'M'; |
234 | ||
235 | if (update) | |
236 | mode = 'U'; | |
237 | ||
238 | if (asprintf(&packet, "%c\002%c%s%n", mode, (int) (strlen(message) + 1), message, &n) < 0) | |
239 | return log_oom(); | |
07f9a21b | 240 | |
3963cea3 LP |
241 | return loop_write(plymouth_fd, packet, n + 1, true); |
242 | } | |
07f9a21b | 243 | |
88b28381 | 244 | static int manager_send_plymouth_message(Manager *m, const char *message) { |
07f9a21b | 245 | const char *plymouth_cancel_message = NULL; |
e9d2527f | 246 | int r; |
07f9a21b | 247 | |
88b28381 | 248 | r = manager_connect_plymouth(m); |
07f9a21b DR |
249 | if (r < 0) |
250 | return r; | |
251 | ||
252 | if (!m->plymouth_cancel_sent) { | |
e9d2527f LP |
253 | |
254 | /* Indicate to plymouth that we listen to Ctrl+C */ | |
07f9a21b DR |
255 | r = loop_write(m->plymouth_fd, PLYMOUTH_REQUEST_KEY, sizeof(PLYMOUTH_REQUEST_KEY), true); |
256 | if (r < 0) | |
e9d2527f LP |
257 | return log_warning_errno(r, "Can't send to plymouth cancel key: %m"); |
258 | ||
07f9a21b | 259 | m->plymouth_cancel_sent = true; |
e9d2527f | 260 | |
b0d92464 | 261 | plymouth_cancel_message = strjoina("fsckd-cancel-msg:", _("Press Ctrl+C to cancel all filesystem checks in progress")); |
e9d2527f | 262 | |
88b28381 | 263 | r = plymouth_send_message(m->plymouth_fd, plymouth_cancel_message, false); |
07f9a21b DR |
264 | if (r < 0) |
265 | log_warning_errno(r, "Can't send filesystem cancel message to plymouth: %m"); | |
e9d2527f | 266 | |
07f9a21b | 267 | } else if (m->numdevices == 0) { |
e9d2527f | 268 | |
07f9a21b | 269 | m->plymouth_cancel_sent = false; |
e9d2527f | 270 | |
88b28381 | 271 | r = plymouth_send_message(m->plymouth_fd, "", false); |
07f9a21b DR |
272 | if (r < 0) |
273 | log_warning_errno(r, "Can't clear plymouth filesystem cancel message: %m"); | |
274 | } | |
275 | ||
88b28381 | 276 | r = plymouth_send_message(m->plymouth_fd, message, true); |
07f9a21b | 277 | if (r < 0) |
e9d2527f | 278 | return log_warning_errno(r, "Couldn't send \"%s\" to plymouth: %m", message); |
07f9a21b DR |
279 | |
280 | return 0; | |
281 | } | |
282 | ||
88b28381 | 283 | static int manager_update_global_progress(Manager *m) { |
ac6e2f0d DR |
284 | Client *current = NULL; |
285 | _cleanup_free_ char *console_message = NULL; | |
07f9a21b DR |
286 | _cleanup_free_ char *fsck_message = NULL; |
287 | int current_numdevices = 0, l = 0, r; | |
ac6e2f0d DR |
288 | double current_percent = 100; |
289 | ||
290 | /* get the overall percentage */ | |
291 | LIST_FOREACH(clients, current, m->clients) { | |
292 | current_numdevices++; | |
293 | ||
294 | /* right now, we only keep the minimum % of all fsckd processes. We could in the future trying to be | |
295 | linear, but max changes and corresponds to the pass. We have all the informations into fsckd | |
296 | already if we can treat that in a smarter way. */ | |
297 | current_percent = MIN(current_percent, current->percent); | |
298 | } | |
299 | ||
300 | /* update if there is anything user-visible to update */ | |
301 | if (fabs(current_percent - m->percent) > 0.001 || current_numdevices != m->numdevices) { | |
302 | m->numdevices = current_numdevices; | |
303 | m->percent = current_percent; | |
304 | ||
b0d92464 DR |
305 | if (asprintf(&console_message, |
306 | ngettext("Checking in progress on %d disk (%3.1f%% complete)", | |
307 | "Checking in progress on %d disks (%3.1f%% complete)", m->numdevices), | |
308 | m->numdevices, m->percent) < 0) | |
ac6e2f0d | 309 | return -ENOMEM; |
705778ad | 310 | |
07f9a21b DR |
311 | if (asprintf(&fsck_message, "fsckd:%d:%3.1f:%s", m->numdevices, m->percent, console_message) < 0) |
312 | return -ENOMEM; | |
ac6e2f0d DR |
313 | |
314 | /* write to console */ | |
315 | if (m->console) { | |
316 | fprintf(m->console, "\r%s\r%n", console_message, &l); | |
317 | fflush(m->console); | |
318 | } | |
319 | ||
07f9a21b | 320 | /* try to connect to plymouth and send message */ |
88b28381 | 321 | r = manager_send_plymouth_message(m, fsck_message); |
07f9a21b DR |
322 | if (r < 0) |
323 | log_debug("Couldn't send message to plymouth"); | |
324 | ||
ac6e2f0d DR |
325 | if (l > m->clear) |
326 | m->clear = l; | |
327 | } | |
328 | return 0; | |
329 | } | |
330 | ||
88b28381 | 331 | static int client_progress_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
ac6e2f0d | 332 | Client *client = userdata; |
ac6e2f0d DR |
333 | FsckProgress fsck_data; |
334 | size_t buflen; | |
88b28381 | 335 | Manager *m; |
ac6e2f0d DR |
336 | int r; |
337 | ||
338 | assert(client); | |
88b28381 | 339 | |
ac6e2f0d DR |
340 | m = client->manager; |
341 | ||
07f9a21b | 342 | /* check first if we need to cancel this client */ |
0b02c7c3 LP |
343 | if (m->cancel_requested) |
344 | client_request_cancel(client); | |
07f9a21b | 345 | |
ac6e2f0d DR |
346 | /* ensure we have enough data to read */ |
347 | r = ioctl(fd, FIONREAD, &buflen); | |
348 | if (r == 0 && buflen != 0 && (size_t) buflen < sizeof(FsckProgress)) { | |
349 | if (client->buflen != buflen) | |
350 | client->buflen = buflen; | |
351 | /* we got twice the same size from a bad behaving client, kick it off the list */ | |
352 | else { | |
353 | log_warning("Closing bad behaving fsck client connection at fd %d", client->fd); | |
f8824a51 | 354 | client_free(client); |
88b28381 | 355 | r = manager_update_global_progress(m); |
ac6e2f0d DR |
356 | if (r < 0) |
357 | log_warning_errno(r, "Couldn't update global progress: %m"); | |
358 | } | |
359 | return 0; | |
360 | } | |
361 | ||
362 | /* read actual data */ | |
363 | r = recv(fd, &fsck_data, sizeof(FsckProgress), 0); | |
364 | if (r == 0) { | |
365 | log_debug("Fsck client connected to fd %d disconnected", client->fd); | |
f8824a51 | 366 | client_free(client); |
ac6e2f0d DR |
367 | } else if (r > 0 && r != sizeof(FsckProgress)) |
368 | log_warning("Unexpected data structure sent to fsckd socket from fd: %d. Ignoring", client->fd); | |
369 | else if (r > 0 && r == sizeof(FsckProgress)) { | |
370 | client->devnum = fsck_data.devnum; | |
371 | client->cur = fsck_data.cur; | |
372 | client->max = fsck_data.max; | |
373 | client->pass = fsck_data.pass; | |
374 | client->percent = compute_percent(client->pass, client->cur, client->max); | |
375 | log_debug("Getting progress for %u:%u (%lu, %lu, %d) : %3.1f%%", | |
376 | major(client->devnum), minor(client->devnum), | |
377 | client->cur, client->max, client->pass, client->percent); | |
378 | } else | |
379 | log_error_errno(r, "Unknown error while trying to read fsck data: %m"); | |
380 | ||
88b28381 | 381 | r = manager_update_global_progress(m); |
ac6e2f0d DR |
382 | if (r < 0) |
383 | log_warning_errno(r, "Couldn't update global progress: %m"); | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
88b28381 | 388 | static int manager_new_connection_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
d42688ef | 389 | _cleanup_(client_freep) Client *c = NULL; |
e78e0674 | 390 | _cleanup_close_ int new_client_fd = -1; |
ac6e2f0d | 391 | Manager *m = userdata; |
e78e0674 | 392 | int r; |
ac6e2f0d DR |
393 | |
394 | assert(m); | |
395 | ||
396 | /* Initialize and list new clients */ | |
397 | new_client_fd = accept4(m->connection_fd, NULL, NULL, SOCK_CLOEXEC); | |
2de68ed9 | 398 | if (new_client_fd < 0) |
ac6e2f0d DR |
399 | return log_error_errno(errno, "Couldn't accept a new connection: %m"); |
400 | ||
e78e0674 LP |
401 | if (m->n_clients >= CLIENTS_MAX) { |
402 | log_error("Too many clients, refusing connection."); | |
403 | return 0; | |
404 | } | |
405 | ||
2de68ed9 LP |
406 | log_debug("New fsck client connected to fd: %d", new_client_fd); |
407 | ||
d42688ef LP |
408 | c = new0(Client, 1); |
409 | if (!c) { | |
d42688ef LP |
410 | log_oom(); |
411 | return 0; | |
412 | } | |
413 | ||
414 | c->fd = new_client_fd; | |
e78e0674 LP |
415 | new_client_fd = -1; |
416 | ||
d42688ef | 417 | r = sd_event_add_io(m->event, &c->event_source, c->fd, EPOLLIN, client_progress_handler, c); |
2de68ed9 | 418 | if (r < 0) { |
d42688ef LP |
419 | log_oom(); |
420 | return 0; | |
2de68ed9 | 421 | } |
d42688ef LP |
422 | |
423 | LIST_PREPEND(clients, m->clients, c); | |
e78e0674 | 424 | m->n_clients++; |
d42688ef LP |
425 | c->manager = m; |
426 | ||
2de68ed9 LP |
427 | /* only request the client to cancel now in case the request is dropped by the client (chance to recancel) */ |
428 | if (m->cancel_requested) | |
d42688ef | 429 | client_request_cancel(c); |
2de68ed9 | 430 | |
d42688ef | 431 | c = NULL; |
ac6e2f0d DR |
432 | return 0; |
433 | } | |
434 | ||
435 | static void manager_free(Manager *m) { | |
ac6e2f0d DR |
436 | if (!m) |
437 | return; | |
438 | ||
439 | /* clear last line */ | |
440 | if (m->console && m->clear > 0) { | |
441 | unsigned j; | |
442 | ||
443 | fputc('\r', m->console); | |
444 | for (j = 0; j < (unsigned) m->clear; j++) | |
445 | fputc(' ', m->console); | |
446 | fputc('\r', m->console); | |
447 | fflush(m->console); | |
448 | } | |
449 | ||
57b394b5 | 450 | sd_event_source_unref(m->connection_event_source); |
ac6e2f0d | 451 | safe_close(m->connection_fd); |
70463136 | 452 | |
f8824a51 LP |
453 | while (m->clients) |
454 | client_free(m->clients); | |
ac6e2f0d | 455 | |
88b28381 LP |
456 | manager_disconnect_plymouth(m); |
457 | ||
458 | if (m->console) | |
459 | fclose(m->console); | |
460 | ||
ac6e2f0d DR |
461 | sd_event_unref(m->event); |
462 | ||
463 | free(m); | |
464 | } | |
465 | ||
466 | static int manager_new(Manager **ret, int fd) { | |
88b28381 | 467 | _cleanup_(manager_freep) Manager *m = NULL; |
ac6e2f0d DR |
468 | int r; |
469 | ||
470 | assert(ret); | |
471 | ||
472 | m = new0(Manager, 1); | |
473 | if (!m) | |
474 | return -ENOMEM; | |
475 | ||
88b28381 LP |
476 | m->plymouth_fd = -1; |
477 | m->connection_fd = fd; | |
478 | m->percent = 100; | |
479 | ||
ac6e2f0d DR |
480 | r = sd_event_default(&m->event); |
481 | if (r < 0) | |
482 | return r; | |
483 | ||
19e887e7 DR |
484 | if (access("/run/systemd/show-status", F_OK) >= 0) { |
485 | m->console = fopen("/dev/console", "we"); | |
486 | if (!m->console) | |
88b28381 | 487 | return -errno; |
19e887e7 | 488 | } |
ac6e2f0d | 489 | |
57b394b5 | 490 | r = sd_event_add_io(m->event, &m->connection_event_source, fd, EPOLLIN, manager_new_connection_handler, m); |
88b28381 LP |
491 | if (r < 0) |
492 | return r; | |
493 | ||
ac6e2f0d DR |
494 | *ret = m; |
495 | m = NULL; | |
496 | ||
497 | return 0; | |
498 | } | |
499 | ||
500 | static int run_event_loop_with_timeout(sd_event *e, usec_t timeout) { | |
501 | int r, code; | |
502 | ||
503 | assert(e); | |
504 | ||
505 | for (;;) { | |
506 | r = sd_event_get_state(e); | |
507 | if (r < 0) | |
508 | return r; | |
509 | if (r == SD_EVENT_FINISHED) | |
510 | break; | |
511 | ||
512 | r = sd_event_run(e, timeout); | |
513 | if (r < 0) | |
514 | return r; | |
515 | ||
516 | /* timeout reached */ | |
517 | if (r == 0) { | |
518 | sd_event_exit(e, 0); | |
519 | break; | |
520 | } | |
521 | } | |
522 | ||
523 | r = sd_event_get_exit_code(e, &code); | |
524 | if (r < 0) | |
525 | return r; | |
526 | ||
527 | return code; | |
528 | } | |
529 | ||
530 | static void help(void) { | |
531 | printf("%s [OPTIONS...]\n\n" | |
532 | "Capture fsck progress and forward one stream to plymouth\n\n" | |
533 | " -h --help Show this help\n" | |
534 | " --version Show package version\n", | |
535 | program_invocation_short_name); | |
536 | } | |
537 | ||
538 | static int parse_argv(int argc, char *argv[]) { | |
539 | ||
540 | enum { | |
541 | ARG_VERSION = 0x100, | |
542 | ARG_ROOT, | |
543 | }; | |
544 | ||
545 | static const struct option options[] = { | |
546 | { "help", no_argument, NULL, 'h' }, | |
547 | { "version", no_argument, NULL, ARG_VERSION }, | |
548 | {} | |
549 | }; | |
550 | ||
551 | int c; | |
552 | ||
553 | assert(argc >= 0); | |
554 | assert(argv); | |
555 | ||
556 | while ((c = getopt_long(argc, argv, "hv", options, NULL)) >= 0) | |
557 | switch (c) { | |
558 | ||
559 | case 'h': | |
560 | help(); | |
561 | return 0; | |
562 | ||
563 | case ARG_VERSION: | |
564 | puts(PACKAGE_STRING); | |
565 | puts(SYSTEMD_FEATURES); | |
566 | return 0; | |
567 | ||
568 | case '?': | |
569 | return -EINVAL; | |
570 | ||
571 | default: | |
572 | assert_not_reached("Unhandled option"); | |
573 | } | |
574 | ||
575 | if (optind < argc) { | |
576 | log_error("Extraneous arguments"); | |
577 | return -EINVAL; | |
578 | } | |
579 | ||
580 | return 1; | |
581 | } | |
582 | ||
583 | int main(int argc, char *argv[]) { | |
88b28381 | 584 | _cleanup_(manager_freep) Manager *m = NULL; |
ac6e2f0d DR |
585 | int fd = -1; |
586 | int r, n; | |
587 | ||
588 | log_set_target(LOG_TARGET_AUTO); | |
589 | log_parse_environment(); | |
590 | log_open(); | |
b0d92464 | 591 | init_gettext(); |
ac6e2f0d DR |
592 | |
593 | r = parse_argv(argc, argv); | |
594 | if (r <= 0) | |
a922c18b | 595 | goto finish; |
ac6e2f0d DR |
596 | |
597 | n = sd_listen_fds(0); | |
598 | if (n > 1) { | |
599 | log_error("Too many file descriptors received."); | |
a922c18b LP |
600 | r = -EINVAL; |
601 | goto finish; | |
adfe5671 | 602 | } else if (n == 1) |
ac6e2f0d | 603 | fd = SD_LISTEN_FDS_START + 0; |
adfe5671 | 604 | else { |
ac6e2f0d DR |
605 | fd = make_socket_fd(LOG_DEBUG, FSCKD_SOCKET_PATH, SOCK_STREAM | SOCK_CLOEXEC); |
606 | if (fd < 0) { | |
a922c18b LP |
607 | r = log_error_errno(fd, "Couldn't create listening socket fd on %s: %m", FSCKD_SOCKET_PATH); |
608 | goto finish; | |
ac6e2f0d DR |
609 | } |
610 | } | |
611 | ||
612 | r = manager_new(&m, fd); | |
613 | if (r < 0) { | |
614 | log_error_errno(r, "Failed to allocate manager: %m"); | |
a922c18b | 615 | goto finish; |
ac6e2f0d DR |
616 | } |
617 | ||
ac6e2f0d DR |
618 | r = run_event_loop_with_timeout(m->event, IDLE_TIME_SECONDS * USEC_PER_SEC); |
619 | if (r < 0) { | |
620 | log_error_errno(r, "Failed to run event loop: %m"); | |
a922c18b | 621 | goto finish; |
ac6e2f0d DR |
622 | } |
623 | ||
624 | sd_event_get_exit_code(m->event, &r); | |
625 | ||
a922c18b | 626 | finish: |
ac6e2f0d DR |
627 | return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; |
628 | } |