]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/mount/mount-tool.c
systemd-mount: support discovery of loop backing file
[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
f0aac575
YW
639static int find_mount_points(const char *what, char ***list) {
640 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
641 _cleanup_strv_free_ char **l = NULL;
642 size_t bufsize = 0, n = 0;
643
644 assert(what);
645 assert(list);
646
647 /* Returns all mount points obtained from /proc/self/mountinfo in *list,
648 * and the number of mount points as return value. */
649
650 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
651 if (!proc_self_mountinfo)
652 return log_error_errno(errno, "Can't open /proc/self/mountinfo: %m");
653
654 for (;;) {
655 _cleanup_free_ char *path = NULL, *where = NULL, *dev = NULL;
656 int r;
657
658 r = fscanf(proc_self_mountinfo,
659 "%*s " /* (1) mount id */
660 "%*s " /* (2) parent id */
661 "%*s " /* (3) major:minor */
662 "%*s " /* (4) root */
663 "%ms " /* (5) mount point */
664 "%*s" /* (6) mount options */
665 "%*[^-]" /* (7) optional fields */
666 "- " /* (8) separator */
667 "%*s " /* (9) file system type */
668 "%ms" /* (10) mount source */
669 "%*s" /* (11) mount options 2 */
670 "%*[^\n]", /* some rubbish at the end */
671 &path, &dev);
672 if (r != 2) {
673 if (r == EOF)
674 break;
675
676 continue;
677 }
678
679 if (!streq(what, dev))
680 continue;
681
682 r = cunescape(path, UNESCAPE_RELAX, &where);
683 if (r < 0)
684 continue;
685
686 /* one extra slot is needed for the terminating NULL */
687 if (!GREEDY_REALLOC(l, bufsize, n + 2))
688 return log_oom();
689
690 l[n] = strdup(where);
691 if (!l[n])
692 return log_oom();
693
694 n++;
695 }
696
697 l[n] = NULL;
698 *list = l;
699 l = NULL; /* avoid freeing */
700
701 return n;
702}
703
704static int find_loop_device(const char *backing_file, char **loop_dev) {
705 _cleanup_closedir_ DIR *d = NULL;
706 struct dirent *de;
707 _cleanup_free_ char *l = NULL;
708
709 assert(backing_file);
710 assert(loop_dev);
711
712 d = opendir("/sys/devices/virtual/block");
713 if (!d) {
714 if (errno == ENOENT)
715 return -ENOENT;
716 return log_error_errno(errno, "Can't open directory /sys/devices/virtual/block: %m");
717 }
718
719 FOREACH_DIRENT(de, d, return -errno) {
720 _cleanup_free_ char *sys = NULL, *fname = NULL;
721 int r;
722
723 dirent_ensure_type(d, de);
724
725 if (de->d_type != DT_DIR)
726 continue;
727
728 if (!startswith(de->d_name, "loop"))
729 continue;
730
731 sys = strjoin("/sys/devices/virtual/block/", de->d_name, "/loop/backing_file");
732 if (!sys)
733 return log_oom();
734
735 r = read_one_line_file(sys, &fname);
736 if (r < 0)
737 continue;
738
739 if (files_same(fname, backing_file, 0) <= 0)
740 continue;
741
742 l = strjoin("/dev/", de->d_name);
743 if (!l)
744 return log_oom();
745
746 break;
747 }
748
749 if (!l)
750 return -ENOENT;
751
752 *loop_dev = l;
753 l = NULL; /* avoid freeing */
754
755 return 0;
756}
757
c37fb55b
LR
758static int stop_mount(
759 sd_bus *bus,
9017f5d8 760 const char *where,
6f6165bf 761 const char *suffix) {
c37fb55b
LR
762
763 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
764 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
765 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
766 _cleanup_free_ char *mount_unit = NULL;
767 int r;
768
769 if (!arg_no_block) {
770 r = bus_wait_for_jobs_new(bus, &w);
771 if (r < 0)
772 return log_error_errno(r, "Could not watch jobs: %m");
773 }
774
9017f5d8 775 r = unit_name_from_path(where, suffix, &mount_unit);
c37fb55b 776 if (r < 0)
9017f5d8 777 return log_error_errno(r, "Failed to make mount unit name from path %s: %m", where);
c37fb55b
LR
778
779 r = sd_bus_message_new_method_call(
780 bus,
781 &m,
782 "org.freedesktop.systemd1",
783 "/org/freedesktop/systemd1",
784 "org.freedesktop.systemd1.Manager",
785 "StopUnit");
786 if (r < 0)
787 return bus_log_create_error(r);
788
789 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
790 if (r < 0)
791 return bus_log_create_error(r);
792
793 /* Name and mode */
794 r = sd_bus_message_append(m, "ss", mount_unit, "fail");
795 if (r < 0)
796 return bus_log_create_error(r);
797
798 polkit_agent_open_if_enabled();
799
800 r = sd_bus_call(bus, m, 0, &error, &reply);
6f6165bf
YW
801 if (r < 0)
802 return log_error_errno(r, "Failed to stop mount unit: %s", bus_error_message(&error, r));
c37fb55b
LR
803
804 if (w) {
805 const char *object;
806
807 r = sd_bus_message_read(reply, "o", &object);
808 if (r < 0)
809 return bus_log_parse_error(r);
810
811 r = bus_wait_for_jobs_one(w, object, arg_quiet);
812 if (r < 0)
813 return r;
814 }
815
816 if (!arg_quiet)
817 log_info("Stopped unit %s%s%s for mount point: %s%s%s",
818 ansi_highlight(), mount_unit, ansi_normal(),
9017f5d8 819 ansi_highlight(), where, ansi_normal());
c37fb55b
LR
820
821 return 0;
822}
823
824static int stop_mounts(
825 sd_bus *bus,
6f6165bf 826 const char *where) {
c37fb55b
LR
827
828 int r;
829
9017f5d8
YW
830 if (path_equal(where, "/")) {
831 log_error("Refusing to operate on root directory: %s", where);
832 return -EINVAL;
833 }
834
835 if (!path_is_safe(where)) {
836 log_error("Path contains unsafe components: %s", where);
837 return -EINVAL;
838 }
839
6f6165bf 840 r = stop_mount(bus, where, ".mount");
c37fb55b
LR
841 if (r < 0)
842 return r;
843
6f6165bf 844 r = stop_mount(bus, where, ".automount");
c37fb55b
LR
845 if (r < 0)
846 return r;
847
848 return 0;
849}
850
9017f5d8
YW
851static int umount_by_device(sd_bus *bus, const char *what) {
852 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
853 _cleanup_udev_unref_ struct udev *udev = NULL;
f0aac575 854 _cleanup_strv_free_ char **list = NULL;
9017f5d8
YW
855 struct stat st;
856 const char *v;
f0aac575 857 char **l;
6f6165bf 858 int r, r2 = 0;
9017f5d8
YW
859
860 assert(what);
861
862 if (stat(what, &st) < 0)
863 return log_error_errno(errno, "Can't stat %s: %m", what);
864
865 if (!S_ISBLK(st.st_mode)) {
6f6165bf 866 log_error("Not a block device: %s", what);
9017f5d8
YW
867 return -ENOTBLK;
868 }
869
870 udev = udev_new();
871 if (!udev)
872 return log_oom();
873
874 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
875 if (!d)
876 return log_oom();
877
878 v = udev_device_get_property_value(d, "ID_FS_USAGE");
879 if (!streq_ptr(v, "filesystem")) {
880 log_error("%s does not contain a known file system.", what);
881 return -EINVAL;
882 }
883
884 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
f0aac575
YW
885 if (!isempty(v))
886 r2 = stop_mounts(bus, v);
9017f5d8 887
f0aac575
YW
888 r = find_mount_points(what, &list);
889 if (r < 0)
890 return r;
891
892 for (l = list; *l; l++) {
893 r = stop_mounts(bus, *l);
6f6165bf 894 if (r < 0)
f0aac575 895 r2 = r;
9017f5d8
YW
896 }
897
6f6165bf
YW
898 return r2;
899}
900
901static int umount_loop(sd_bus *bus, const char *backing_file) {
f0aac575
YW
902 _cleanup_free_ char *loop_dev = NULL;
903 int r;
6f6165bf
YW
904
905 assert(backing_file);
906
f0aac575
YW
907 r = find_loop_device(backing_file, &loop_dev);
908 if (r < 0)
909 return log_error_errno(r, r == -ENOENT ? "File %s is not mounted." : "Can't get loop device for %s: %m", backing_file);
6f6165bf 910
f0aac575 911 return umount_by_device(bus, loop_dev);
9017f5d8
YW
912}
913
914static int action_umount(
915 sd_bus *bus,
916 int argc,
917 char **argv) {
918
919 int i, r, r2 = 0;
920
921 for (i = optind; i < argc; i++) {
6f6165bf
YW
922 _cleanup_free_ char *u = NULL, *p = NULL;
923 struct stat st;
9017f5d8
YW
924
925 u = fstab_node_to_udev_node(argv[i]);
926 if (!u)
927 return log_oom();
9017f5d8 928
6f6165bf 929 r = path_make_absolute_cwd(u, &p);
9017f5d8 930 if (r < 0) {
6f6165bf 931 r2 = log_error_errno(r, "Failed to make path absolute: %m");
9017f5d8
YW
932 continue;
933 }
934
6f6165bf 935 path_kill_slashes(p);
9017f5d8 936
6f6165bf
YW
937 if (stat(p, &st) < 0)
938 return log_error_errno(errno, "Can't stat %s: %m", p);
9017f5d8 939
6f6165bf
YW
940 if (S_ISBLK(st.st_mode))
941 r = umount_by_device(bus, p);
942 else if (S_ISREG(st.st_mode))
943 r = umount_loop(bus, p);
944 else if (S_ISDIR(st.st_mode))
945 r = stop_mounts(bus, p);
946 else {
947 log_error("Invalid file type: %s", p);
948 r = -EINVAL;
949 }
950
951 if (r < 0)
9017f5d8
YW
952 r2 = r;
953 }
954
955 return r2;
956}
957
450442cf
LP
958static int acquire_mount_type(struct udev_device *d) {
959 const char *v;
960
961 assert(d);
962
963 if (arg_mount_type)
964 return 0;
965
966 v = udev_device_get_property_value(d, "ID_FS_TYPE");
967 if (isempty(v))
968 return 0;
969
970 arg_mount_type = strdup(v);
971 if (!arg_mount_type)
972 return log_oom();
973
974 log_debug("Discovered type=%s", arg_mount_type);
975 return 1;
976}
977
978static int acquire_mount_options(struct udev_device *d) {
979 const char *v;
980
981 if (arg_mount_options)
982 return 0;
983
984 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS");
985 if (isempty(v))
986 return 0;
987
988 arg_mount_options = strdup(v);
989 if (!arg_mount_options)
990 return log_oom();
991
992 log_debug("Discovered options=%s", arg_mount_options);
993 return 1;
994}
995
996static const char *get_model(struct udev_device *d) {
997 const char *model;
998
999 assert(d);
1000
1001 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
1002 if (model)
1003 return model;
1004
1005 return udev_device_get_property_value(d, "ID_MODEL");
1006}
1007
1008static const char* get_label(struct udev_device *d) {
1009 const char *label;
1010
1011 assert(d);
1012
1013 label = udev_device_get_property_value(d, "ID_FS_LABEL");
1014 if (label)
1015 return label;
1016
1017 return udev_device_get_property_value(d, "ID_PART_ENTRY_NAME");
1018}
1019
1020static int acquire_mount_where(struct udev_device *d) {
1021 const char *v;
1022
1023 if (arg_mount_where)
1024 return 0;
1025
1026 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
1027 if (isempty(v)) {
1028 _cleanup_free_ char *escaped = NULL;
1029 const char *name;
1030
1031 name = get_label(d);
1032 if (!name)
1033 name = get_model(d);
1034 if (!name) {
1035 const char *dn;
1036
1037 dn = udev_device_get_devnode(d);
1038 if (!dn)
1039 return 0;
1040
1041 name = basename(dn);
1042 }
1043
1044 escaped = xescape(name, "\\");
f0aac575
YW
1045 if (!escaped)
1046 return log_oom();
450442cf
LP
1047 if (!filename_is_valid(escaped))
1048 return 0;
1049
605405c6 1050 arg_mount_where = strjoin("/run/media/system/", escaped);
450442cf
LP
1051 } else
1052 arg_mount_where = strdup(v);
1053
1054 if (!arg_mount_where)
1055 return log_oom();
1056
1057 log_debug("Discovered where=%s", arg_mount_where);
1058 return 1;
1059}
1060
f0aac575
YW
1061static int acquire_mount_where_for_loop_dev(const char *loop_dev) {
1062 _cleanup_strv_free_ char **list = NULL;
1063 int r;
1064
1065 if (arg_mount_where)
1066 return 0;
1067
1068 r = find_mount_points(loop_dev, &list);
1069 if (r < 0)
1070 return r;
1071 else if (r == 0) {
1072 log_error("Can't find mount point of %s. It is expected that %s is already mounted on a place.", loop_dev, loop_dev);
1073 return -EINVAL;
1074 } else if (r >= 2) {
1075 log_error("%s is mounted on %d places. It is expected that %s is mounted on a place.", loop_dev, r, loop_dev);
1076 return -EINVAL;
1077 }
1078
1079 arg_mount_where = strdup(list[0]);
1080 if (!arg_mount_where)
1081 return log_oom();
1082
1083 log_debug("Discovered where=%s", arg_mount_where);
1084 return 1;
1085}
1086
450442cf
LP
1087static int acquire_description(struct udev_device *d) {
1088 const char *model, *label;
1089
1090 if (arg_description)
1091 return 0;
1092
1093 model = get_model(d);
1094
1095 label = get_label(d);
1096 if (!label)
1097 label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER");
1098
1099 if (model && label)
605405c6 1100 arg_description = strjoin(model, " ", label);
450442cf
LP
1101 else if (label)
1102 arg_description = strdup(label);
1103 else if (model)
1104 arg_description = strdup(model);
1105 else
05b4d3b5 1106 return 0;
450442cf
LP
1107
1108 if (!arg_description)
1109 return log_oom();
1110
1111 log_debug("Discovered description=%s", arg_description);
1112 return 1;
1113}
1114
1115static int acquire_removable(struct udev_device *d) {
1116 const char *v;
1117
1118 /* Shortcut this if there's no reason to check it */
1119 if (arg_action != ACTION_DEFAULT && arg_timeout_idle_set && arg_bind_device >= 0)
1120 return 0;
1121
1122 for (;;) {
1123 v = udev_device_get_sysattr_value(d, "removable");
1124 if (v)
1125 break;
1126
1127 d = udev_device_get_parent(d);
1128 if (!d)
1129 return 0;
1130
1131 if (!streq_ptr(udev_device_get_subsystem(d), "block"))
1132 return 0;
1133 }
1134
1135 if (parse_boolean(v) <= 0)
1136 return 0;
1137
1138 log_debug("Discovered removable device.");
1139
1140 if (arg_action == ACTION_DEFAULT) {
1141 log_debug("Automatically turning on automount.");
1142 arg_action = ACTION_AUTOMOUNT;
1143 }
1144
1145 if (!arg_timeout_idle_set) {
1146 log_debug("Setting idle timeout to 1s.");
1147 arg_timeout_idle = USEC_PER_SEC;
1148 }
1149
1150 if (arg_bind_device < 0) {
1151 log_debug("Binding automount unit to device.");
1152 arg_bind_device = true;
1153 }
1154
1155 return 1;
1156}
1157
f0aac575 1158static int discover_loop_backing_file(void) {
450442cf
LP
1159 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
1160 _cleanup_udev_unref_ struct udev *udev = NULL;
f0aac575 1161 _cleanup_free_ char *loop_dev = NULL;
450442cf
LP
1162 struct stat st;
1163 const char *v;
1164 int r;
1165
f0aac575
YW
1166 r = find_loop_device(arg_mount_what, &loop_dev);
1167 if (r < 0 && r != -ENOENT)
1168 return log_error_errno(errno, "Can't get loop device for %s: %m", arg_mount_what);
1169
1170 if (r == -ENOENT) {
1171 _cleanup_free_ char *escaped = NULL;
1172
1173 if (arg_mount_where)
1174 return 0;
1175
1176 escaped = xescape(basename(arg_mount_what), "\\");
1177 if (!escaped)
1178 return log_oom();
1179 if (!filename_is_valid(escaped))
1180 return -EINVAL;
1181
1182 arg_mount_where = strjoin("/run/media/system/", escaped);
1183 if (!arg_mount_where)
1184 return log_oom();
1185
1186 log_debug("Discovered where=%s", arg_mount_where);
450442cf 1187 return 0;
f0aac575
YW
1188 }
1189
1190 if (stat(loop_dev, &st) < 0)
1191 return log_error_errno(errno, "Can't stat %s: %m", loop_dev);
450442cf 1192
f0aac575
YW
1193 if (!S_ISBLK(st.st_mode)) {
1194 log_error("Invalid file type: %s", loop_dev);
450442cf
LP
1195 return -EINVAL;
1196 }
1197
f0aac575
YW
1198 udev = udev_new();
1199 if (!udev)
1200 return log_oom();
1201
1202 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
1203 if (!d)
1204 return log_oom();
1205
1206 v = udev_device_get_property_value(d, "ID_FS_USAGE");
1207 if (!streq_ptr(v, "filesystem")) {
1208 log_error("%s does not contain a known file system.", arg_mount_what);
1209 return -EINVAL;
1210 }
1211
1212 r = acquire_mount_type(d);
1213 if (r < 0)
1214 return r;
1215
1216 r = acquire_mount_options(d);
1217 if (r < 0)
1218 return r;
1219
1220 r = acquire_mount_where_for_loop_dev(loop_dev);
1221 if (r < 0)
1222 return r;
1223
1224 r = acquire_description(d);
1225 if (r < 0)
1226 return r;
1227
1228 return 0;
1229}
1230
1231static int discover_device(void) {
1232 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
1233 _cleanup_udev_unref_ struct udev *udev = NULL;
1234 struct stat st;
1235 const char *v;
1236 int r;
1237
450442cf
LP
1238 if (stat(arg_mount_what, &st) < 0)
1239 return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what);
1240
f0aac575
YW
1241 if (S_ISREG(st.st_mode))
1242 return discover_loop_backing_file();
1243
450442cf 1244 if (!S_ISBLK(st.st_mode)) {
f0aac575
YW
1245 log_error("Invalid file type: %s", arg_mount_what);
1246 return -EINVAL;
450442cf
LP
1247 }
1248
1249 udev = udev_new();
1250 if (!udev)
1251 return log_oom();
1252
1253 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
1254 if (!d)
1255 return log_oom();
1256
1257 v = udev_device_get_property_value(d, "ID_FS_USAGE");
1258 if (!streq_ptr(v, "filesystem")) {
9017f5d8 1259 log_error("%s does not contain a known file system.", arg_mount_what);
450442cf
LP
1260 return -EINVAL;
1261 }
1262
1263 r = acquire_mount_type(d);
1264 if (r < 0)
1265 return r;
1266
1267 r = acquire_mount_options(d);
1268 if (r < 0)
1269 return r;
1270
1271 r = acquire_mount_where(d);
1272 if (r < 0)
1273 return r;
1274
1275 r = acquire_description(d);
1276 if (r < 0)
1277 return r;
1278
1279 r = acquire_removable(d);
1280 if (r < 0)
1281 return r;
1282
1283 return 0;
1284}
1285
1286enum {
1287 COLUMN_NODE,
1288 COLUMN_PATH,
1289 COLUMN_MODEL,
1290 COLUMN_WWN,
1291 COLUMN_FSTYPE,
1292 COLUMN_LABEL,
1293 COLUMN_UUID,
1294 _COLUMN_MAX,
1295};
1296
1297struct item {
1298 char* columns[_COLUMN_MAX];
1299};
1300
1301static int compare_item(const void *a, const void *b) {
1302 const struct item *x = a, *y = b;
1303
1304 if (x->columns[COLUMN_NODE] == y->columns[COLUMN_NODE])
1305 return 0;
1306 if (!x->columns[COLUMN_NODE])
1307 return 1;
1308 if (!y->columns[COLUMN_NODE])
1309 return -1;
1310
1311 return path_compare(x->columns[COLUMN_NODE], y->columns[COLUMN_NODE]);
1312}
1313
1314static int list_devices(void) {
1315
1316 static const char * const titles[_COLUMN_MAX] = {
1317 [COLUMN_NODE] = "NODE",
1318 [COLUMN_PATH] = "PATH",
1319 [COLUMN_MODEL] = "MODEL",
1320 [COLUMN_WWN] = "WWN",
1321 [COLUMN_FSTYPE] = "TYPE",
1322 [COLUMN_LABEL] = "LABEL",
1323 [COLUMN_UUID] = "UUID"
1324 };
1325
1326 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
1327 _cleanup_udev_unref_ struct udev *udev = NULL;
1328 struct udev_list_entry *item = NULL, *first = NULL;
1329 size_t n_allocated = 0, n = 0, i;
1330 size_t column_width[_COLUMN_MAX];
1331 struct item *items = NULL;
1332 unsigned c;
1333 int r;
1334
1335 for (c = 0; c < _COLUMN_MAX; c++)
1336 column_width[c] = strlen(titles[c]);
1337
1338 udev = udev_new();
1339 if (!udev)
1340 return log_oom();
1341
1342 e = udev_enumerate_new(udev);
1343 if (!e)
1344 return log_oom();
1345
1346 r = udev_enumerate_add_match_subsystem(e, "block");
1347 if (r < 0)
1348 return log_error_errno(r, "Failed to add block match: %m");
1349
1350 r = udev_enumerate_add_match_property(e, "ID_FS_USAGE", "filesystem");
1351 if (r < 0)
1352 return log_error_errno(r, "Failed to add property match: %m");
1353
1354 r = udev_enumerate_scan_devices(e);
1355 if (r < 0)
1356 return log_error_errno(r, "Failed to scan devices: %m");
1357
1358 first = udev_enumerate_get_list_entry(e);
1359 udev_list_entry_foreach(item, first) {
1360 _cleanup_udev_device_unref_ struct udev_device *d;
1361 struct item *j;
1362
1363 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
1364 if (!d) {
1365 r = log_oom();
1366 goto finish;
1367 }
1368
1369 if (!GREEDY_REALLOC0(items, n_allocated, n+1)) {
1370 r = log_oom();
1371 goto finish;
1372 }
1373
1374 j = items + n++;
1375
1376 for (c = 0; c < _COLUMN_MAX; c++) {
6fd667e5 1377 const char *x = NULL;
450442cf
LP
1378 size_t k;
1379
1380 switch (c) {
1381
1382 case COLUMN_NODE:
1383 x = udev_device_get_devnode(d);
1384 break;
1385
1386 case COLUMN_PATH:
1387 x = udev_device_get_property_value(d, "ID_PATH");
1388 break;
1389
1390 case COLUMN_MODEL:
1391 x = get_model(d);
1392 break;
1393
1394 case COLUMN_WWN:
1395 x = udev_device_get_property_value(d, "ID_WWN");
1396 break;
1397
1398 case COLUMN_FSTYPE:
1399 x = udev_device_get_property_value(d, "ID_FS_TYPE");
1400 break;
1401
1402 case COLUMN_LABEL:
1403 x = get_label(d);
1404 break;
1405
1406 case COLUMN_UUID:
1407 x = udev_device_get_property_value(d, "ID_FS_UUID");
1408 break;
1409 }
1410
1411 if (isempty(x))
1412 continue;
1413
1414 j->columns[c] = strdup(x);
1415 if (!j->columns[c]) {
1416 r = log_oom();
1417 goto finish;
1418 }
1419
1420 k = strlen(x);
1421 if (k > column_width[c])
1422 column_width[c] = k;
1423 }
1424 }
1425
1426 if (n == 0) {
1427 log_info("No devices found.");
1428 goto finish;
1429 }
1430
1431 qsort_safe(items, n, sizeof(struct item), compare_item);
1432
1433 pager_open(arg_no_pager, false);
1434
1435 fputs(ansi_underline(), stdout);
1436 for (c = 0; c < _COLUMN_MAX; c++) {
1437 if (c > 0)
1438 fputc(' ', stdout);
1439
1440 printf("%-*s", (int) column_width[c], titles[c]);
1441 }
1442 fputs(ansi_normal(), stdout);
1443 fputc('\n', stdout);
1444
1445 for (i = 0; i < n; i++) {
1446 for (c = 0; c < _COLUMN_MAX; c++) {
1447 if (c > 0)
1448 fputc(' ', stdout);
1449
1450 printf("%-*s", (int) column_width[c], strna(items[i].columns[c]));
1451 }
1452 fputc('\n', stdout);
1453 }
1454
1455 r = 0;
1456
1457finish:
1458 for (i = 0; i < n; i++)
1459 for (c = 0; c < _COLUMN_MAX; c++)
1460 free(items[i].columns[c]);
1461
1462 free(items);
1463 return r;
1464}
1465
1466int main(int argc, char* argv[]) {
1467 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1468 int r;
1469
1470 log_parse_environment();
1471 log_open();
1472
1473 r = parse_argv(argc, argv);
1474 if (r <= 0)
1475 goto finish;
1476
1477 if (arg_action == ACTION_LIST) {
1478 r = list_devices();
1479 goto finish;
1480 }
1481
9017f5d8
YW
1482 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1483 if (r < 0) {
1484 log_error_errno(r, "Failed to create bus connection: %m");
1485 goto finish;
1486 }
1487
1488 if (arg_action == ACTION_UMOUNT) {
1489 r = action_umount(bus, argc, argv);
1490 goto finish;
1491 }
1492
f0aac575
YW
1493 path_kill_slashes(arg_mount_what);
1494
1495 if (!path_is_safe(arg_mount_what)) {
1496 log_error("Path contains unsafe components: %s", arg_mount_what);
1497 r = -EINVAL;
450442cf 1498 goto finish;
f0aac575
YW
1499 }
1500
1501 if (arg_discover) {
1502 r = discover_device();
1503 if (r < 0)
1504 goto finish;
1505 }
1506
450442cf
LP
1507 if (!arg_mount_where) {
1508 log_error("Can't figure out where to mount %s.", arg_mount_what);
1509 r = -EINVAL;
1510 goto finish;
1511 }
1512
1513 path_kill_slashes(arg_mount_where);
1514
1515 if (path_equal(arg_mount_where, "/")) {
1516 log_error("Refusing to operate on root directory.");
1517 r = -EINVAL;
1518 goto finish;
1519 }
1520
1521 if (!path_is_safe(arg_mount_where)) {
f0aac575 1522 log_error("Path contains unsafe components: %s", arg_mount_where);
450442cf
LP
1523 r = -EINVAL;
1524 goto finish;
1525 }
1526
1527 if (streq_ptr(arg_mount_type, "auto"))
1528 arg_mount_type = mfree(arg_mount_type);
1529 if (streq_ptr(arg_mount_options, "defaults"))
1530 arg_mount_options = mfree(arg_mount_options);
1531
1532 if (!is_device_path(arg_mount_what))
1533 arg_fsck = false;
1534
1535 if (arg_fsck && arg_mount_type && arg_transport == BUS_TRANSPORT_LOCAL) {
1536 r = fsck_exists(arg_mount_type);
1537 if (r < 0)
1538 log_warning_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", arg_mount_type);
1539 else if (r == 0) {
1540 log_debug("Disabling file system check as fsck for %s doesn't exist.", arg_mount_type);
1541 arg_fsck = false; /* fsck doesn't exist, let's not attempt it */
1542 }
1543 }
1544
450442cf
LP
1545 switch (arg_action) {
1546
1547 case ACTION_MOUNT:
1548 case ACTION_DEFAULT:
1549 r = start_transient_mount(bus, argv + optind);
1550 break;
1551
1552 case ACTION_AUTOMOUNT:
1553 r = start_transient_automount(bus, argv + optind);
1554 break;
1555
1556 default:
1557 assert_not_reached("Unexpected action.");
1558 }
1559
1560finish:
1561 bus = sd_bus_flush_close_unref(bus);
1562
1563 pager_close();
1564
1565 free(arg_mount_what);
1566 free(arg_mount_where);
1567 free(arg_mount_type);
1568 free(arg_mount_options);
1569 free(arg_description);
1570 strv_free(arg_property);
1571 strv_free(arg_automount_property);
1572
1573 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1574}