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