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