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