Branch data 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 : : }
|