]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/mount/mount-tool.c
hwdb: provide axis ranges for the Dell XPS M1530 (#4016)
[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
322 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
323 if (r < 0)
324 return r;
325
326 if (arg_bind_device && is_device_path(arg_mount_what)) {
327 _cleanup_free_ char *device_unit = NULL;
328
329 r = unit_name_from_path(arg_mount_what, ".device", &device_unit);
330 if (r < 0)
331 return r;
332
333 r = sd_bus_message_append(m, "(sv)(sv)",
334 "After", "as", 1, device_unit,
335 "BindsTo", "as", 1, device_unit);
336 if (r < 0)
337 return r;
338 }
339
340 r = bus_append_unit_property_assignment_many(m, properties);
341 if (r < 0)
342 return r;
343
344 return 0;
345}
346
347static int transient_mount_set_properties(sd_bus_message *m) {
348 int r;
349
350 assert(m);
351
352 r = transient_unit_set_properties(m, arg_property);
353 if (r < 0)
354 return r;
355
356 if (arg_mount_what) {
357 r = sd_bus_message_append(m, "(sv)", "What", "s", arg_mount_what);
358 if (r < 0)
359 return r;
360 }
361
362 if (arg_mount_type) {
363 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_mount_type);
364 if (r < 0)
365 return r;
366 }
367
368 if (arg_mount_options) {
369 r = sd_bus_message_append(m, "(sv)", "Options", "s", arg_mount_options);
370 if (r < 0)
371 return r;
372 }
373
374 if (arg_fsck) {
375 _cleanup_free_ char *fsck = NULL;
376
377 r = unit_name_from_path_instance("systemd-fsck", arg_mount_what, ".service", &fsck);
378 if (r < 0)
379 return r;
380
381 r = sd_bus_message_append(m,
382 "(sv)(sv)",
383 "Requires", "as", 1, fsck,
384 "After", "as", 1, fsck);
385 if (r < 0)
386 return r;
387 }
388
389 return 0;
390}
391
392static int transient_automount_set_properties(sd_bus_message *m) {
393 int r;
394
395 assert(m);
396
397 r = transient_unit_set_properties(m, arg_automount_property);
398 if (r < 0)
399 return r;
400
401 if (arg_timeout_idle != USEC_INFINITY) {
402 r = sd_bus_message_append(m, "(sv)", "TimeoutIdleUSec", "t", arg_timeout_idle);
403 if (r < 0)
404 return r;
405 }
406
407 return 0;
408}
409
410static int start_transient_mount(
411 sd_bus *bus,
412 char **argv) {
413
414 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
415 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
416 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
417 _cleanup_free_ char *mount_unit = NULL;
418 int r;
419
420 if (!arg_no_block) {
421 r = bus_wait_for_jobs_new(bus, &w);
422 if (r < 0)
423 return log_error_errno(r, "Could not watch jobs: %m");
424 }
425
426 r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
427 if (r < 0)
428 return log_error_errno(r, "Failed to make mount unit name: %m");
429
430 r = sd_bus_message_new_method_call(
431 bus,
432 &m,
433 "org.freedesktop.systemd1",
434 "/org/freedesktop/systemd1",
435 "org.freedesktop.systemd1.Manager",
436 "StartTransientUnit");
437 if (r < 0)
438 return bus_log_create_error(r);
439
440 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
441 if (r < 0)
442 return bus_log_create_error(r);
443
444 /* Name and mode */
445 r = sd_bus_message_append(m, "ss", mount_unit, "fail");
446 if (r < 0)
447 return bus_log_create_error(r);
448
449 /* Properties */
450 r = sd_bus_message_open_container(m, 'a', "(sv)");
451 if (r < 0)
452 return bus_log_create_error(r);
453
454 r = transient_mount_set_properties(m);
455 if (r < 0)
456 return bus_log_create_error(r);
457
458 r = sd_bus_message_close_container(m);
459 if (r < 0)
460 return bus_log_create_error(r);
461
462 /* Auxiliary units */
463 r = sd_bus_message_append(m, "a(sa(sv))", 0);
464 if (r < 0)
465 return bus_log_create_error(r);
466
467 polkit_agent_open_if_enabled();
468
469 r = sd_bus_call(bus, m, 0, &error, &reply);
470 if (r < 0)
471 return log_error_errno(r, "Failed to start transient mount unit: %s", bus_error_message(&error, r));
472
473 if (w) {
474 const char *object;
475
476 r = sd_bus_message_read(reply, "o", &object);
477 if (r < 0)
478 return bus_log_parse_error(r);
479
480 r = bus_wait_for_jobs_one(w, object, arg_quiet);
481 if (r < 0)
482 return r;
483 }
484
485 if (!arg_quiet)
486 log_info("Started unit %s%s%s for mount point: %s%s%s",
487 ansi_highlight(), mount_unit, ansi_normal(),
488 ansi_highlight(), arg_mount_where, ansi_normal());
489
490 return 0;
491}
492
493static int start_transient_automount(
494 sd_bus *bus,
495 char **argv) {
496
497 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
498 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
499 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
500 _cleanup_free_ char *automount_unit = NULL, *mount_unit = NULL;
501 int r;
502
503 if (!arg_no_block) {
504 r = bus_wait_for_jobs_new(bus, &w);
505 if (r < 0)
506 return log_error_errno(r, "Could not watch jobs: %m");
507 }
508
509 r = unit_name_from_path(arg_mount_where, ".automount", &automount_unit);
510 if (r < 0)
511 return log_error_errno(r, "Failed to make automount unit name: %m");
512
513 r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
514 if (r < 0)
515 return log_error_errno(r, "Failed to make mount unit name: %m");
516
517 r = sd_bus_message_new_method_call(
518 bus,
519 &m,
520 "org.freedesktop.systemd1",
521 "/org/freedesktop/systemd1",
522 "org.freedesktop.systemd1.Manager",
523 "StartTransientUnit");
524 if (r < 0)
525 return bus_log_create_error(r);
526
527 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
528 if (r < 0)
529 return bus_log_create_error(r);
530
531 /* Name and mode */
532 r = sd_bus_message_append(m, "ss", automount_unit, "fail");
533 if (r < 0)
534 return bus_log_create_error(r);
535
536 /* Properties */
537 r = sd_bus_message_open_container(m, 'a', "(sv)");
538 if (r < 0)
539 return bus_log_create_error(r);
540
541 r = transient_automount_set_properties(m);
542 if (r < 0)
543 return bus_log_create_error(r);
544
545 r = sd_bus_message_close_container(m);
546 if (r < 0)
547 return bus_log_create_error(r);
548
549 /* Auxiliary units */
550 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
551 if (r < 0)
552 return bus_log_create_error(r);
553
554 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
555 if (r < 0)
556 return bus_log_create_error(r);
557
558 r = sd_bus_message_append(m, "s", mount_unit);
559 if (r < 0)
560 return bus_log_create_error(r);
561
562 r = sd_bus_message_open_container(m, 'a', "(sv)");
563 if (r < 0)
564 return bus_log_create_error(r);
565
566 r = transient_mount_set_properties(m);
567 if (r < 0)
568 return bus_log_create_error(r);
569
570 r = sd_bus_message_close_container(m);
571 if (r < 0)
572 return bus_log_create_error(r);
573
574 r = sd_bus_message_close_container(m);
575 if (r < 0)
576 return bus_log_create_error(r);
577
578 r = sd_bus_message_close_container(m);
579 if (r < 0)
580 return bus_log_create_error(r);
581
582 polkit_agent_open_if_enabled();
583
584 r = sd_bus_call(bus, m, 0, &error, &reply);
585 if (r < 0)
586 return log_error_errno(r, "Failed to start transient automount unit: %s", bus_error_message(&error, r));
587
588 if (w) {
589 const char *object;
590
591 r = sd_bus_message_read(reply, "o", &object);
592 if (r < 0)
593 return bus_log_parse_error(r);
594
595 r = bus_wait_for_jobs_one(w, object, arg_quiet);
596 if (r < 0)
597 return r;
598 }
599
600 if (!arg_quiet)
601 log_info("Started unit %s%s%s for mount point: %s%s%s",
602 ansi_highlight(), automount_unit, ansi_normal(),
603 ansi_highlight(), arg_mount_where, ansi_normal());
604
605 return 0;
606}
607
608static int acquire_mount_type(struct udev_device *d) {
609 const char *v;
610
611 assert(d);
612
613 if (arg_mount_type)
614 return 0;
615
616 v = udev_device_get_property_value(d, "ID_FS_TYPE");
617 if (isempty(v))
618 return 0;
619
620 arg_mount_type = strdup(v);
621 if (!arg_mount_type)
622 return log_oom();
623
624 log_debug("Discovered type=%s", arg_mount_type);
625 return 1;
626}
627
628static int acquire_mount_options(struct udev_device *d) {
629 const char *v;
630
631 if (arg_mount_options)
632 return 0;
633
634 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS");
635 if (isempty(v))
636 return 0;
637
638 arg_mount_options = strdup(v);
639 if (!arg_mount_options)
640 return log_oom();
641
642 log_debug("Discovered options=%s", arg_mount_options);
643 return 1;
644}
645
646static const char *get_model(struct udev_device *d) {
647 const char *model;
648
649 assert(d);
650
651 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
652 if (model)
653 return model;
654
655 return udev_device_get_property_value(d, "ID_MODEL");
656}
657
658static const char* get_label(struct udev_device *d) {
659 const char *label;
660
661 assert(d);
662
663 label = udev_device_get_property_value(d, "ID_FS_LABEL");
664 if (label)
665 return label;
666
667 return udev_device_get_property_value(d, "ID_PART_ENTRY_NAME");
668}
669
670static int acquire_mount_where(struct udev_device *d) {
671 const char *v;
672
673 if (arg_mount_where)
674 return 0;
675
676 v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
677 if (isempty(v)) {
678 _cleanup_free_ char *escaped = NULL;
679 const char *name;
680
681 name = get_label(d);
682 if (!name)
683 name = get_model(d);
684 if (!name) {
685 const char *dn;
686
687 dn = udev_device_get_devnode(d);
688 if (!dn)
689 return 0;
690
691 name = basename(dn);
692 }
693
694 escaped = xescape(name, "\\");
695 if (!filename_is_valid(escaped))
696 return 0;
697
698 arg_mount_where = strjoin("/run/media/system/", escaped, NULL);
699 } else
700 arg_mount_where = strdup(v);
701
702 if (!arg_mount_where)
703 return log_oom();
704
705 log_debug("Discovered where=%s", arg_mount_where);
706 return 1;
707}
708
709static int acquire_description(struct udev_device *d) {
710 const char *model, *label;
711
712 if (arg_description)
713 return 0;
714
715 model = get_model(d);
716
717 label = get_label(d);
718 if (!label)
719 label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER");
720
721 if (model && label)
722 arg_description = strjoin(model, " ", label, NULL);
723 else if (label)
724 arg_description = strdup(label);
725 else if (model)
726 arg_description = strdup(model);
727 else
728 return NULL;
729
730 if (!arg_description)
731 return log_oom();
732
733 log_debug("Discovered description=%s", arg_description);
734 return 1;
735}
736
737static int acquire_removable(struct udev_device *d) {
738 const char *v;
739
740 /* Shortcut this if there's no reason to check it */
741 if (arg_action != ACTION_DEFAULT && arg_timeout_idle_set && arg_bind_device >= 0)
742 return 0;
743
744 for (;;) {
745 v = udev_device_get_sysattr_value(d, "removable");
746 if (v)
747 break;
748
749 d = udev_device_get_parent(d);
750 if (!d)
751 return 0;
752
753 if (!streq_ptr(udev_device_get_subsystem(d), "block"))
754 return 0;
755 }
756
757 if (parse_boolean(v) <= 0)
758 return 0;
759
760 log_debug("Discovered removable device.");
761
762 if (arg_action == ACTION_DEFAULT) {
763 log_debug("Automatically turning on automount.");
764 arg_action = ACTION_AUTOMOUNT;
765 }
766
767 if (!arg_timeout_idle_set) {
768 log_debug("Setting idle timeout to 1s.");
769 arg_timeout_idle = USEC_PER_SEC;
770 }
771
772 if (arg_bind_device < 0) {
773 log_debug("Binding automount unit to device.");
774 arg_bind_device = true;
775 }
776
777 return 1;
778}
779
780static int discover_device(void) {
781 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
782 _cleanup_udev_unref_ struct udev *udev = NULL;
783 struct stat st;
784 const char *v;
785 int r;
786
787 if (!arg_discover)
788 return 0;
789
790 if (!is_device_path(arg_mount_what)) {
791 log_error("Discovery only supported for block devices, don't know what to do.");
792 return -EINVAL;
793 }
794
795 if (stat(arg_mount_what, &st) < 0)
796 return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what);
797
798 if (!S_ISBLK(st.st_mode)) {
799 log_error("Path %s is not a block device, don't know what to do.", arg_mount_what);
800 return -ENOTBLK;
801 }
802
803 udev = udev_new();
804 if (!udev)
805 return log_oom();
806
807 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
808 if (!d)
809 return log_oom();
810
811 v = udev_device_get_property_value(d, "ID_FS_USAGE");
812 if (!streq_ptr(v, "filesystem")) {
813 log_error("%s does not contain a file system.", arg_mount_what);
814 return -EINVAL;
815 }
816
817 r = acquire_mount_type(d);
818 if (r < 0)
819 return r;
820
821 r = acquire_mount_options(d);
822 if (r < 0)
823 return r;
824
825 r = acquire_mount_where(d);
826 if (r < 0)
827 return r;
828
829 r = acquire_description(d);
830 if (r < 0)
831 return r;
832
833 r = acquire_removable(d);
834 if (r < 0)
835 return r;
836
837 return 0;
838}
839
840enum {
841 COLUMN_NODE,
842 COLUMN_PATH,
843 COLUMN_MODEL,
844 COLUMN_WWN,
845 COLUMN_FSTYPE,
846 COLUMN_LABEL,
847 COLUMN_UUID,
848 _COLUMN_MAX,
849};
850
851struct item {
852 char* columns[_COLUMN_MAX];
853};
854
855static int compare_item(const void *a, const void *b) {
856 const struct item *x = a, *y = b;
857
858 if (x->columns[COLUMN_NODE] == y->columns[COLUMN_NODE])
859 return 0;
860 if (!x->columns[COLUMN_NODE])
861 return 1;
862 if (!y->columns[COLUMN_NODE])
863 return -1;
864
865 return path_compare(x->columns[COLUMN_NODE], y->columns[COLUMN_NODE]);
866}
867
868static int list_devices(void) {
869
870 static const char * const titles[_COLUMN_MAX] = {
871 [COLUMN_NODE] = "NODE",
872 [COLUMN_PATH] = "PATH",
873 [COLUMN_MODEL] = "MODEL",
874 [COLUMN_WWN] = "WWN",
875 [COLUMN_FSTYPE] = "TYPE",
876 [COLUMN_LABEL] = "LABEL",
877 [COLUMN_UUID] = "UUID"
878 };
879
880 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
881 _cleanup_udev_unref_ struct udev *udev = NULL;
882 struct udev_list_entry *item = NULL, *first = NULL;
883 size_t n_allocated = 0, n = 0, i;
884 size_t column_width[_COLUMN_MAX];
885 struct item *items = NULL;
886 unsigned c;
887 int r;
888
889 for (c = 0; c < _COLUMN_MAX; c++)
890 column_width[c] = strlen(titles[c]);
891
892 udev = udev_new();
893 if (!udev)
894 return log_oom();
895
896 e = udev_enumerate_new(udev);
897 if (!e)
898 return log_oom();
899
900 r = udev_enumerate_add_match_subsystem(e, "block");
901 if (r < 0)
902 return log_error_errno(r, "Failed to add block match: %m");
903
904 r = udev_enumerate_add_match_property(e, "ID_FS_USAGE", "filesystem");
905 if (r < 0)
906 return log_error_errno(r, "Failed to add property match: %m");
907
908 r = udev_enumerate_scan_devices(e);
909 if (r < 0)
910 return log_error_errno(r, "Failed to scan devices: %m");
911
912 first = udev_enumerate_get_list_entry(e);
913 udev_list_entry_foreach(item, first) {
914 _cleanup_udev_device_unref_ struct udev_device *d;
915 struct item *j;
916
917 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
918 if (!d) {
919 r = log_oom();
920 goto finish;
921 }
922
923 if (!GREEDY_REALLOC0(items, n_allocated, n+1)) {
924 r = log_oom();
925 goto finish;
926 }
927
928 j = items + n++;
929
930 for (c = 0; c < _COLUMN_MAX; c++) {
931 const char *x;
932 size_t k;
933
934 switch (c) {
935
936 case COLUMN_NODE:
937 x = udev_device_get_devnode(d);
938 break;
939
940 case COLUMN_PATH:
941 x = udev_device_get_property_value(d, "ID_PATH");
942 break;
943
944 case COLUMN_MODEL:
945 x = get_model(d);
946 break;
947
948 case COLUMN_WWN:
949 x = udev_device_get_property_value(d, "ID_WWN");
950 break;
951
952 case COLUMN_FSTYPE:
953 x = udev_device_get_property_value(d, "ID_FS_TYPE");
954 break;
955
956 case COLUMN_LABEL:
957 x = get_label(d);
958 break;
959
960 case COLUMN_UUID:
961 x = udev_device_get_property_value(d, "ID_FS_UUID");
962 break;
963 }
964
965 if (isempty(x))
966 continue;
967
968 j->columns[c] = strdup(x);
969 if (!j->columns[c]) {
970 r = log_oom();
971 goto finish;
972 }
973
974 k = strlen(x);
975 if (k > column_width[c])
976 column_width[c] = k;
977 }
978 }
979
980 if (n == 0) {
981 log_info("No devices found.");
982 goto finish;
983 }
984
985 qsort_safe(items, n, sizeof(struct item), compare_item);
986
987 pager_open(arg_no_pager, false);
988
989 fputs(ansi_underline(), stdout);
990 for (c = 0; c < _COLUMN_MAX; c++) {
991 if (c > 0)
992 fputc(' ', stdout);
993
994 printf("%-*s", (int) column_width[c], titles[c]);
995 }
996 fputs(ansi_normal(), stdout);
997 fputc('\n', stdout);
998
999 for (i = 0; i < n; i++) {
1000 for (c = 0; c < _COLUMN_MAX; c++) {
1001 if (c > 0)
1002 fputc(' ', stdout);
1003
1004 printf("%-*s", (int) column_width[c], strna(items[i].columns[c]));
1005 }
1006 fputc('\n', stdout);
1007 }
1008
1009 r = 0;
1010
1011finish:
1012 for (i = 0; i < n; i++)
1013 for (c = 0; c < _COLUMN_MAX; c++)
1014 free(items[i].columns[c]);
1015
1016 free(items);
1017 return r;
1018}
1019
1020int main(int argc, char* argv[]) {
1021 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1022 int r;
1023
1024 log_parse_environment();
1025 log_open();
1026
1027 r = parse_argv(argc, argv);
1028 if (r <= 0)
1029 goto finish;
1030
1031 if (arg_action == ACTION_LIST) {
1032 r = list_devices();
1033 goto finish;
1034 }
1035
1036 r = discover_device();
1037 if (r < 0)
1038 goto finish;
1039 if (!arg_mount_where) {
1040 log_error("Can't figure out where to mount %s.", arg_mount_what);
1041 r = -EINVAL;
1042 goto finish;
1043 }
1044
1045 path_kill_slashes(arg_mount_where);
1046
1047 if (path_equal(arg_mount_where, "/")) {
1048 log_error("Refusing to operate on root directory.");
1049 r = -EINVAL;
1050 goto finish;
1051 }
1052
1053 if (!path_is_safe(arg_mount_where)) {
1054 log_error("Path is contains unsafe components.");
1055 r = -EINVAL;
1056 goto finish;
1057 }
1058
1059 if (streq_ptr(arg_mount_type, "auto"))
1060 arg_mount_type = mfree(arg_mount_type);
1061 if (streq_ptr(arg_mount_options, "defaults"))
1062 arg_mount_options = mfree(arg_mount_options);
1063
1064 if (!is_device_path(arg_mount_what))
1065 arg_fsck = false;
1066
1067 if (arg_fsck && arg_mount_type && arg_transport == BUS_TRANSPORT_LOCAL) {
1068 r = fsck_exists(arg_mount_type);
1069 if (r < 0)
1070 log_warning_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", arg_mount_type);
1071 else if (r == 0) {
1072 log_debug("Disabling file system check as fsck for %s doesn't exist.", arg_mount_type);
1073 arg_fsck = false; /* fsck doesn't exist, let's not attempt it */
1074 }
1075 }
1076
1077 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1078 if (r < 0) {
1079 log_error_errno(r, "Failed to create bus connection: %m");
1080 goto finish;
1081 }
1082
1083 switch (arg_action) {
1084
1085 case ACTION_MOUNT:
1086 case ACTION_DEFAULT:
1087 r = start_transient_mount(bus, argv + optind);
1088 break;
1089
1090 case ACTION_AUTOMOUNT:
1091 r = start_transient_automount(bus, argv + optind);
1092 break;
1093
1094 default:
1095 assert_not_reached("Unexpected action.");
1096 }
1097
1098finish:
1099 bus = sd_bus_flush_close_unref(bus);
1100
1101 pager_close();
1102
1103 free(arg_mount_what);
1104 free(arg_mount_where);
1105 free(arg_mount_type);
1106 free(arg_mount_options);
1107 free(arg_description);
1108 strv_free(arg_property);
1109 strv_free(arg_automount_property);
1110
1111 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1112}