]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-settle.c
a172b25adad80fb39b4a9159c6390c3b937ad4c1
[thirdparty/systemd.git] / src / udev / udevadm-settle.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2 /*
3 * Copyright © 2009 Canonical Ltd.
4 * Copyright © 2009 Scott James Remnant <scott@netsplit.com>
5 */
6
7 #include <errno.h>
8 #include <getopt.h>
9 #include <poll.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include "time-util.h"
17 #include "udevadm.h"
18 #include "udev.h"
19
20 static usec_t arg_timeout = 120 * USEC_PER_SEC;
21 static const char *arg_exists = NULL;
22
23 static int help(void) {
24 printf("%s settle [OPTIONS]\n\n"
25 "Wait for pending udev events.\n\n"
26 " -h --help Show this help\n"
27 " -V --version Show package version\n"
28 " -t --timeout=SEC Maximum time to wait for events\n"
29 " -E --exit-if-exists=FILE Stop waiting if file exists\n"
30 , program_invocation_short_name);
31
32 return 0;
33 }
34
35 static int parse_argv(int argc, char *argv[]) {
36 static const struct option options[] = {
37 { "timeout", required_argument, NULL, 't' },
38 { "exit-if-exists", required_argument, NULL, 'E' },
39 { "version", no_argument, NULL, 'V' },
40 { "help", no_argument, NULL, 'h' },
41 { "seq-start", required_argument, NULL, 's' }, /* removed */
42 { "seq-end", required_argument, NULL, 'e' }, /* removed */
43 { "quiet", no_argument, NULL, 'q' }, /* removed */
44 {}
45 };
46
47 int c, r;
48
49 while ((c = getopt_long(argc, argv, "t:E:Vhs:e:q", options, NULL)) >= 0) {
50 switch (c) {
51 case 't':
52 r = parse_sec(optarg, &arg_timeout);
53 if (r < 0)
54 return log_error_errno(r, "Failed to parse timeout value '%s': %m", optarg);
55 break;
56 case 'E':
57 arg_exists = optarg;
58 break;
59 case 'V':
60 return version();
61 case 'h':
62 return help();
63 case 's':
64 case 'e':
65 case 'q':
66 log_info("Option -%c no longer supported.", c);
67 return -EINVAL;
68 case '?':
69 return -EINVAL;
70 default:
71 assert_not_reached("Unknown option.");
72 }
73 }
74
75 return 1;
76 }
77
78 int settle_main(int argc, char *argv[], void *userdata) {
79 _cleanup_(udev_queue_unrefp) struct udev_queue *queue = NULL;
80 struct pollfd pfd;
81 usec_t deadline;
82 int r;
83
84 r = parse_argv(argc, argv);
85 if (r <= 0)
86 return r;
87
88 deadline = now(CLOCK_MONOTONIC) + arg_timeout;
89
90 /* guarantee that the udev daemon isn't pre-processing */
91 if (getuid() == 0) {
92 _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
93
94 uctrl = udev_ctrl_new();
95 if (uctrl) {
96 r = udev_ctrl_send_ping(uctrl, MAX(5U, arg_timeout / USEC_PER_SEC));
97 if (r < 0) {
98 log_debug_errno(r, "Failed to connect to udev daemon.");
99 return 0;
100 }
101 }
102 }
103
104 queue = udev_queue_new(NULL);
105 if (!queue)
106 return log_error_errno(errno, "Failed to get udev queue: %m");
107
108 r = udev_queue_get_fd(queue);
109 if (r < 0) {
110 log_debug_errno(r, "Queue is empty, nothing to watch.");
111 return 0;
112 }
113
114 pfd = (struct pollfd) {
115 .events = POLLIN,
116 .fd = r,
117 };
118
119 for (;;) {
120 if (arg_exists && access(arg_exists, F_OK) >= 0)
121 return 0;
122
123 /* exit if queue is empty */
124 if (udev_queue_get_queue_is_empty(queue))
125 return 0;
126
127 if (now(CLOCK_MONOTONIC) >= deadline)
128 return -ETIMEDOUT;
129
130 /* wake up when queue becomes empty */
131 if (poll(&pfd, 1, MSEC_PER_SEC) > 0 && pfd.revents & POLLIN) {
132 r = udev_queue_flush(queue);
133 if (r < 0)
134 return log_error_errno(r, "Failed to flush queue: %m");
135 }
136 }
137 }