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