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