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