]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/mount/mount-tool.c
smack: handling smack onlycap list (#5542)
[thirdparty/systemd.git] / src / mount / mount-tool.c
CommitLineData
450442cf
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2016 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <getopt.h>
21
22#include "libudev.h"
23#include "sd-bus.h"
24
25#include "bus-error.h"
26#include "bus-unit-util.h"
27#include "bus-util.h"
28#include "escape.h"
29#include "fstab-util.h"
30#include "pager.h"
31#include "parse-util.h"
32#include "path-util.h"
33#include "spawn-polkit-agent.h"
34#include "strv.h"
35#include "udev-util.h"
36#include "unit-name.h"
37#include "terminal-util.h"
38
39enum {
40 ACTION_DEFAULT,
41 ACTION_MOUNT,
42 ACTION_AUTOMOUNT,
c37fb55b 43 ACTION_UMOUNT,
450442cf
LP
44 ACTION_LIST,
45} arg_action = ACTION_DEFAULT;
46
47static bool arg_no_block = false;
48static bool arg_no_pager = false;
49static bool arg_ask_password = true;
50static bool arg_quiet = false;
51static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
52static bool arg_user = false;
53static const char *arg_host = NULL;
54static bool arg_discover = false;
55static char *arg_mount_what = NULL;
56static char *arg_mount_where = NULL;
57static char *arg_mount_type = NULL;
58static char *arg_mount_options = NULL;
59static char *arg_description = NULL;
60static char **arg_property = NULL;
61static usec_t arg_timeout_idle = USEC_INFINITY;
62static bool arg_timeout_idle_set = false;
63static char **arg_automount_property = NULL;
64static int arg_bind_device = -1;
65static bool arg_fsck = true;
66
67static void polkit_agent_open_if_enabled(void) {
68
69 /* Open the polkit agent as a child process if necessary */
70 if (!arg_ask_password)
71 return;
72
73 if (arg_transport != BUS_TRANSPORT_LOCAL)
74 return;
75
76 polkit_agent_open();
77}
78
79static void help(void) {
80 printf("%s [OPTIONS...] WHAT [WHERE]\n\n"
81 "Establish a mount or auto-mount point transiently.\n\n"
82 " -h --help Show this help\n"
83 " --version Show package version\n"
84 " --no-block Do not wait until operation finished\n"
85 " --no-pager Do not pipe output into a pager\n"
86 " --no-ask-password Do not prompt for password\n"
87 " -q --quiet Suppress information messages during runtime\n"
88 " --user Run as user unit\n"
89 " -H --host=[USER@]HOST Operate on remote host\n"
90 " -M --machine=CONTAINER Operate on local container\n"
91 " --discover Discover mount device metadata\n"
92 " -t --type=TYPE File system type\n"
93 " -o --options=OPTIONS Mount options\n"
94 " --fsck=no Don't run file system check before mount\n"
95 " --description=TEXT Description for unit\n"
96 " -p --property=NAME=VALUE Set mount unit property\n"
97 " -A --automount=BOOL Create an auto-mount point\n"
98 " --timeout-idle-sec=SEC Specify automount idle timeout\n"
99 " --automount-property=NAME=VALUE\n"
100 " Set automount unit property\n"
101 " --bind-device Bind automount unit to device\n"
102 " --list List mountable block devices\n"
d20a328f 103 " -u --umount Unmount mount points\n"
450442cf
LP
104 , program_invocation_short_name);
105}
106
107static int parse_argv(int argc, char *argv[]) {
108
109 enum {
110 ARG_VERSION = 0x100,
111 ARG_NO_BLOCK,
112 ARG_NO_PAGER,
113 ARG_NO_ASK_PASSWORD,
114 ARG_USER,
115 ARG_SYSTEM,
116 ARG_DISCOVER,
117 ARG_MOUNT_TYPE,
118 ARG_MOUNT_OPTIONS,
119 ARG_FSCK,
120 ARG_DESCRIPTION,
121 ARG_TIMEOUT_IDLE,
122 ARG_AUTOMOUNT,
123 ARG_AUTOMOUNT_PROPERTY,
124 ARG_BIND_DEVICE,
125 ARG_LIST,
126 };
127
128 static const struct option options[] = {
129 { "help", no_argument, NULL, 'h' },
130 { "version", no_argument, NULL, ARG_VERSION },
131 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
132 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
133 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
134 { "quiet", no_argument, NULL, 'q' },
135 { "user", no_argument, NULL, ARG_USER },
136 { "system", no_argument, NULL, ARG_SYSTEM },
137 { "host", required_argument, NULL, 'H' },
138 { "machine", required_argument, NULL, 'M' },
139 { "discover", no_argument, NULL, ARG_DISCOVER },
140 { "type", required_argument, NULL, 't' },
141 { "options", required_argument, NULL, 'o' },
0f923832 142 { "fsck", required_argument, NULL, ARG_FSCK },
450442cf
LP
143 { "description", required_argument, NULL, ARG_DESCRIPTION },
144 { "property", required_argument, NULL, 'p' },
145 { "automount", required_argument, NULL, ARG_AUTOMOUNT },
146 { "timeout-idle-sec", required_argument, NULL, ARG_TIMEOUT_IDLE },
147 { "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY },
148 { "bind-device", no_argument, NULL, ARG_BIND_DEVICE },
149 { "list", no_argument, NULL, ARG_LIST },
c37fb55b
LR
150 { "umount", no_argument, NULL, 'u' },
151 { "unmount", no_argument, NULL, 'u' },
450442cf
LP
152 {},
153 };
154
155 int r, c;
156
157 assert(argc >= 0);
158 assert(argv);
159
c37fb55b
LR
160 if (strstr(program_invocation_short_name, "systemd-umount"))
161 arg_action = ACTION_UMOUNT;
162
163 while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:Au", options, NULL)) >= 0)
450442cf
LP
164
165 switch (c) {
166
167 case 'h':
168 help();
169 return 0;
170
171 case ARG_VERSION:
172 return version();
173
174 case ARG_NO_BLOCK:
175 arg_no_block = true;
176 break;
177
178 case ARG_NO_PAGER:
179 arg_no_pager = true;
180 break;
181
182 case ARG_NO_ASK_PASSWORD:
183 arg_ask_password = false;
184 break;
185
186 case 'q':
187 arg_quiet = true;
188 break;
189
190 case ARG_USER:
191 arg_user = true;
192 break;
193
194 case ARG_SYSTEM:
195 arg_user = false;
196 break;
197
198 case 'H':
199 arg_transport = BUS_TRANSPORT_REMOTE;
200 arg_host = optarg;
201 break;
202
203 case 'M':
204 arg_transport = BUS_TRANSPORT_MACHINE;
205 arg_host = optarg;
206 break;
207
208 case ARG_DISCOVER:
209 arg_discover = true;
210 break;
211
212 case 't':
213 if (free_and_strdup(&arg_mount_type, optarg) < 0)
214 return log_oom();
215 break;
216
217 case 'o':
218 if (free_and_strdup(&arg_mount_options, optarg) < 0)
219 return log_oom();
220 break;
221
222 case ARG_FSCK:
223 r = parse_boolean(optarg);
224 if (r < 0)
225 return log_error_errno(r, "Failed to parse --fsck= argument: %s", optarg);
226
227 arg_fsck = r;
228 break;
229
230 case ARG_DESCRIPTION:
231 if (free_and_strdup(&arg_description, optarg) < 0)
232 return log_oom();
233 break;
234
235 case 'p':
236 if (strv_extend(&arg_property, optarg) < 0)
237 return log_oom();
238
239 break;
240
241 case 'A':
242 arg_action = ACTION_AUTOMOUNT;
243 break;
244
245 case ARG_AUTOMOUNT:
246 r = parse_boolean(optarg);
247 if (r < 0)
248 return log_error_errno(r, "--automount= expects a valid boolean parameter: %s", optarg);
249
250 arg_action = r ? ACTION_AUTOMOUNT : ACTION_MOUNT;
251 break;
252
253 case ARG_TIMEOUT_IDLE:
254 r = parse_sec(optarg, &arg_timeout_idle);
255 if (r < 0)
256 return log_error_errno(r, "Failed to parse timeout: %s", optarg);
257
258 break;
259
260 case ARG_AUTOMOUNT_PROPERTY:
261 if (strv_extend(&arg_automount_property, optarg) < 0)
262 return log_oom();
263
264 break;
265
266 case ARG_BIND_DEVICE:
267 arg_bind_device = true;
268 break;
269
270 case ARG_LIST:
271 arg_action = ACTION_LIST;
272 break;
273
c37fb55b
LR
274 case 'u':
275 arg_action = ACTION_UMOUNT;
276 break;
277
450442cf
LP
278 case '?':
279 return -EINVAL;
280
281 default:
282 assert_not_reached("Unhandled option");
283 }
284
285 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
286 log_error("Execution in user context is not supported on non-local systems.");
287 return -EINVAL;
288 }
289
290 if (arg_action == ACTION_LIST) {
291 if (optind < argc) {
292 log_error("Too many arguments.");
293 return -EINVAL;
294 }
295
296 if (arg_transport != BUS_TRANSPORT_LOCAL) {
297 log_error("Listing devices only supported locally.");
298 return -EOPNOTSUPP;
299 }
300 } else {
301 if (optind >= argc) {
302 log_error("At least one argument required.");
303 return -EINVAL;
304 }
305
306 if (argc > optind+2) {
307 log_error("At most two arguments required.");
308 return -EINVAL;
309 }
310
311 arg_mount_what = fstab_node_to_udev_node(argv[optind]);
312 if (!arg_mount_what)
313 return log_oom();
314
315 if (argc > optind+1) {
316 r = path_make_absolute_cwd(argv[optind+1], &arg_mount_where);
317 if (r < 0)
318 return log_error_errno(r, "Failed to make path absolute: %m");
319 } else
320 arg_discover = true;
321
322 if (arg_discover && arg_transport != BUS_TRANSPORT_LOCAL) {
323 log_error("Automatic mount location discovery is only supported locally.");
324 return -EOPNOTSUPP;
325 }
326 }
327
328 return 1;
329}
330
331static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
332 int r;
333
05b4d3b5
AK
334 if (!isempty(arg_description)) {
335 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
336 if (r < 0)
337 return r;
338 }
450442cf
LP
339
340 if (arg_bind_device && is_device_path(arg_mount_what)) {
341 _cleanup_free_ char *device_unit = NULL;
342
343 r = unit_name_from_path(arg_mount_what, ".device", &device_unit);
344 if (r < 0)
345 return r;
346
347 r = sd_bus_message_append(m, "(sv)(sv)",
348 "After", "as", 1, device_unit,
349 "BindsTo", "as", 1, device_unit);
350 if (r < 0)
351 return r;
352 }
353
354 r = bus_append_unit_property_assignment_many(m, properties);
355 if (r < 0)
356 return r;
357
358 return 0;
359}
360
361static int transient_mount_set_properties(sd_bus_message *m) {
362 int r;
363
364 assert(m);
365
366 r = transient_unit_set_properties(m, arg_property);
367 if (r < 0)
368 return r;
369
370 if (arg_mount_what) {
371 r = sd_bus_message_append(m, "(sv)", "What", "s", arg_mount_what);
372 if (r < 0)
373 return r;
374 }
375
376 if (arg_mount_type) {
377 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_mount_type);
378 if (r < 0)
379 return r;
380 }
381
382 if (arg_mount_options) {
383 r = sd_bus_message_append(m, "(sv)", "Options", "s", arg_mount_options);
384 if (r < 0)
385 return r;
386 }
387
388 if (arg_fsck) {
389 _cleanup_free_ char *fsck = NULL;
390
391 r = unit_name_from_path_instance("systemd-fsck", arg_mount_what, ".service", &fsck);
392 if (r < 0)
393 return r;
394
395 r = sd_bus_message_append(m,
396 "(sv)(sv)",
397 "Requires", "as", 1, fsck,
398 "After", "as", 1, fsck);
399 if (r < 0)
400 return r;
401 }
402
403 return 0;
404}
405
406static int transient_automount_set_properties(sd_bus_message *m) {
407 int r;
408
409 assert(m);
410
411 r = transient_unit_set_properties(m, arg_automount_property);
412 if (r < 0)
413 return r;
414
415 if (arg_timeout_idle != USEC_INFINITY) {
416 r = sd_bus_message_append(m, "(sv)", "TimeoutIdleUSec", "t", arg_timeout_idle);
417 if (r < 0)
418 return r;
419 }
420
421 return 0;
422}
423
424static int start_transient_mount(
425 sd_bus *bus,
426 char **argv) {
427
428 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
429 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
430 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
431 _cleanup_free_ char *mount_unit = NULL;
432 int r;
433
434 if (!arg_no_block) {
435 r = bus_wait_for_jobs_new(bus, &w);
436 if (r < 0)
437 return log_error_errno(r, "Could not watch jobs: %m");
438 }
439
440 r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
441 if (r < 0)
442 return log_error_errno(r, "Failed to make mount unit name: %m");
443
444 r = sd_bus_message_new_method_call(
445 bus,
446 &m,
447 "org.freedesktop.systemd1",
448 "/org/freedesktop/systemd1",
449 "org.freedesktop.systemd1.Manager",
450 "StartTransientUnit");
451 if (r < 0)
452 return bus_log_create_error(r);
453
454 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
455 if (r < 0)
456 return bus_log_create_error(r);
457
458 /* Name and mode */
459 r = sd_bus_message_append(m, "ss", mount_unit, "fail");
460 if (r < 0)
461 return bus_log_create_error(r);
462
463 /* Properties */
464 r = sd_bus_message_open_container(m, 'a', "(sv)");
465 if (r < 0)
466 return bus_log_create_error(r);
467
468 r = transient_mount_set_properties(m);
469 if (r < 0)
470 return bus_log_create_error(r);
471
472 r = sd_bus_message_close_container(m);
473 if (r < 0)
474 return bus_log_create_error(r);
475
476 /* Auxiliary units */
477 r = sd_bus_message_append(m, "a(sa(sv))", 0);
478 if (r < 0)
479 return bus_log_create_error(r);
480
481 polkit_agent_open_if_enabled();
482
483 r = sd_bus_call(bus, m, 0, &error, &reply);
484 if (r < 0)
485 return log_error_errno(r, "Failed to start transient mount unit: %s", bus_error_message(&error, r));
486
487 if (w) {
488 const char *object;
489
490 r = sd_bus_message_read(reply, "o", &object);
491 if (r < 0)
492 return bus_log_parse_error(r);
493
494 r = bus_wait_for_jobs_one(w, object, arg_quiet);
495 if (r < 0)
496 return r;
497 }
498
499 if (!arg_quiet)
500 log_info("Started unit %s%s%s for mount point: %s%s%s",
501 ansi_highlight(), mount_unit, ansi_normal(),
502 ansi_highlight(), arg_mount_where, ansi_normal());
503
504 return 0;
505}
506
507static int start_transient_automount(
508 sd_bus *bus,
509 char **argv) {
510
511 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
512 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
513 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
514 _cleanup_free_ char *automount_unit = NULL, *mount_unit = NULL;
515 int r;
516
517 if (!arg_no_block) {
518 r = bus_wait_for_jobs_new(bus, &w);
519 if (r < 0)
520 return log_error_errno(r, "Could not watch jobs: %m");
521 }
522
523 r = unit_name_from_path(arg_mount_where, ".automount", &automount_unit);
524 if (r < 0)
525 return log_error_errno(r, "Failed to make automount unit name: %m");
526
527 r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
528 if (r < 0)
529 return log_error_errno(r, "Failed to make mount unit name: %m");
530
531 r = sd_bus_message_new_method_call(
532 bus,
533 &m,
534 "org.freedesktop.systemd1",
535 "/org/freedesktop/systemd1",
536 "org.freedesktop.systemd1.Manager",
537 "StartTransientUnit");
538 if (r < 0)
539 return bus_log_create_error(r);
540
541 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
542 if (r < 0)
543 return bus_log_create_error(r);
544
545 /* Name and mode */
546 r = sd_bus_message_append(m, "ss", automount_unit, "fail");
547 if (r < 0)
548 return bus_log_create_error(r);
549
550 /* Properties */
551 r = sd_bus_message_open_container(m, 'a', "(sv)");
552 if (r < 0)
553 return bus_log_create_error(r);
554
555 r = transient_automount_set_properties(m);
556 if (r < 0)
557 return bus_log_create_error(r);
558
559 r = sd_bus_message_close_container(m);
560 if (r < 0)
561 return bus_log_create_error(r);
562
563 /* Auxiliary units */
564 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
565 if (r < 0)
566 return bus_log_create_error(r);
567
568 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
569 if (r < 0)
570 return bus_log_create_error(r);
571
572 r = sd_bus_message_append(m, "s", mount_unit);
573 if (r < 0)
574 return bus_log_create_error(r);
575
576 r = sd_bus_message_open_container(m, 'a', "(sv)");
577 if (r < 0)
578 return bus_log_create_error(r);
579
580 r = transient_mount_set_properties(m);
581 if (r < 0)
582 return bus_log_create_error(r);
583
584 r = sd_bus_message_close_container(m);
585 if (r < 0)
586 return bus_log_create_error(r);
587
588 r = sd_bus_message_close_container(m);
589 if (r < 0)
590 return bus_log_create_error(r);
591
592 r = sd_bus_message_close_container(m);
593 if (r < 0)
594 return bus_log_create_error(r);
595
596 polkit_agent_open_if_enabled();
597
598 r = sd_bus_call(bus, m, 0, &error, &reply);
599 if (r < 0)
600 return log_error_errno(r, "Failed to start transient automount unit: %s", bus_error_message(&error, r));
601
602 if (w) {
603 const char *object;
604
605 r = sd_bus_message_read(reply, "o", &object);
606 if (r < 0)
607 return bus_log_parse_error(r);
608
609 r = bus_wait_for_jobs_one(w, object, arg_quiet);
610 if (r < 0)
611 return r;
612 }
613
614 if (!arg_quiet)
615 log_info("Started unit %s%s%s for mount point: %s%s%s",
616 ansi_highlight(), automount_unit, ansi_normal(),
617 ansi_highlight(), arg_mount_where, ansi_normal());
618
619 return 0;
620}
621
c37fb55b
LR
622static int stop_mount(
623 sd_bus *bus,
624 char **argv,
625 const char *suffix) {
626
627 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
628 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
629 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
630 _cleanup_free_ char *mount_unit = NULL;
631 int r;
632
633 if (!arg_no_block) {
634 r = bus_wait_for_jobs_new(bus, &w);
635 if (r < 0)
636 return log_error_errno(r, "Could not watch jobs: %m");
637 }
638
639 r = unit_name_from_path(arg_mount_where, suffix, &mount_unit);
640 if (r < 0)
641 return log_error_errno(r, "Failed to make mount unit name: %m");
642
643 r = sd_bus_message_new_method_call(
644 bus,
645 &m,
646 "org.freedesktop.systemd1",
647 "/org/freedesktop/systemd1",
648 "org.freedesktop.systemd1.Manager",
649 "StopUnit");
650 if (r < 0)
651 return bus_log_create_error(r);
652
653 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
654 if (r < 0)
655 return bus_log_create_error(r);
656
657 /* Name and mode */
658 r = sd_bus_message_append(m, "ss", mount_unit, "fail");
659 if (r < 0)
660 return bus_log_create_error(r);
661
662 polkit_agent_open_if_enabled();
663
664 r = sd_bus_call(bus, m, 0, &error, &reply);
665 if (r < 0)
666 return log_error_errno(r, "Failed to stop mount unit: %s", bus_error_message(&error, r));
667
668 if (w) {
669 const char *object;
670
671 r = sd_bus_message_read(reply, "o", &object);
672 if (r < 0)
673 return bus_log_parse_error(r);
674
675 r = bus_wait_for_jobs_one(w, object, arg_quiet);
676 if (r < 0)
677 return r;
678 }
679
680 if (!arg_quiet)
681 log_info("Stopped unit %s%s%s for mount point: %s%s%s",
682 ansi_highlight(), mount_unit, ansi_normal(),
683 ansi_highlight(), arg_mount_where, ansi_normal());
684
685 return 0;
686}
687
688static int stop_mounts(
689 sd_bus *bus,
690 char **argv) {
691
692 int r;
693
694 r = stop_mount(bus, argv + optind, ".mount");
695 if (r < 0)
696 return r;
697
698 r = stop_mount(bus, argv + optind, ".automount");
699 if (r < 0)
700 return r;
701
702 return 0;
703}
704
450442cf
LP
705static int acquire_mount_type(struct udev_device *d) {
706 const char *v;
707
708 assert(d);
709
710 if (arg_mount_type)
711 return 0;
712
713 v = udev_device_get_property_value(d, "ID_FS_TYPE");
714 if (isempty(v))
715 return 0;
716
717 arg_mount_type = strdup(v);
718 if (!arg_mount_type)
719 return log_oom();
720
721 log_debug("Discovered type=%s", arg_mount_type);
722 return 1;
723}
724
725static int acquire_mount_options(struct udev_device *d) {
726 const char *v;
727
728 if (arg_mount_options)
729 return 0;
730
731 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS");
732 if (isempty(v))
733 return 0;
734
735 arg_mount_options = strdup(v);
736 if (!arg_mount_options)
737 return log_oom();
738
739 log_debug("Discovered options=%s", arg_mount_options);
740 return 1;
741}
742
743static const char *get_model(struct udev_device *d) {
744 const char *model;
745
746 assert(d);
747
748 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
749 if (model)
750 return model;
751
752 return udev_device_get_property_value(d, "ID_MODEL");
753}
754
755static const char* get_label(struct udev_device *d) {
756 const char *label;
757
758 assert(d);
759
760 label = udev_device_get_property_value(d, "ID_FS_LABEL");
761 if (label)
762 return label;
763
764 return udev_device_get_property_value(d, "ID_PART_ENTRY_NAME");
765}
766
767static int acquire_mount_where(struct udev_device *d) {
768 const char *v;
769
770 if (arg_mount_where)
771 return 0;
772
773 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
774 if (isempty(v)) {
775 _cleanup_free_ char *escaped = NULL;
776 const char *name;
777
778 name = get_label(d);
779 if (!name)
780 name = get_model(d);
781 if (!name) {
782 const char *dn;
783
784 dn = udev_device_get_devnode(d);
785 if (!dn)
786 return 0;
787
788 name = basename(dn);
789 }
790
791 escaped = xescape(name, "\\");
792 if (!filename_is_valid(escaped))
793 return 0;
794
605405c6 795 arg_mount_where = strjoin("/run/media/system/", escaped);
450442cf
LP
796 } else
797 arg_mount_where = strdup(v);
798
799 if (!arg_mount_where)
800 return log_oom();
801
802 log_debug("Discovered where=%s", arg_mount_where);
803 return 1;
804}
805
806static int acquire_description(struct udev_device *d) {
807 const char *model, *label;
808
809 if (arg_description)
810 return 0;
811
812 model = get_model(d);
813
814 label = get_label(d);
815 if (!label)
816 label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER");
817
818 if (model && label)
605405c6 819 arg_description = strjoin(model, " ", label);
450442cf
LP
820 else if (label)
821 arg_description = strdup(label);
822 else if (model)
823 arg_description = strdup(model);
824 else
05b4d3b5 825 return 0;
450442cf
LP
826
827 if (!arg_description)
828 return log_oom();
829
830 log_debug("Discovered description=%s", arg_description);
831 return 1;
832}
833
834static int acquire_removable(struct udev_device *d) {
835 const char *v;
836
837 /* Shortcut this if there's no reason to check it */
838 if (arg_action != ACTION_DEFAULT && arg_timeout_idle_set && arg_bind_device >= 0)
839 return 0;
840
841 for (;;) {
842 v = udev_device_get_sysattr_value(d, "removable");
843 if (v)
844 break;
845
846 d = udev_device_get_parent(d);
847 if (!d)
848 return 0;
849
850 if (!streq_ptr(udev_device_get_subsystem(d), "block"))
851 return 0;
852 }
853
854 if (parse_boolean(v) <= 0)
855 return 0;
856
857 log_debug("Discovered removable device.");
858
859 if (arg_action == ACTION_DEFAULT) {
860 log_debug("Automatically turning on automount.");
861 arg_action = ACTION_AUTOMOUNT;
862 }
863
864 if (!arg_timeout_idle_set) {
865 log_debug("Setting idle timeout to 1s.");
866 arg_timeout_idle = USEC_PER_SEC;
867 }
868
869 if (arg_bind_device < 0) {
870 log_debug("Binding automount unit to device.");
871 arg_bind_device = true;
872 }
873
874 return 1;
875}
876
877static int discover_device(void) {
878 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
879 _cleanup_udev_unref_ struct udev *udev = NULL;
880 struct stat st;
881 const char *v;
882 int r;
883
884 if (!arg_discover)
885 return 0;
886
887 if (!is_device_path(arg_mount_what)) {
888 log_error("Discovery only supported for block devices, don't know what to do.");
889 return -EINVAL;
890 }
891
892 if (stat(arg_mount_what, &st) < 0)
893 return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what);
894
895 if (!S_ISBLK(st.st_mode)) {
896 log_error("Path %s is not a block device, don't know what to do.", arg_mount_what);
897 return -ENOTBLK;
898 }
899
900 udev = udev_new();
901 if (!udev)
902 return log_oom();
903
904 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
905 if (!d)
906 return log_oom();
907
908 v = udev_device_get_property_value(d, "ID_FS_USAGE");
909 if (!streq_ptr(v, "filesystem")) {
910 log_error("%s does not contain a file system.", arg_mount_what);
911 return -EINVAL;
912 }
913
914 r = acquire_mount_type(d);
915 if (r < 0)
916 return r;
917
918 r = acquire_mount_options(d);
919 if (r < 0)
920 return r;
921
922 r = acquire_mount_where(d);
923 if (r < 0)
924 return r;
925
926 r = acquire_description(d);
927 if (r < 0)
928 return r;
929
930 r = acquire_removable(d);
931 if (r < 0)
932 return r;
933
934 return 0;
935}
936
937enum {
938 COLUMN_NODE,
939 COLUMN_PATH,
940 COLUMN_MODEL,
941 COLUMN_WWN,
942 COLUMN_FSTYPE,
943 COLUMN_LABEL,
944 COLUMN_UUID,
945 _COLUMN_MAX,
946};
947
948struct item {
949 char* columns[_COLUMN_MAX];
950};
951
952static int compare_item(const void *a, const void *b) {
953 const struct item *x = a, *y = b;
954
955 if (x->columns[COLUMN_NODE] == y->columns[COLUMN_NODE])
956 return 0;
957 if (!x->columns[COLUMN_NODE])
958 return 1;
959 if (!y->columns[COLUMN_NODE])
960 return -1;
961
962 return path_compare(x->columns[COLUMN_NODE], y->columns[COLUMN_NODE]);
963}
964
965static int list_devices(void) {
966
967 static const char * const titles[_COLUMN_MAX] = {
968 [COLUMN_NODE] = "NODE",
969 [COLUMN_PATH] = "PATH",
970 [COLUMN_MODEL] = "MODEL",
971 [COLUMN_WWN] = "WWN",
972 [COLUMN_FSTYPE] = "TYPE",
973 [COLUMN_LABEL] = "LABEL",
974 [COLUMN_UUID] = "UUID"
975 };
976
977 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
978 _cleanup_udev_unref_ struct udev *udev = NULL;
979 struct udev_list_entry *item = NULL, *first = NULL;
980 size_t n_allocated = 0, n = 0, i;
981 size_t column_width[_COLUMN_MAX];
982 struct item *items = NULL;
983 unsigned c;
984 int r;
985
986 for (c = 0; c < _COLUMN_MAX; c++)
987 column_width[c] = strlen(titles[c]);
988
989 udev = udev_new();
990 if (!udev)
991 return log_oom();
992
993 e = udev_enumerate_new(udev);
994 if (!e)
995 return log_oom();
996
997 r = udev_enumerate_add_match_subsystem(e, "block");
998 if (r < 0)
999 return log_error_errno(r, "Failed to add block match: %m");
1000
1001 r = udev_enumerate_add_match_property(e, "ID_FS_USAGE", "filesystem");
1002 if (r < 0)
1003 return log_error_errno(r, "Failed to add property match: %m");
1004
1005 r = udev_enumerate_scan_devices(e);
1006 if (r < 0)
1007 return log_error_errno(r, "Failed to scan devices: %m");
1008
1009 first = udev_enumerate_get_list_entry(e);
1010 udev_list_entry_foreach(item, first) {
1011 _cleanup_udev_device_unref_ struct udev_device *d;
1012 struct item *j;
1013
1014 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
1015 if (!d) {
1016 r = log_oom();
1017 goto finish;
1018 }
1019
1020 if (!GREEDY_REALLOC0(items, n_allocated, n+1)) {
1021 r = log_oom();
1022 goto finish;
1023 }
1024
1025 j = items + n++;
1026
1027 for (c = 0; c < _COLUMN_MAX; c++) {
6fd667e5 1028 const char *x = NULL;
450442cf
LP
1029 size_t k;
1030
1031 switch (c) {
1032
1033 case COLUMN_NODE:
1034 x = udev_device_get_devnode(d);
1035 break;
1036
1037 case COLUMN_PATH:
1038 x = udev_device_get_property_value(d, "ID_PATH");
1039 break;
1040
1041 case COLUMN_MODEL:
1042 x = get_model(d);
1043 break;
1044
1045 case COLUMN_WWN:
1046 x = udev_device_get_property_value(d, "ID_WWN");
1047 break;
1048
1049 case COLUMN_FSTYPE:
1050 x = udev_device_get_property_value(d, "ID_FS_TYPE");
1051 break;
1052
1053 case COLUMN_LABEL:
1054 x = get_label(d);
1055 break;
1056
1057 case COLUMN_UUID:
1058 x = udev_device_get_property_value(d, "ID_FS_UUID");
1059 break;
1060 }
1061
1062 if (isempty(x))
1063 continue;
1064
1065 j->columns[c] = strdup(x);
1066 if (!j->columns[c]) {
1067 r = log_oom();
1068 goto finish;
1069 }
1070
1071 k = strlen(x);
1072 if (k > column_width[c])
1073 column_width[c] = k;
1074 }
1075 }
1076
1077 if (n == 0) {
1078 log_info("No devices found.");
1079 goto finish;
1080 }
1081
1082 qsort_safe(items, n, sizeof(struct item), compare_item);
1083
1084 pager_open(arg_no_pager, false);
1085
1086 fputs(ansi_underline(), stdout);
1087 for (c = 0; c < _COLUMN_MAX; c++) {
1088 if (c > 0)
1089 fputc(' ', stdout);
1090
1091 printf("%-*s", (int) column_width[c], titles[c]);
1092 }
1093 fputs(ansi_normal(), stdout);
1094 fputc('\n', stdout);
1095
1096 for (i = 0; i < n; i++) {
1097 for (c = 0; c < _COLUMN_MAX; c++) {
1098 if (c > 0)
1099 fputc(' ', stdout);
1100
1101 printf("%-*s", (int) column_width[c], strna(items[i].columns[c]));
1102 }
1103 fputc('\n', stdout);
1104 }
1105
1106 r = 0;
1107
1108finish:
1109 for (i = 0; i < n; i++)
1110 for (c = 0; c < _COLUMN_MAX; c++)
1111 free(items[i].columns[c]);
1112
1113 free(items);
1114 return r;
1115}
1116
1117int main(int argc, char* argv[]) {
1118 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1119 int r;
1120
1121 log_parse_environment();
1122 log_open();
1123
1124 r = parse_argv(argc, argv);
1125 if (r <= 0)
1126 goto finish;
1127
1128 if (arg_action == ACTION_LIST) {
1129 r = list_devices();
1130 goto finish;
1131 }
1132
1133 r = discover_device();
1134 if (r < 0)
1135 goto finish;
1136 if (!arg_mount_where) {
1137 log_error("Can't figure out where to mount %s.", arg_mount_what);
1138 r = -EINVAL;
1139 goto finish;
1140 }
1141
1142 path_kill_slashes(arg_mount_where);
1143
1144 if (path_equal(arg_mount_where, "/")) {
1145 log_error("Refusing to operate on root directory.");
1146 r = -EINVAL;
1147 goto finish;
1148 }
1149
1150 if (!path_is_safe(arg_mount_where)) {
1151 log_error("Path is contains unsafe components.");
1152 r = -EINVAL;
1153 goto finish;
1154 }
1155
1156 if (streq_ptr(arg_mount_type, "auto"))
1157 arg_mount_type = mfree(arg_mount_type);
1158 if (streq_ptr(arg_mount_options, "defaults"))
1159 arg_mount_options = mfree(arg_mount_options);
1160
1161 if (!is_device_path(arg_mount_what))
1162 arg_fsck = false;
1163
1164 if (arg_fsck && arg_mount_type && arg_transport == BUS_TRANSPORT_LOCAL) {
1165 r = fsck_exists(arg_mount_type);
1166 if (r < 0)
1167 log_warning_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", arg_mount_type);
1168 else if (r == 0) {
1169 log_debug("Disabling file system check as fsck for %s doesn't exist.", arg_mount_type);
1170 arg_fsck = false; /* fsck doesn't exist, let's not attempt it */
1171 }
1172 }
1173
1174 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1175 if (r < 0) {
1176 log_error_errno(r, "Failed to create bus connection: %m");
1177 goto finish;
1178 }
1179
1180 switch (arg_action) {
1181
1182 case ACTION_MOUNT:
1183 case ACTION_DEFAULT:
1184 r = start_transient_mount(bus, argv + optind);
1185 break;
1186
1187 case ACTION_AUTOMOUNT:
1188 r = start_transient_automount(bus, argv + optind);
1189 break;
1190
c37fb55b
LR
1191 case ACTION_UMOUNT:
1192 r = stop_mounts(bus, argv + optind);
1193 break;
1194
450442cf
LP
1195 default:
1196 assert_not_reached("Unexpected action.");
1197 }
1198
1199finish:
1200 bus = sd_bus_flush_close_unref(bus);
1201
1202 pager_close();
1203
1204 free(arg_mount_what);
1205 free(arg_mount_where);
1206 free(arg_mount_type);
1207 free(arg_mount_options);
1208 free(arg_description);
1209 strv_free(arg_property);
1210 strv_free(arg_automount_property);
1211
1212 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1213}