Line data Source code
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 "sd-bus.h"
17 : #include "sd-login.h"
18 :
19 : #include "libudev-util.h"
20 : #include "string-util.h"
21 : #include "strv.h"
22 : #include "time-util.h"
23 : #include "udev-ctrl.h"
24 : #include "udevadm.h"
25 : #include "unit-def.h"
26 : #include "util.h"
27 : #include "virt.h"
28 :
29 : static usec_t arg_timeout = 120 * USEC_PER_SEC;
30 : static const char *arg_exists = NULL;
31 :
32 0 : static int help(void) {
33 0 : printf("%s settle [OPTIONS]\n\n"
34 : "Wait for pending udev events.\n\n"
35 : " -h --help Show this help\n"
36 : " -V --version Show package version\n"
37 : " -t --timeout=SEC Maximum time to wait for events\n"
38 : " -E --exit-if-exists=FILE Stop waiting if file exists\n"
39 : , program_invocation_short_name);
40 :
41 0 : return 0;
42 : }
43 :
44 0 : static int parse_argv(int argc, char *argv[]) {
45 : static const struct option options[] = {
46 : { "timeout", required_argument, NULL, 't' },
47 : { "exit-if-exists", required_argument, NULL, 'E' },
48 : { "version", no_argument, NULL, 'V' },
49 : { "help", no_argument, NULL, 'h' },
50 : { "seq-start", required_argument, NULL, 's' }, /* removed */
51 : { "seq-end", required_argument, NULL, 'e' }, /* removed */
52 : { "quiet", no_argument, NULL, 'q' }, /* removed */
53 : {}
54 : };
55 :
56 : int c, r;
57 :
58 0 : while ((c = getopt_long(argc, argv, "t:E:Vhs:e:q", options, NULL)) >= 0) {
59 0 : switch (c) {
60 0 : case 't':
61 0 : r = parse_sec(optarg, &arg_timeout);
62 0 : if (r < 0)
63 0 : return log_error_errno(r, "Failed to parse timeout value '%s': %m", optarg);
64 0 : break;
65 0 : case 'E':
66 0 : arg_exists = optarg;
67 0 : break;
68 0 : case 'V':
69 0 : return print_version();
70 0 : case 'h':
71 0 : return help();
72 0 : case 's':
73 : case 'e':
74 : case 'q':
75 0 : return log_info_errno(SYNTHETIC_ERRNO(EINVAL),
76 : "Option -%c no longer supported.",
77 : c);
78 0 : case '?':
79 0 : return -EINVAL;
80 0 : default:
81 0 : assert_not_reached("Unknown option.");
82 : }
83 : }
84 :
85 0 : return 1;
86 : }
87 :
88 0 : static int emit_deprecation_warning(void) {
89 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
90 0 : _cleanup_free_ char *unit = NULL, *unit_path = NULL;
91 0 : _cleanup_strv_free_ char **a = NULL, **b = NULL;
92 : int r;
93 :
94 0 : r = sd_pid_get_unit(0, &unit);
95 0 : if (r < 0 || !streq(unit, "systemd-udev-settle.service"))
96 0 : return 0;
97 :
98 0 : log_notice("systemd-udev-settle.service is deprecated.");
99 :
100 0 : r = sd_bus_open_system(&bus);
101 0 : if (r < 0)
102 0 : return log_debug_errno(r, "Failed to open system bus, skipping dependency queries: %m");
103 :
104 0 : unit_path = unit_dbus_path_from_name("systemd-udev-settle.service");
105 0 : if (!unit_path)
106 0 : return -ENOMEM;
107 :
108 0 : (void) sd_bus_get_property_strv(
109 : bus,
110 : "org.freedesktop.systemd1",
111 : unit_path,
112 : "org.freedesktop.systemd1.Unit",
113 : "WantedBy",
114 : NULL,
115 : &a);
116 :
117 0 : (void) sd_bus_get_property_strv(
118 : bus,
119 : "org.freedesktop.systemd1",
120 : unit_path,
121 : "org.freedesktop.systemd1.Unit",
122 : "RequiredBy",
123 : NULL,
124 : &b);
125 :
126 0 : r = strv_extend_strv(&a, b, true);
127 0 : if (r < 0)
128 0 : return r;
129 :
130 0 : if (!strv_isempty(a)) {
131 0 : _cleanup_free_ char *t = NULL;
132 :
133 0 : t = strv_join(a, ", ");
134 0 : if (!t)
135 0 : return -ENOMEM;
136 :
137 0 : log_notice("Hint: please fix %s not to pull it in.", t);
138 : }
139 :
140 0 : return 0;
141 : }
142 :
143 0 : int settle_main(int argc, char *argv[], void *userdata) {
144 0 : _cleanup_(udev_queue_unrefp) struct udev_queue *queue = NULL;
145 : struct pollfd pfd;
146 : usec_t deadline;
147 : int r;
148 :
149 0 : r = parse_argv(argc, argv);
150 0 : if (r <= 0)
151 0 : return r;
152 :
153 0 : if (running_in_chroot() > 0) {
154 0 : log_info("Running in chroot, ignoring request.");
155 0 : return 0;
156 : }
157 :
158 0 : deadline = now(CLOCK_MONOTONIC) + arg_timeout;
159 :
160 : /* guarantee that the udev daemon isn't pre-processing */
161 0 : if (getuid() == 0) {
162 0 : _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
163 :
164 0 : if (udev_ctrl_new(&uctrl) >= 0) {
165 0 : r = udev_ctrl_send_ping(uctrl);
166 0 : if (r < 0) {
167 0 : log_debug_errno(r, "Failed to connect to udev daemon: %m");
168 0 : return 0;
169 : }
170 :
171 0 : r = udev_ctrl_wait(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout));
172 0 : if (r < 0)
173 0 : return log_error_errno(r, "Failed to wait for daemon to reply: %m");
174 : }
175 : }
176 :
177 0 : queue = udev_queue_new(NULL);
178 0 : if (!queue)
179 0 : return log_error_errno(errno, "Failed to get udev queue: %m");
180 :
181 0 : r = udev_queue_get_fd(queue);
182 0 : if (r < 0) {
183 0 : log_debug_errno(r, "Queue is empty, nothing to watch.");
184 0 : return 0;
185 : }
186 :
187 0 : pfd = (struct pollfd) {
188 : .events = POLLIN,
189 : .fd = r,
190 : };
191 :
192 0 : (void) emit_deprecation_warning();
193 :
194 : for (;;) {
195 0 : if (arg_exists && access(arg_exists, F_OK) >= 0)
196 0 : return 0;
197 :
198 : /* exit if queue is empty */
199 0 : if (udev_queue_get_queue_is_empty(queue))
200 0 : return 0;
201 :
202 0 : if (now(CLOCK_MONOTONIC) >= deadline)
203 0 : return -ETIMEDOUT;
204 :
205 : /* wake up when queue becomes empty */
206 0 : if (poll(&pfd, 1, MSEC_PER_SEC) > 0 && pfd.revents & POLLIN) {
207 0 : r = udev_queue_flush(queue);
208 0 : if (r < 0)
209 0 : return log_error_errno(r, "Failed to flush queue: %m");
210 : }
211 : }
212 : }
|