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