1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <getopt.h>
4 #include <stdlib.h>
5 #include <sys/file.h>
6 #include <unistd.h>
7 
8 #include "blockdev-util.h"
9 #include "btrfs-util.h"
10 #include "devnum-util.h"
11 #include "fd-util.h"
12 #include "fdset.h"
13 #include "main-func.h"
14 #include "parse-util.h"
15 #include "path-util.h"
16 #include "pretty-print.h"
17 #include "process-util.h"
18 #include "signal-util.h"
19 #include "sort-util.h"
20 #include "strv.h"
21 #include "time-util.h"
22 #include "udevadm.h"
23 
24 static usec_t arg_timeout_usec = USEC_INFINITY;
25 static char **arg_devices = NULL;
26 static char **arg_backing = NULL;
27 static char **arg_cmdline = NULL;
28 static bool arg_print = false;
29 
30 STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep);
31 STATIC_DESTRUCTOR_REGISTER(arg_backing, strv_freep);
32 STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
33 
help(void)34 static int help(void) {
35         _cleanup_free_ char *link = NULL;
36         int r;
37 
38         r = terminal_urlify_man("udevadm", "8", &link);
39         if (r < 0)
40                 return log_oom();
41 
42         printf("%s [OPTIONS...] COMMAND\n"
43                "%s [OPTIONS...] --print\n"
44                "\n%sLock a block device and run a command.%s\n\n"
45                "  -h --help            Print this message\n"
46                "  -V --version         Print version of the program\n"
47                "  -d --device=DEVICE   Block device to lock\n"
48                "  -b --backing=FILE    File whose backing block device to lock\n"
49                "  -t --timeout=SECS    Block at most the specified time waiting for lock\n"
50                "  -p --print           Only show which block device the lock would be taken on\n"
51                "\nSee the %s for details.\n",
52                program_invocation_short_name,
53                program_invocation_short_name,
54                ansi_highlight(),
55                ansi_normal(),
56                link);
57 
58         return 0;
59 }
60 
parse_argv(int argc,char * argv[])61 static int parse_argv(int argc, char *argv[]) {
62 
63         static const struct option options[] = {
64                 { "help",    no_argument,       NULL, 'h'      },
65                 { "version", no_argument,       NULL, 'V'      },
66                 { "device",  required_argument, NULL, 'd'      },
67                 { "backing", required_argument, NULL, 'b'      },
68                 { "timeout", required_argument, NULL, 't'      },
69                 { "print",   no_argument,       NULL, 'p'      },
70                 {}
71         };
72 
73         int c, r;
74 
75         assert(argc >= 0);
76         assert(argv);
77 
78         while ((c = getopt_long(argc, argv, arg_print ? "hVd:b:t:p" : "+hVd:b:t:p", options, NULL)) >= 0)
79 
80                 switch (c) {
81 
82                 case 'h':
83                         return help();
84 
85                 case 'V':
86                         return print_version();
87 
88                 case 'd':
89                 case 'b': {
90                         _cleanup_free_ char *s = NULL;
91                         char ***l = c == 'd' ? &arg_devices : &arg_backing;
92 
93                         r = path_make_absolute_cwd(optarg, &s);
94                         if (r < 0)
95                                 return log_error_errno(r, "Failed to make path '%s' absolute: %m", optarg);
96 
97                         path_simplify(s);
98 
99                         if (strv_consume(l, TAKE_PTR(s)) < 0)
100                                 return log_oom();
101 
102                         strv_uniq(*l);
103                         break;
104                 }
105 
106                 case 't':
107                         r = parse_sec(optarg, &arg_timeout_usec);
108                         if (r < 0)
109                                 return log_error_errno(r, "Failed to parse --timeout= parameter: %s", optarg);
110                         break;
111 
112                 case 'p':
113                         arg_print = true;
114                         break;
115 
116                 case '?':
117                         return -EINVAL;
118 
119                 default:
120                         assert_not_reached();
121                 }
122 
123         if (arg_print) {
124                 if (optind != argc)
125                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No arguments expected");
126         } else {
127                 if (optind + 1 > argc)
128                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too few arguments, command to execute.");
129 
130                 arg_cmdline = strv_copy(argv + optind);
131                 if (!arg_cmdline)
132                         return log_oom();
133         }
134 
135         if (strv_isempty(arg_devices) && strv_isempty(arg_backing))
136                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No devices to lock specified, refusing.");
137 
138         return 1;
139 }
140 
find_devno(dev_t ** devnos,size_t * n_devnos,const char * device,bool backing)141 static int find_devno(
142                 dev_t **devnos,
143                 size_t *n_devnos,
144                 const char *device,
145                 bool backing) {
146 
147         _cleanup_close_ int fd = -1;
148         dev_t devt, whole_devt;
149         struct stat st;
150         int r;
151 
152         assert(devnos);
153         assert(n_devnos);
154         assert(*devnos || *n_devnos == 0);
155         assert(device);
156 
157         fd = open(device, O_CLOEXEC|O_PATH);
158         if (fd < 0)
159                 return log_error_errno(errno, "Failed to open '%s': %m", device);
160 
161         if (fstat(fd, &st) < 0)
162                 return log_error_errno(errno, "Failed to stat '%s': %m", device);
163 
164         if (S_ISBLK(st.st_mode))
165                 devt = st.st_rdev;
166         else if (!backing)
167                 return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Not a block device: %s", device);
168         else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
169                 return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Not a block device, regular file or directory: %s", device);
170         else if (major(st.st_dev) != 0)
171                 devt = st.st_dev;
172         else {
173                 _cleanup_close_ int regfd = -1;
174 
175                 /* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
176                  * handing, to get the backing device node. */
177 
178                 regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
179                 if (regfd < 0)
180                         return log_error_errno(regfd, "Failed to open '%s': %m", device);
181 
182                 r = btrfs_get_block_device_fd(regfd, &devt);
183                 if (r == -ENOTTY)
184                         return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Path '%s' not backed by block device.", device);
185                 if (r < 0)
186                         return log_error_errno(r, "Failed to acquire btrfs backing device of '%s': %m", device);
187         }
188 
189         r = block_get_whole_disk(devt, &whole_devt);
190         if (r < 0)
191                 return log_error_errno(r, "Failed to find whole block device for '%s': %m", device);
192 
193         if (typesafe_bsearch(&whole_devt, *devnos, *n_devnos, devt_compare_func)) {
194                 log_debug("Device %u:%u already listed for locking, ignoring.", major(whole_devt), minor(whole_devt));
195                 return 0;
196         }
197 
198         if (!GREEDY_REALLOC(*devnos, *n_devnos + 1))
199                 return log_oom();
200 
201         (*devnos)[(*n_devnos)++] = whole_devt;
202 
203         /* Immediately sort again, to ensure the binary search above will work for the next device we add */
204         typesafe_qsort(*devnos, *n_devnos, devt_compare_func);
205         return 1;
206 }
207 
lock_device(const char * path,dev_t devno,usec_t deadline)208 static int lock_device(
209                 const char *path,
210                 dev_t devno,
211                 usec_t deadline) {
212 
213         _cleanup_close_ int fd = -1;
214         struct stat st;
215         int r;
216 
217         fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
218         if (fd < 0)
219                 return log_error_errno(errno, "Failed to open '%s': %m", path);
220 
221         if (fstat(fd, &st) < 0)
222                 return log_error_errno(errno, "Failed to stat '%s': %m", path);
223 
224         /* Extra safety: check that the device still refers to what we think it refers to */
225         if (!S_ISBLK(st.st_mode) || st.st_rdev != devno)
226                 return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Path '%s' no longer refers to specified block device %u:%u: %m", path, major(devno), minor(devno));
227 
228         if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
229 
230                 if (errno != EAGAIN)
231                         return log_error_errno(errno, "Failed to lock device '%s': %m", path);
232 
233                 if (deadline == 0)
234                         return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Device '%s' is currently locked.", path);
235 
236                 if (deadline == USEC_INFINITY)  {
237 
238                         log_info("Device '%s' is currently locked, waiting…", path);
239 
240                         if (flock(fd, LOCK_EX) < 0)
241                                 return log_error_errno(errno, "Failed to lock device '%s': %m", path);
242 
243                 } else {
244                         _cleanup_(sigkill_waitp) pid_t flock_pid = 0;
245 
246                         /* flock() doesn't support a time-out. Let's fake one then. The traditional way to do
247                          * this is via alarm()/setitimer()/timer_create(), but that's racy, given that the
248                          * SIGALRM might already fire between the alarm() and the flock() in which case the
249                          * flock() is never cancelled and we lock up (this is a short time window, but with
250                          * short timeouts on a loaded machine we might run into it, who knows?). Let's
251                          * instead do the lock out-of-process: fork off a child that does the locking, and
252                          * that we'll wait on and kill if it takes too long. */
253 
254                         log_info("Device '%s' is currently locked, waiting %s…",
255                                  path, FORMAT_TIMESPAN(usec_sub_unsigned(deadline, now(CLOCK_MONOTONIC)), 0));
256 
257                         BLOCK_SIGNALS(SIGCHLD);
258 
259                         r = safe_fork("(timed-flock)", FORK_DEATHSIG|FORK_LOG, &flock_pid);
260                         if (r < 0)
261                                 return r;
262                         if (r == 0) {
263                                 /* Child */
264 
265                                 if (flock(fd, LOCK_EX) < 0) {
266                                         log_error_errno(errno, "Failed to lock device '%s': %m", path);
267                                         _exit(EXIT_FAILURE);
268                                 }
269 
270                                 _exit(EXIT_SUCCESS);
271                         }
272 
273                         for (;;) {
274                                 siginfo_t si;
275                                 sigset_t ss;
276                                 usec_t n;
277 
278                                 assert(sigemptyset(&ss) >= 0);
279                                 assert(sigaddset(&ss, SIGCHLD) >= 0);
280 
281                                 n = now(CLOCK_MONOTONIC);
282                                 if (n >= deadline)
283                                         return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Timeout reached.");
284 
285                                 r = sigtimedwait(&ss, NULL, TIMESPEC_STORE(deadline - n));
286                                 if (r < 0) {
287                                         if (errno != EAGAIN)
288                                                 return log_error_errno(errno, "Failed to wait for SIGCHLD: %m");
289 
290                                         return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Timeout reached.");
291                                 }
292 
293                                 assert(r == SIGCHLD);
294 
295                                 zero(si);
296 
297                                 if (waitid(P_PID, flock_pid, &si, WEXITED|WNOHANG|WNOWAIT) < 0)
298                                         return log_error_errno(errno, "Failed to wait for child: %m");
299 
300                                 if (si.si_pid != 0) {
301                                         assert(si.si_pid == flock_pid);
302 
303                                         if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
304                                                 return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Unexpected exit status of file lock child.");
305 
306                                         break;
307                                 }
308 
309                                 log_debug("Got SIGCHLD for other child, continuing.");
310                         }
311                 }
312         }
313 
314         log_debug("Successfully locked %s (%u:%u)…", path, major(devno), minor(devno));
315 
316         return TAKE_FD(fd);
317 }
318 
lock_main(int argc,char * argv[],void * userdata)319 int lock_main(int argc, char *argv[], void *userdata) {
320         _cleanup_(fdset_freep) FDSet *fds = NULL;
321         _cleanup_free_ dev_t *devnos = NULL;
322         size_t n_devnos = 0;
323         usec_t deadline;
324         pid_t pid;
325         int r;
326 
327         r = parse_argv(argc, argv);
328         if (r <= 0)
329                 return r;
330 
331         STRV_FOREACH(i, arg_devices) {
332                 r = find_devno(&devnos, &n_devnos, *i, /* backing= */ false);
333                 if (r < 0)
334                         return r;
335         }
336 
337         STRV_FOREACH(i, arg_backing) {
338                 r = find_devno(&devnos, &n_devnos, *i, /* backing= */ true);
339                 if (r < 0)
340                         return r;
341         }
342 
343         assert(n_devnos > 0);
344 
345         fds = fdset_new();
346         if (!fds)
347                 return log_oom();
348 
349         if (IN_SET(arg_timeout_usec, 0, USEC_INFINITY))
350                 deadline = arg_timeout_usec;
351         else
352                 deadline = usec_add(now(CLOCK_MONOTONIC), arg_timeout_usec);
353 
354         for (size_t i = 0; i < n_devnos; i++) {
355                 _cleanup_free_ char *node = NULL;
356 
357                 r = device_path_make_canonical(S_IFBLK, devnos[i], &node);
358                 if (r < 0)
359                         return log_error_errno(r, "Failed to format block device path: %m");
360 
361                 if (arg_print)
362                         printf("%s\n", node);
363                 else {
364                         _cleanup_close_ int fd = -1;
365 
366                         fd = lock_device(node, devnos[i], deadline);
367                         if (fd < 0)
368                                 return fd;
369 
370                         r = fdset_put(fds, fd);
371                         if (r < 0)
372                                 return log_oom();
373 
374                         TAKE_FD(fd);
375                 }
376         }
377 
378         if (arg_print)
379                 return EXIT_SUCCESS;
380 
381         /* Ignore SIGINT and allow the forked process to receive it */
382         (void) ignore_signals(SIGINT);
383 
384         r = safe_fork("(lock)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
385         if (r < 0)
386                 return r;
387         if (r == 0) {
388                 /* Child */
389 
390                 execvp(arg_cmdline[0], arg_cmdline);
391                 log_open();
392                 log_error_errno(errno, "Failed to execute %s: %m", arg_cmdline[0]);
393                 _exit(EXIT_FAILURE);
394         }
395 
396         return wait_for_terminate_and_check(arg_cmdline[0], pid, 0);
397 }
398