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