]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/mount/mount-tool.c
systemctl: print next timer trigger time with the status verb (#6242)
[thirdparty/systemd.git] / src / mount / mount-tool.c
CommitLineData
450442cf
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2016 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <getopt.h>
21
22#include "libudev.h"
23#include "sd-bus.h"
24
25#include "bus-error.h"
26#include "bus-unit-util.h"
27#include "bus-util.h"
6f6165bf 28#include "dirent-util.h"
450442cf 29#include "escape.h"
9017f5d8 30#include "fd-util.h"
6f6165bf 31#include "fileio.h"
450442cf
LP
32#include "fstab-util.h"
33#include "pager.h"
34#include "parse-util.h"
35#include "path-util.h"
36#include "spawn-polkit-agent.h"
6f6165bf 37#include "stat-util.h"
450442cf
LP
38#include "strv.h"
39#include "udev-util.h"
40#include "unit-name.h"
41#include "terminal-util.h"
42
43enum {
44 ACTION_DEFAULT,
45 ACTION_MOUNT,
46 ACTION_AUTOMOUNT,
c37fb55b 47 ACTION_UMOUNT,
450442cf
LP
48 ACTION_LIST,
49} arg_action = ACTION_DEFAULT;
50
51static bool arg_no_block = false;
52static bool arg_no_pager = false;
53static bool arg_ask_password = true;
54static bool arg_quiet = false;
55static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
56static bool arg_user = false;
57static const char *arg_host = NULL;
58static bool arg_discover = false;
59static char *arg_mount_what = NULL;
60static char *arg_mount_where = NULL;
61static char *arg_mount_type = NULL;
62static char *arg_mount_options = NULL;
63static char *arg_description = NULL;
64static char **arg_property = NULL;
65static usec_t arg_timeout_idle = USEC_INFINITY;
66static bool arg_timeout_idle_set = false;
67static char **arg_automount_property = NULL;
68static int arg_bind_device = -1;
69static bool arg_fsck = true;
70
71static void polkit_agent_open_if_enabled(void) {
72
73 /* Open the polkit agent as a child process if necessary */
74 if (!arg_ask_password)
75 return;
76
77 if (arg_transport != BUS_TRANSPORT_LOCAL)
78 return;
79
80 polkit_agent_open();
81}
82
83static void help(void) {
9017f5d8
YW
84 printf("systemd-mount [OPTIONS...] WHAT [WHERE]\n"
85 "systemd-mount [OPTIONS...] --list\n"
86 "%s [OPTIONS...] %sWHAT|WHERE...\n\n"
450442cf
LP
87 "Establish a mount or auto-mount point transiently.\n\n"
88 " -h --help Show this help\n"
89 " --version Show package version\n"
90 " --no-block Do not wait until operation finished\n"
91 " --no-pager Do not pipe output into a pager\n"
92 " --no-ask-password Do not prompt for password\n"
93 " -q --quiet Suppress information messages during runtime\n"
94 " --user Run as user unit\n"
95 " -H --host=[USER@]HOST Operate on remote host\n"
96 " -M --machine=CONTAINER Operate on local container\n"
97 " --discover Discover mount device metadata\n"
98 " -t --type=TYPE File system type\n"
99 " -o --options=OPTIONS Mount options\n"
100 " --fsck=no Don't run file system check before mount\n"
101 " --description=TEXT Description for unit\n"
102 " -p --property=NAME=VALUE Set mount unit property\n"
103 " -A --automount=BOOL Create an auto-mount point\n"
104 " --timeout-idle-sec=SEC Specify automount idle timeout\n"
105 " --automount-property=NAME=VALUE\n"
106 " Set automount unit property\n"
107 " --bind-device Bind automount unit to device\n"
108 " --list List mountable block devices\n"
9017f5d8
YW
109 " -u --umount Unmount mount points\n",
110 program_invocation_short_name,
111 streq(program_invocation_short_name, "systemd-umount") ? "" : "--umount ");
450442cf
LP
112}
113
114static int parse_argv(int argc, char *argv[]) {
115
116 enum {
117 ARG_VERSION = 0x100,
118 ARG_NO_BLOCK,
119 ARG_NO_PAGER,
120 ARG_NO_ASK_PASSWORD,
121 ARG_USER,
122 ARG_SYSTEM,
123 ARG_DISCOVER,
124 ARG_MOUNT_TYPE,
125 ARG_MOUNT_OPTIONS,
126 ARG_FSCK,
127 ARG_DESCRIPTION,
128 ARG_TIMEOUT_IDLE,
129 ARG_AUTOMOUNT,
130 ARG_AUTOMOUNT_PROPERTY,
131 ARG_BIND_DEVICE,
132 ARG_LIST,
133 };
134
135 static const struct option options[] = {
136 { "help", no_argument, NULL, 'h' },
137 { "version", no_argument, NULL, ARG_VERSION },
138 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
139 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
140 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
141 { "quiet", no_argument, NULL, 'q' },
142 { "user", no_argument, NULL, ARG_USER },
143 { "system", no_argument, NULL, ARG_SYSTEM },
144 { "host", required_argument, NULL, 'H' },
145 { "machine", required_argument, NULL, 'M' },
146 { "discover", no_argument, NULL, ARG_DISCOVER },
147 { "type", required_argument, NULL, 't' },
148 { "options", required_argument, NULL, 'o' },
0f923832 149 { "fsck", required_argument, NULL, ARG_FSCK },
450442cf
LP
150 { "description", required_argument, NULL, ARG_DESCRIPTION },
151 { "property", required_argument, NULL, 'p' },
152 { "automount", required_argument, NULL, ARG_AUTOMOUNT },
153 { "timeout-idle-sec", required_argument, NULL, ARG_TIMEOUT_IDLE },
154 { "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY },
155 { "bind-device", no_argument, NULL, ARG_BIND_DEVICE },
156 { "list", no_argument, NULL, ARG_LIST },
c37fb55b
LR
157 { "umount", no_argument, NULL, 'u' },
158 { "unmount", no_argument, NULL, 'u' },
450442cf
LP
159 {},
160 };
161
162 int r, c;
163
164 assert(argc >= 0);
165 assert(argv);
166
c37fb55b
LR
167 if (strstr(program_invocation_short_name, "systemd-umount"))
168 arg_action = ACTION_UMOUNT;
169
170 while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:Au", options, NULL)) >= 0)
450442cf
LP
171
172 switch (c) {
173
174 case 'h':
175 help();
176 return 0;
177
178 case ARG_VERSION:
179 return version();
180
181 case ARG_NO_BLOCK:
182 arg_no_block = true;
183 break;
184
185 case ARG_NO_PAGER:
186 arg_no_pager = true;
187 break;
188
189 case ARG_NO_ASK_PASSWORD:
190 arg_ask_password = false;
191 break;
192
193 case 'q':
194 arg_quiet = true;
195 break;
196
197 case ARG_USER:
198 arg_user = true;
199 break;
200
201 case ARG_SYSTEM:
202 arg_user = false;
203 break;
204
205 case 'H':
206 arg_transport = BUS_TRANSPORT_REMOTE;
207 arg_host = optarg;
208 break;
209
210 case 'M':
211 arg_transport = BUS_TRANSPORT_MACHINE;
212 arg_host = optarg;
213 break;
214
215 case ARG_DISCOVER:
216 arg_discover = true;
217 break;
218
219 case 't':
220 if (free_and_strdup(&arg_mount_type, optarg) < 0)
221 return log_oom();
222 break;
223
224 case 'o':
225 if (free_and_strdup(&arg_mount_options, optarg) < 0)
226 return log_oom();
227 break;
228
229 case ARG_FSCK:
230 r = parse_boolean(optarg);
231 if (r < 0)
232 return log_error_errno(r, "Failed to parse --fsck= argument: %s", optarg);
233
234 arg_fsck = r;
235 break;
236
237 case ARG_DESCRIPTION:
238 if (free_and_strdup(&arg_description, optarg) < 0)
239 return log_oom();
240 break;
241
242 case 'p':
243 if (strv_extend(&arg_property, optarg) < 0)
244 return log_oom();
245
246 break;
247
248 case 'A':
249 arg_action = ACTION_AUTOMOUNT;
250 break;
251
252 case ARG_AUTOMOUNT:
253 r = parse_boolean(optarg);
254 if (r < 0)
255 return log_error_errno(r, "--automount= expects a valid boolean parameter: %s", optarg);
256
257 arg_action = r ? ACTION_AUTOMOUNT : ACTION_MOUNT;
258 break;
259
260 case ARG_TIMEOUT_IDLE:
261 r = parse_sec(optarg, &arg_timeout_idle);
262 if (r < 0)
263 return log_error_errno(r, "Failed to parse timeout: %s", optarg);
264
265 break;
266
267 case ARG_AUTOMOUNT_PROPERTY:
268 if (strv_extend(&arg_automount_property, optarg) < 0)
269 return log_oom();
270
271 break;
272
273 case ARG_BIND_DEVICE:
274 arg_bind_device = true;
275 break;
276
277 case ARG_LIST:
278 arg_action = ACTION_LIST;
279 break;
280
c37fb55b
LR
281 case 'u':
282 arg_action = ACTION_UMOUNT;
283 break;
284
450442cf
LP
285 case '?':
286 return -EINVAL;
287
288 default:
289 assert_not_reached("Unhandled option");
290 }
291
292 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
293 log_error("Execution in user context is not supported on non-local systems.");
294 return -EINVAL;
295 }
296
297 if (arg_action == ACTION_LIST) {
298 if (optind < argc) {
299 log_error("Too many arguments.");
300 return -EINVAL;
301 }
302
303 if (arg_transport != BUS_TRANSPORT_LOCAL) {
304 log_error("Listing devices only supported locally.");
305 return -EOPNOTSUPP;
306 }
9017f5d8
YW
307 } else if (arg_action == ACTION_UMOUNT) {
308 if (optind >= argc) {
309 log_error("At least one argument required.");
310 return -EINVAL;
311 }
312
313 if (arg_transport != BUS_TRANSPORT_LOCAL) {
314 log_error("Unmounting devices only supported locally.");
315 return -EOPNOTSUPP;
316 }
450442cf
LP
317 } else {
318 if (optind >= argc) {
319 log_error("At least one argument required.");
320 return -EINVAL;
321 }
322
323 if (argc > optind+2) {
324 log_error("At most two arguments required.");
325 return -EINVAL;
326 }
327
328 arg_mount_what = fstab_node_to_udev_node(argv[optind]);
329 if (!arg_mount_what)
330 return log_oom();
331
332 if (argc > optind+1) {
333 r = path_make_absolute_cwd(argv[optind+1], &arg_mount_where);
334 if (r < 0)
335 return log_error_errno(r, "Failed to make path absolute: %m");
336 } else
337 arg_discover = true;
338
339 if (arg_discover && arg_transport != BUS_TRANSPORT_LOCAL) {
340 log_error("Automatic mount location discovery is only supported locally.");
341 return -EOPNOTSUPP;
342 }
343 }
344
345 return 1;
346}
347
348static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
349 int r;
350
05b4d3b5
AK
351 if (!isempty(arg_description)) {
352 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
353 if (r < 0)
354 return r;
355 }
450442cf
LP
356
357 if (arg_bind_device && is_device_path(arg_mount_what)) {
358 _cleanup_free_ char *device_unit = NULL;
359
360 r = unit_name_from_path(arg_mount_what, ".device", &device_unit);
361 if (r < 0)
362 return r;
363
364 r = sd_bus_message_append(m, "(sv)(sv)",
365 "After", "as", 1, device_unit,
366 "BindsTo", "as", 1, device_unit);
367 if (r < 0)
368 return r;
369 }
370
371 r = bus_append_unit_property_assignment_many(m, properties);
372 if (r < 0)
373 return r;
374
375 return 0;
376}
377
378static int transient_mount_set_properties(sd_bus_message *m) {
379 int r;
380
381 assert(m);
382
383 r = transient_unit_set_properties(m, arg_property);
384 if (r < 0)
385 return r;
386
387 if (arg_mount_what) {
388 r = sd_bus_message_append(m, "(sv)", "What", "s", arg_mount_what);
389 if (r < 0)
390 return r;
391 }
392
393 if (arg_mount_type) {
394 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_mount_type);
395 if (r < 0)
396 return r;
397 }
398
399 if (arg_mount_options) {
400 r = sd_bus_message_append(m, "(sv)", "Options", "s", arg_mount_options);
401 if (r < 0)
402 return r;
403 }
404
405 if (arg_fsck) {
406 _cleanup_free_ char *fsck = NULL;
407
408 r = unit_name_from_path_instance("systemd-fsck", arg_mount_what, ".service", &fsck);
409 if (r < 0)
410 return r;
411
412 r = sd_bus_message_append(m,
413 "(sv)(sv)",
414 "Requires", "as", 1, fsck,
415 "After", "as", 1, fsck);
416 if (r < 0)
417 return r;
418 }
419
420 return 0;
421}
422
423static int transient_automount_set_properties(sd_bus_message *m) {
424 int r;
425
426 assert(m);
427
428 r = transient_unit_set_properties(m, arg_automount_property);
429 if (r < 0)
430 return r;
431
432 if (arg_timeout_idle != USEC_INFINITY) {
433 r = sd_bus_message_append(m, "(sv)", "TimeoutIdleUSec", "t", arg_timeout_idle);
434 if (r < 0)
435 return r;
436 }
437
438 return 0;
439}
440
441static int start_transient_mount(
442 sd_bus *bus,
443 char **argv) {
444
445 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
446 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
447 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
448 _cleanup_free_ char *mount_unit = NULL;
449 int r;
450
451 if (!arg_no_block) {
452 r = bus_wait_for_jobs_new(bus, &w);
453 if (r < 0)
454 return log_error_errno(r, "Could not watch jobs: %m");
455 }
456
457 r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
458 if (r < 0)
459 return log_error_errno(r, "Failed to make mount unit name: %m");
460
461 r = sd_bus_message_new_method_call(
462 bus,
463 &m,
464 "org.freedesktop.systemd1",
465 "/org/freedesktop/systemd1",
466 "org.freedesktop.systemd1.Manager",
467 "StartTransientUnit");
468 if (r < 0)
469 return bus_log_create_error(r);
470
471 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
472 if (r < 0)
473 return bus_log_create_error(r);
474
475 /* Name and mode */
476 r = sd_bus_message_append(m, "ss", mount_unit, "fail");
477 if (r < 0)
478 return bus_log_create_error(r);
479
480 /* Properties */
481 r = sd_bus_message_open_container(m, 'a', "(sv)");
482 if (r < 0)
483 return bus_log_create_error(r);
484
485 r = transient_mount_set_properties(m);
486 if (r < 0)
487 return bus_log_create_error(r);
488
489 r = sd_bus_message_close_container(m);
490 if (r < 0)
491 return bus_log_create_error(r);
492
493 /* Auxiliary units */
494 r = sd_bus_message_append(m, "a(sa(sv))", 0);
495 if (r < 0)
496 return bus_log_create_error(r);
497
498 polkit_agent_open_if_enabled();
499
500 r = sd_bus_call(bus, m, 0, &error, &reply);
501 if (r < 0)
502 return log_error_errno(r, "Failed to start transient mount unit: %s", bus_error_message(&error, r));
503
504 if (w) {
505 const char *object;
506
507 r = sd_bus_message_read(reply, "o", &object);
508 if (r < 0)
509 return bus_log_parse_error(r);
510
511 r = bus_wait_for_jobs_one(w, object, arg_quiet);
512 if (r < 0)
513 return r;
514 }
515
516 if (!arg_quiet)
517 log_info("Started unit %s%s%s for mount point: %s%s%s",
518 ansi_highlight(), mount_unit, ansi_normal(),
519 ansi_highlight(), arg_mount_where, ansi_normal());
520
521 return 0;
522}
523
524static int start_transient_automount(
525 sd_bus *bus,
526 char **argv) {
527
528 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
529 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
530 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
531 _cleanup_free_ char *automount_unit = NULL, *mount_unit = NULL;
532 int r;
533
534 if (!arg_no_block) {
535 r = bus_wait_for_jobs_new(bus, &w);
536 if (r < 0)
537 return log_error_errno(r, "Could not watch jobs: %m");
538 }
539
540 r = unit_name_from_path(arg_mount_where, ".automount", &automount_unit);
541 if (r < 0)
542 return log_error_errno(r, "Failed to make automount unit name: %m");
543
544 r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
545 if (r < 0)
546 return log_error_errno(r, "Failed to make mount unit name: %m");
547
548 r = sd_bus_message_new_method_call(
549 bus,
550 &m,
551 "org.freedesktop.systemd1",
552 "/org/freedesktop/systemd1",
553 "org.freedesktop.systemd1.Manager",
554 "StartTransientUnit");
555 if (r < 0)
556 return bus_log_create_error(r);
557
558 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
559 if (r < 0)
560 return bus_log_create_error(r);
561
562 /* Name and mode */
563 r = sd_bus_message_append(m, "ss", automount_unit, "fail");
564 if (r < 0)
565 return bus_log_create_error(r);
566
567 /* Properties */
568 r = sd_bus_message_open_container(m, 'a', "(sv)");
569 if (r < 0)
570 return bus_log_create_error(r);
571
572 r = transient_automount_set_properties(m);
573 if (r < 0)
574 return bus_log_create_error(r);
575
576 r = sd_bus_message_close_container(m);
577 if (r < 0)
578 return bus_log_create_error(r);
579
580 /* Auxiliary units */
581 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
582 if (r < 0)
583 return bus_log_create_error(r);
584
585 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
586 if (r < 0)
587 return bus_log_create_error(r);
588
589 r = sd_bus_message_append(m, "s", mount_unit);
590 if (r < 0)
591 return bus_log_create_error(r);
592
593 r = sd_bus_message_open_container(m, 'a', "(sv)");
594 if (r < 0)
595 return bus_log_create_error(r);
596
597 r = transient_mount_set_properties(m);
598 if (r < 0)
599 return bus_log_create_error(r);
600
601 r = sd_bus_message_close_container(m);
602 if (r < 0)
603 return bus_log_create_error(r);
604
605 r = sd_bus_message_close_container(m);
606 if (r < 0)
607 return bus_log_create_error(r);
608
609 r = sd_bus_message_close_container(m);
610 if (r < 0)
611 return bus_log_create_error(r);
612
613 polkit_agent_open_if_enabled();
614
615 r = sd_bus_call(bus, m, 0, &error, &reply);
616 if (r < 0)
617 return log_error_errno(r, "Failed to start transient automount unit: %s", bus_error_message(&error, r));
618
619 if (w) {
620 const char *object;
621
622 r = sd_bus_message_read(reply, "o", &object);
623 if (r < 0)
624 return bus_log_parse_error(r);
625
626 r = bus_wait_for_jobs_one(w, object, arg_quiet);
627 if (r < 0)
628 return r;
629 }
630
631 if (!arg_quiet)
632 log_info("Started unit %s%s%s for mount point: %s%s%s",
633 ansi_highlight(), automount_unit, ansi_normal(),
634 ansi_highlight(), arg_mount_where, ansi_normal());
635
636 return 0;
637}
638
c37fb55b
LR
639static int stop_mount(
640 sd_bus *bus,
9017f5d8 641 const char *where,
6f6165bf 642 const char *suffix) {
c37fb55b
LR
643
644 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
645 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
646 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
647 _cleanup_free_ char *mount_unit = NULL;
648 int r;
649
650 if (!arg_no_block) {
651 r = bus_wait_for_jobs_new(bus, &w);
652 if (r < 0)
653 return log_error_errno(r, "Could not watch jobs: %m");
654 }
655
9017f5d8 656 r = unit_name_from_path(where, suffix, &mount_unit);
c37fb55b 657 if (r < 0)
9017f5d8 658 return log_error_errno(r, "Failed to make mount unit name from path %s: %m", where);
c37fb55b
LR
659
660 r = sd_bus_message_new_method_call(
661 bus,
662 &m,
663 "org.freedesktop.systemd1",
664 "/org/freedesktop/systemd1",
665 "org.freedesktop.systemd1.Manager",
666 "StopUnit");
667 if (r < 0)
668 return bus_log_create_error(r);
669
670 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
671 if (r < 0)
672 return bus_log_create_error(r);
673
674 /* Name and mode */
675 r = sd_bus_message_append(m, "ss", mount_unit, "fail");
676 if (r < 0)
677 return bus_log_create_error(r);
678
679 polkit_agent_open_if_enabled();
680
681 r = sd_bus_call(bus, m, 0, &error, &reply);
6f6165bf
YW
682 if (r < 0)
683 return log_error_errno(r, "Failed to stop mount unit: %s", bus_error_message(&error, r));
c37fb55b
LR
684
685 if (w) {
686 const char *object;
687
688 r = sd_bus_message_read(reply, "o", &object);
689 if (r < 0)
690 return bus_log_parse_error(r);
691
692 r = bus_wait_for_jobs_one(w, object, arg_quiet);
693 if (r < 0)
694 return r;
695 }
696
697 if (!arg_quiet)
698 log_info("Stopped unit %s%s%s for mount point: %s%s%s",
699 ansi_highlight(), mount_unit, ansi_normal(),
9017f5d8 700 ansi_highlight(), where, ansi_normal());
c37fb55b
LR
701
702 return 0;
703}
704
705static int stop_mounts(
706 sd_bus *bus,
6f6165bf 707 const char *where) {
c37fb55b
LR
708
709 int r;
710
9017f5d8
YW
711 if (path_equal(where, "/")) {
712 log_error("Refusing to operate on root directory: %s", where);
713 return -EINVAL;
714 }
715
716 if (!path_is_safe(where)) {
717 log_error("Path contains unsafe components: %s", where);
718 return -EINVAL;
719 }
720
6f6165bf 721 r = stop_mount(bus, where, ".mount");
c37fb55b
LR
722 if (r < 0)
723 return r;
724
6f6165bf 725 r = stop_mount(bus, where, ".automount");
c37fb55b
LR
726 if (r < 0)
727 return r;
728
729 return 0;
730}
731
9017f5d8
YW
732static int umount_by_mountinfo(sd_bus *bus, const char *what) {
733 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
734 int r, r2 = 0;
735
736 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
737 if (!proc_self_mountinfo)
738 return -errno;
739
740 for (;;) {
741 _cleanup_free_ char *path = NULL, *where = NULL, *dev = NULL;
742 int k;
743
744 k = fscanf(proc_self_mountinfo,
745 "%*s " /* (1) mount id */
746 "%*s " /* (2) parent id */
747 "%*s " /* (3) major:minor */
748 "%*s " /* (4) root */
749 "%ms " /* (5) mount point */
750 "%*s" /* (6) mount options */
751 "%*[^-]" /* (7) optional fields */
752 "- " /* (8) separator */
753 "%*s " /* (9) file system type */
754 "%ms" /* (10) mount source */
755 "%*s" /* (11) mount options 2 */
756 "%*[^\n]", /* some rubbish at the end */
757 &path, &dev);
758 if (k != 2) {
759 if (k == EOF)
760 break;
761
762 continue;
763 }
764
765 if (!streq(what, dev))
766 continue;
767
768 r = cunescape(path, UNESCAPE_RELAX, &where);
769 if (r < 0) {
6f6165bf 770 r2 = r;
9017f5d8
YW
771 continue;
772 }
773
6f6165bf 774 r = stop_mounts(bus, where);
9017f5d8 775 if (r < 0) {
6f6165bf 776 r2 = r;
9017f5d8
YW
777 continue;
778 }
779 }
780
781 return r2;
782}
783
784static int umount_by_device(sd_bus *bus, const char *what) {
785 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
786 _cleanup_udev_unref_ struct udev *udev = NULL;
787 _cleanup_free_ char *where = NULL;
788 struct stat st;
789 const char *v;
6f6165bf 790 int r, r2 = 0;
9017f5d8
YW
791
792 assert(what);
793
794 if (stat(what, &st) < 0)
795 return log_error_errno(errno, "Can't stat %s: %m", what);
796
797 if (!S_ISBLK(st.st_mode)) {
6f6165bf 798 log_error("Not a block device: %s", what);
9017f5d8
YW
799 return -ENOTBLK;
800 }
801
802 udev = udev_new();
803 if (!udev)
804 return log_oom();
805
806 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
807 if (!d)
808 return log_oom();
809
810 v = udev_device_get_property_value(d, "ID_FS_USAGE");
811 if (!streq_ptr(v, "filesystem")) {
812 log_error("%s does not contain a known file system.", what);
813 return -EINVAL;
814 }
815
816 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
817 if (!isempty(v)) {
818 where = strdup(v);
819 if (!where)
820 return log_oom();
6f6165bf 821 r2 = stop_mounts(bus, where);
9017f5d8
YW
822 }
823
824 v = udev_device_get_devnode(d);
825 if (!isempty(v)) {
6f6165bf
YW
826 r = umount_by_mountinfo(bus, v);
827 if (r < 0)
828 return r;
9017f5d8
YW
829 }
830
6f6165bf
YW
831 return r2;
832}
833
834static int umount_loop(sd_bus *bus, const char *backing_file) {
835 _cleanup_closedir_ DIR *d = NULL;
836 struct dirent *de;
837 int r, r2 = 0;
838 bool found = false;
839
840 assert(backing_file);
841
842 d = opendir("/sys/devices/virtual/block");
843 if (!d) {
844 if (errno == ENOENT)
845 return log_error_errno(errno, "File %s is not mounted.", backing_file);
846 return log_error_errno(errno, "Can't open directory /sys/devices/virtual/block: %m");
847 }
848
849 FOREACH_DIRENT(de, d, return -errno) {
850 _cleanup_free_ char *sys = NULL, *what = NULL, *fname = NULL;
851
852 dirent_ensure_type(d, de);
853
854 if (de->d_type != DT_DIR)
855 continue;
856
857 if (!startswith(de->d_name, "loop"))
858 continue;
859
860 sys = strjoin("/sys/devices/virtual/block/", de->d_name, "/loop/backing_file");
861 if (!sys)
862 return log_oom();
863
864 r = read_one_line_file(sys, &fname);
865 if (r < 0) {
866 if (r != -ENOENT)
867 r2 = log_error_errno(r, "Can't read %s: %m", sys);
868 continue;
869 }
870
871 if (!files_same(fname, backing_file, 0))
872 continue;
873
874 found = true;
875
876 what = strjoin("/dev/", de->d_name);
877 if (!what)
878 return log_oom();
879
880 r = umount_by_device(bus, what);
881 if (r < 0)
882 r2 = r;
883 }
884
885 if (!found)
886 return log_error_errno(ENOENT, "File %s is not mounted.", backing_file);
887
888 return r2;
9017f5d8
YW
889}
890
891static int action_umount(
892 sd_bus *bus,
893 int argc,
894 char **argv) {
895
896 int i, r, r2 = 0;
897
898 for (i = optind; i < argc; i++) {
6f6165bf
YW
899 _cleanup_free_ char *u = NULL, *p = NULL;
900 struct stat st;
9017f5d8
YW
901
902 u = fstab_node_to_udev_node(argv[i]);
903 if (!u)
904 return log_oom();
9017f5d8 905
6f6165bf 906 r = path_make_absolute_cwd(u, &p);
9017f5d8 907 if (r < 0) {
6f6165bf 908 r2 = log_error_errno(r, "Failed to make path absolute: %m");
9017f5d8
YW
909 continue;
910 }
911
6f6165bf 912 path_kill_slashes(p);
9017f5d8 913
6f6165bf
YW
914 if (stat(p, &st) < 0)
915 return log_error_errno(errno, "Can't stat %s: %m", p);
9017f5d8 916
6f6165bf
YW
917 if (S_ISBLK(st.st_mode))
918 r = umount_by_device(bus, p);
919 else if (S_ISREG(st.st_mode))
920 r = umount_loop(bus, p);
921 else if (S_ISDIR(st.st_mode))
922 r = stop_mounts(bus, p);
923 else {
924 log_error("Invalid file type: %s", p);
925 r = -EINVAL;
926 }
927
928 if (r < 0)
9017f5d8
YW
929 r2 = r;
930 }
931
932 return r2;
933}
934
450442cf
LP
935static int acquire_mount_type(struct udev_device *d) {
936 const char *v;
937
938 assert(d);
939
940 if (arg_mount_type)
941 return 0;
942
943 v = udev_device_get_property_value(d, "ID_FS_TYPE");
944 if (isempty(v))
945 return 0;
946
947 arg_mount_type = strdup(v);
948 if (!arg_mount_type)
949 return log_oom();
950
951 log_debug("Discovered type=%s", arg_mount_type);
952 return 1;
953}
954
955static int acquire_mount_options(struct udev_device *d) {
956 const char *v;
957
958 if (arg_mount_options)
959 return 0;
960
961 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS");
962 if (isempty(v))
963 return 0;
964
965 arg_mount_options = strdup(v);
966 if (!arg_mount_options)
967 return log_oom();
968
969 log_debug("Discovered options=%s", arg_mount_options);
970 return 1;
971}
972
973static const char *get_model(struct udev_device *d) {
974 const char *model;
975
976 assert(d);
977
978 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
979 if (model)
980 return model;
981
982 return udev_device_get_property_value(d, "ID_MODEL");
983}
984
985static const char* get_label(struct udev_device *d) {
986 const char *label;
987
988 assert(d);
989
990 label = udev_device_get_property_value(d, "ID_FS_LABEL");
991 if (label)
992 return label;
993
994 return udev_device_get_property_value(d, "ID_PART_ENTRY_NAME");
995}
996
997static int acquire_mount_where(struct udev_device *d) {
998 const char *v;
999
1000 if (arg_mount_where)
1001 return 0;
1002
1003 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
1004 if (isempty(v)) {
1005 _cleanup_free_ char *escaped = NULL;
1006 const char *name;
1007
1008 name = get_label(d);
1009 if (!name)
1010 name = get_model(d);
1011 if (!name) {
1012 const char *dn;
1013
1014 dn = udev_device_get_devnode(d);
1015 if (!dn)
1016 return 0;
1017
1018 name = basename(dn);
1019 }
1020
1021 escaped = xescape(name, "\\");
1022 if (!filename_is_valid(escaped))
1023 return 0;
1024
605405c6 1025 arg_mount_where = strjoin("/run/media/system/", escaped);
450442cf
LP
1026 } else
1027 arg_mount_where = strdup(v);
1028
1029 if (!arg_mount_where)
1030 return log_oom();
1031
1032 log_debug("Discovered where=%s", arg_mount_where);
1033 return 1;
1034}
1035
1036static int acquire_description(struct udev_device *d) {
1037 const char *model, *label;
1038
1039 if (arg_description)
1040 return 0;
1041
1042 model = get_model(d);
1043
1044 label = get_label(d);
1045 if (!label)
1046 label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER");
1047
1048 if (model && label)
605405c6 1049 arg_description = strjoin(model, " ", label);
450442cf
LP
1050 else if (label)
1051 arg_description = strdup(label);
1052 else if (model)
1053 arg_description = strdup(model);
1054 else
05b4d3b5 1055 return 0;
450442cf
LP
1056
1057 if (!arg_description)
1058 return log_oom();
1059
1060 log_debug("Discovered description=%s", arg_description);
1061 return 1;
1062}
1063
1064static int acquire_removable(struct udev_device *d) {
1065 const char *v;
1066
1067 /* Shortcut this if there's no reason to check it */
1068 if (arg_action != ACTION_DEFAULT && arg_timeout_idle_set && arg_bind_device >= 0)
1069 return 0;
1070
1071 for (;;) {
1072 v = udev_device_get_sysattr_value(d, "removable");
1073 if (v)
1074 break;
1075
1076 d = udev_device_get_parent(d);
1077 if (!d)
1078 return 0;
1079
1080 if (!streq_ptr(udev_device_get_subsystem(d), "block"))
1081 return 0;
1082 }
1083
1084 if (parse_boolean(v) <= 0)
1085 return 0;
1086
1087 log_debug("Discovered removable device.");
1088
1089 if (arg_action == ACTION_DEFAULT) {
1090 log_debug("Automatically turning on automount.");
1091 arg_action = ACTION_AUTOMOUNT;
1092 }
1093
1094 if (!arg_timeout_idle_set) {
1095 log_debug("Setting idle timeout to 1s.");
1096 arg_timeout_idle = USEC_PER_SEC;
1097 }
1098
1099 if (arg_bind_device < 0) {
1100 log_debug("Binding automount unit to device.");
1101 arg_bind_device = true;
1102 }
1103
1104 return 1;
1105}
1106
1107static int discover_device(void) {
1108 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
1109 _cleanup_udev_unref_ struct udev *udev = NULL;
1110 struct stat st;
1111 const char *v;
1112 int r;
1113
1114 if (!arg_discover)
1115 return 0;
1116
1117 if (!is_device_path(arg_mount_what)) {
1118 log_error("Discovery only supported for block devices, don't know what to do.");
1119 return -EINVAL;
1120 }
1121
1122 if (stat(arg_mount_what, &st) < 0)
1123 return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what);
1124
1125 if (!S_ISBLK(st.st_mode)) {
1126 log_error("Path %s is not a block device, don't know what to do.", arg_mount_what);
1127 return -ENOTBLK;
1128 }
1129
1130 udev = udev_new();
1131 if (!udev)
1132 return log_oom();
1133
1134 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
1135 if (!d)
1136 return log_oom();
1137
1138 v = udev_device_get_property_value(d, "ID_FS_USAGE");
1139 if (!streq_ptr(v, "filesystem")) {
9017f5d8 1140 log_error("%s does not contain a known file system.", arg_mount_what);
450442cf
LP
1141 return -EINVAL;
1142 }
1143
1144 r = acquire_mount_type(d);
1145 if (r < 0)
1146 return r;
1147
1148 r = acquire_mount_options(d);
1149 if (r < 0)
1150 return r;
1151
1152 r = acquire_mount_where(d);
1153 if (r < 0)
1154 return r;
1155
1156 r = acquire_description(d);
1157 if (r < 0)
1158 return r;
1159
1160 r = acquire_removable(d);
1161 if (r < 0)
1162 return r;
1163
1164 return 0;
1165}
1166
1167enum {
1168 COLUMN_NODE,
1169 COLUMN_PATH,
1170 COLUMN_MODEL,
1171 COLUMN_WWN,
1172 COLUMN_FSTYPE,
1173 COLUMN_LABEL,
1174 COLUMN_UUID,
1175 _COLUMN_MAX,
1176};
1177
1178struct item {
1179 char* columns[_COLUMN_MAX];
1180};
1181
1182static int compare_item(const void *a, const void *b) {
1183 const struct item *x = a, *y = b;
1184
1185 if (x->columns[COLUMN_NODE] == y->columns[COLUMN_NODE])
1186 return 0;
1187 if (!x->columns[COLUMN_NODE])
1188 return 1;
1189 if (!y->columns[COLUMN_NODE])
1190 return -1;
1191
1192 return path_compare(x->columns[COLUMN_NODE], y->columns[COLUMN_NODE]);
1193}
1194
1195static int list_devices(void) {
1196
1197 static const char * const titles[_COLUMN_MAX] = {
1198 [COLUMN_NODE] = "NODE",
1199 [COLUMN_PATH] = "PATH",
1200 [COLUMN_MODEL] = "MODEL",
1201 [COLUMN_WWN] = "WWN",
1202 [COLUMN_FSTYPE] = "TYPE",
1203 [COLUMN_LABEL] = "LABEL",
1204 [COLUMN_UUID] = "UUID"
1205 };
1206
1207 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
1208 _cleanup_udev_unref_ struct udev *udev = NULL;
1209 struct udev_list_entry *item = NULL, *first = NULL;
1210 size_t n_allocated = 0, n = 0, i;
1211 size_t column_width[_COLUMN_MAX];
1212 struct item *items = NULL;
1213 unsigned c;
1214 int r;
1215
1216 for (c = 0; c < _COLUMN_MAX; c++)
1217 column_width[c] = strlen(titles[c]);
1218
1219 udev = udev_new();
1220 if (!udev)
1221 return log_oom();
1222
1223 e = udev_enumerate_new(udev);
1224 if (!e)
1225 return log_oom();
1226
1227 r = udev_enumerate_add_match_subsystem(e, "block");
1228 if (r < 0)
1229 return log_error_errno(r, "Failed to add block match: %m");
1230
1231 r = udev_enumerate_add_match_property(e, "ID_FS_USAGE", "filesystem");
1232 if (r < 0)
1233 return log_error_errno(r, "Failed to add property match: %m");
1234
1235 r = udev_enumerate_scan_devices(e);
1236 if (r < 0)
1237 return log_error_errno(r, "Failed to scan devices: %m");
1238
1239 first = udev_enumerate_get_list_entry(e);
1240 udev_list_entry_foreach(item, first) {
1241 _cleanup_udev_device_unref_ struct udev_device *d;
1242 struct item *j;
1243
1244 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
1245 if (!d) {
1246 r = log_oom();
1247 goto finish;
1248 }
1249
1250 if (!GREEDY_REALLOC0(items, n_allocated, n+1)) {
1251 r = log_oom();
1252 goto finish;
1253 }
1254
1255 j = items + n++;
1256
1257 for (c = 0; c < _COLUMN_MAX; c++) {
6fd667e5 1258 const char *x = NULL;
450442cf
LP
1259 size_t k;
1260
1261 switch (c) {
1262
1263 case COLUMN_NODE:
1264 x = udev_device_get_devnode(d);
1265 break;
1266
1267 case COLUMN_PATH:
1268 x = udev_device_get_property_value(d, "ID_PATH");
1269 break;
1270
1271 case COLUMN_MODEL:
1272 x = get_model(d);
1273 break;
1274
1275 case COLUMN_WWN:
1276 x = udev_device_get_property_value(d, "ID_WWN");
1277 break;
1278
1279 case COLUMN_FSTYPE:
1280 x = udev_device_get_property_value(d, "ID_FS_TYPE");
1281 break;
1282
1283 case COLUMN_LABEL:
1284 x = get_label(d);
1285 break;
1286
1287 case COLUMN_UUID:
1288 x = udev_device_get_property_value(d, "ID_FS_UUID");
1289 break;
1290 }
1291
1292 if (isempty(x))
1293 continue;
1294
1295 j->columns[c] = strdup(x);
1296 if (!j->columns[c]) {
1297 r = log_oom();
1298 goto finish;
1299 }
1300
1301 k = strlen(x);
1302 if (k > column_width[c])
1303 column_width[c] = k;
1304 }
1305 }
1306
1307 if (n == 0) {
1308 log_info("No devices found.");
1309 goto finish;
1310 }
1311
1312 qsort_safe(items, n, sizeof(struct item), compare_item);
1313
1314 pager_open(arg_no_pager, false);
1315
1316 fputs(ansi_underline(), stdout);
1317 for (c = 0; c < _COLUMN_MAX; c++) {
1318 if (c > 0)
1319 fputc(' ', stdout);
1320
1321 printf("%-*s", (int) column_width[c], titles[c]);
1322 }
1323 fputs(ansi_normal(), stdout);
1324 fputc('\n', stdout);
1325
1326 for (i = 0; i < n; i++) {
1327 for (c = 0; c < _COLUMN_MAX; c++) {
1328 if (c > 0)
1329 fputc(' ', stdout);
1330
1331 printf("%-*s", (int) column_width[c], strna(items[i].columns[c]));
1332 }
1333 fputc('\n', stdout);
1334 }
1335
1336 r = 0;
1337
1338finish:
1339 for (i = 0; i < n; i++)
1340 for (c = 0; c < _COLUMN_MAX; c++)
1341 free(items[i].columns[c]);
1342
1343 free(items);
1344 return r;
1345}
1346
1347int main(int argc, char* argv[]) {
1348 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1349 int r;
1350
1351 log_parse_environment();
1352 log_open();
1353
1354 r = parse_argv(argc, argv);
1355 if (r <= 0)
1356 goto finish;
1357
1358 if (arg_action == ACTION_LIST) {
1359 r = list_devices();
1360 goto finish;
1361 }
1362
9017f5d8
YW
1363 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1364 if (r < 0) {
1365 log_error_errno(r, "Failed to create bus connection: %m");
1366 goto finish;
1367 }
1368
1369 if (arg_action == ACTION_UMOUNT) {
1370 r = action_umount(bus, argc, argv);
1371 goto finish;
1372 }
1373
450442cf
LP
1374 r = discover_device();
1375 if (r < 0)
1376 goto finish;
1377 if (!arg_mount_where) {
1378 log_error("Can't figure out where to mount %s.", arg_mount_what);
1379 r = -EINVAL;
1380 goto finish;
1381 }
1382
1383 path_kill_slashes(arg_mount_where);
1384
1385 if (path_equal(arg_mount_where, "/")) {
1386 log_error("Refusing to operate on root directory.");
1387 r = -EINVAL;
1388 goto finish;
1389 }
1390
1391 if (!path_is_safe(arg_mount_where)) {
9017f5d8 1392 log_error("Path contains unsafe components.");
450442cf
LP
1393 r = -EINVAL;
1394 goto finish;
1395 }
1396
1397 if (streq_ptr(arg_mount_type, "auto"))
1398 arg_mount_type = mfree(arg_mount_type);
1399 if (streq_ptr(arg_mount_options, "defaults"))
1400 arg_mount_options = mfree(arg_mount_options);
1401
1402 if (!is_device_path(arg_mount_what))
1403 arg_fsck = false;
1404
1405 if (arg_fsck && arg_mount_type && arg_transport == BUS_TRANSPORT_LOCAL) {
1406 r = fsck_exists(arg_mount_type);
1407 if (r < 0)
1408 log_warning_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", arg_mount_type);
1409 else if (r == 0) {
1410 log_debug("Disabling file system check as fsck for %s doesn't exist.", arg_mount_type);
1411 arg_fsck = false; /* fsck doesn't exist, let's not attempt it */
1412 }
1413 }
1414
450442cf
LP
1415 switch (arg_action) {
1416
1417 case ACTION_MOUNT:
1418 case ACTION_DEFAULT:
1419 r = start_transient_mount(bus, argv + optind);
1420 break;
1421
1422 case ACTION_AUTOMOUNT:
1423 r = start_transient_automount(bus, argv + optind);
1424 break;
1425
1426 default:
1427 assert_not_reached("Unexpected action.");
1428 }
1429
1430finish:
1431 bus = sd_bus_flush_close_unref(bus);
1432
1433 pager_close();
1434
1435 free(arg_mount_what);
1436 free(arg_mount_where);
1437 free(arg_mount_type);
1438 free(arg_mount_options);
1439 free(arg_description);
1440 strv_free(arg_property);
1441 strv_free(arg_automount_property);
1442
1443 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1444}