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