chiark / gitweb /
Remove src/getty-generator
[elogind.git] / src / gpt-auto-generator / gpt-auto-generator.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <sys/statfs.h>
25 #include <blkid/blkid.h>
26
27 #include "sd-id128.h"
28 #include "libudev.h"
29 #include "path-util.h"
30 #include "util.h"
31 #include "mkdir.h"
32 #include "missing.h"
33 #include "udev-util.h"
34 #include "special.h"
35 #include "unit-name.h"
36 #include "virt.h"
37 #include "generator.h"
38 #include "gpt.h"
39 #include "fileio.h"
40 #include "efivars.h"
41 #include "blkid-util.h"
42 #include "btrfs-util.h"
43
44 static const char *arg_dest = "/tmp";
45 static bool arg_enabled = true;
46 static bool arg_root_enabled = true;
47 static bool arg_root_rw = false;
48
49 static int add_swap(const char *path) {
50         _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
51         _cleanup_fclose_ FILE *f = NULL;
52
53         assert(path);
54
55         log_debug("Adding swap: %s", path);
56
57         name = unit_name_from_path(path, ".swap");
58         if (!name)
59                 return log_oom();
60
61         unit = strjoin(arg_dest, "/", name, NULL);
62         if (!unit)
63                 return log_oom();
64
65         f = fopen(unit, "wxe");
66         if (!f)
67                 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
68
69         fprintf(f,
70                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
71                 "[Unit]\n"
72                 "Description=Swap Partition\n"
73                 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
74                 "[Swap]\n"
75                 "What=%s\n",
76                 path);
77
78         fflush(f);
79         if (ferror(f))
80                 return log_error_errno(errno, "Failed to write unit file %s: %m", unit);
81
82         lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
83         if (!lnk)
84                 return log_oom();
85
86         mkdir_parents_label(lnk, 0755);
87         if (symlink(unit, lnk) < 0)
88                 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
89
90         return 0;
91 }
92
93 static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
94         _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
95         _cleanup_fclose_ FILE *f = NULL;
96         char *from, *ret;
97         int r;
98
99         assert(id);
100         assert(what);
101         assert(device);
102
103         d = unit_name_from_path(what, ".device");
104         if (!d)
105                 return log_oom();
106
107         e = unit_name_escape(id);
108         if (!e)
109                 return log_oom();
110
111         n = unit_name_build("systemd-cryptsetup", e, ".service");
112         if (!n)
113                 return log_oom();
114
115         p = strjoin(arg_dest, "/", n, NULL);
116         if (!p)
117                 return log_oom();
118
119         f = fopen(p, "wxe");
120         if (!f)
121                 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
122
123         fprintf(f,
124                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
125                 "[Unit]\n"
126                 "Description=Cryptography Setup for %%I\n"
127                 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
128                 "DefaultDependencies=no\n"
129                 "Conflicts=umount.target\n"
130                 "BindsTo=dev-mapper-%%i.device %s\n"
131                 "Before=umount.target cryptsetup.target\n"
132                 "After=%s\n"
133                 "IgnoreOnIsolate=true\n"
134                 "[Service]\n"
135                 "Type=oneshot\n"
136                 "RemainAfterExit=yes\n"
137                 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
138                 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
139                 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
140                 d, d,
141                 id, what, rw ? "" : "read-only",
142                 id);
143
144         fflush(f);
145         if (ferror(f))
146                 return log_error_errno(errno, "Failed to write file %s: %m", p);
147
148         from = strjoina("../", n);
149
150         to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
151         if (!to)
152                 return log_oom();
153
154         mkdir_parents_label(to, 0755);
155         if (symlink(from, to) < 0)
156                 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
157
158         free(to);
159         to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
160         if (!to)
161                 return log_oom();
162
163         mkdir_parents_label(to, 0755);
164         if (symlink(from, to) < 0)
165                 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
166
167         free(to);
168         to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
169         if (!to)
170                 return log_oom();
171
172         mkdir_parents_label(to, 0755);
173         if (symlink(from, to) < 0)
174                 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
175
176         free(p);
177         p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
178         if (!p)
179                 return log_oom();
180
181         mkdir_parents_label(p, 0755);
182         r = write_string_file(p,
183                         "# Automatically generated by systemd-gpt-auto-generator\n\n"
184                         "[Unit]\n"
185                         "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
186         if (r < 0)
187                 return log_error_errno(r, "Failed to write device drop-in: %m");
188
189         ret = strappend("/dev/mapper/", id);
190         if (!ret)
191                 return log_oom();
192
193         *device = ret;
194         return 0;
195 }
196
197 static int add_mount(
198                 const char *id,
199                 const char *what,
200                 const char *where,
201                 const char *fstype,
202                 bool rw,
203                 const char *description,
204                 const char *post) {
205
206         _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL;
207         _cleanup_fclose_ FILE *f = NULL;
208         int r;
209
210         assert(id);
211         assert(what);
212         assert(where);
213         assert(description);
214
215         log_debug("Adding %s: %s %s", where, what, strna(fstype));
216
217         if (streq_ptr(fstype, "crypto_LUKS")) {
218
219                 r = add_cryptsetup(id, what, rw, &crypto_what);
220                 if (r < 0)
221                         return r;
222
223                 what = crypto_what;
224                 fstype = NULL;
225         }
226
227         unit = unit_name_from_path(where, ".mount");
228         if (!unit)
229                 return log_oom();
230
231         p = strjoin(arg_dest, "/", unit, NULL);
232         if (!p)
233                 return log_oom();
234
235         f = fopen(p, "wxe");
236         if (!f)
237                 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
238
239         fprintf(f,
240                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
241                 "[Unit]\n"
242                 "Description=%s\n"
243                 "Documentation=man:systemd-gpt-auto-generator(8)\n",
244                 description);
245
246         if (post)
247                 fprintf(f, "Before=%s\n", post);
248
249         r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
250         if (r < 0)
251                 return r;
252
253         fprintf(f,
254                 "\n"
255                 "[Mount]\n"
256                 "What=%s\n"
257                 "Where=%s\n",
258                 what, where);
259
260         if (fstype)
261                 fprintf(f, "Type=%s\n", fstype);
262
263         fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
264
265         fflush(f);
266         if (ferror(f))
267                 return log_error_errno(errno, "Failed to write unit file %s: %m", p);
268
269         if (post) {
270                 lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL);
271                 if (!lnk)
272                         return log_oom();
273
274                 mkdir_parents_label(lnk, 0755);
275                 if (symlink(p, lnk) < 0)
276                         return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
277         }
278
279         return 0;
280 }
281
282 static int probe_and_add_mount(
283                 const char *id,
284                 const char *what,
285                 const char *where,
286                 bool rw,
287                 const char *description,
288                 const char *post) {
289
290         _cleanup_blkid_free_probe_ blkid_probe b = NULL;
291         const char *fstype = NULL;
292         int r;
293
294         assert(id);
295         assert(what);
296         assert(where);
297         assert(description);
298
299         if (path_is_mount_point(where, true) <= 0 &&
300             dir_is_empty(where) <= 0) {
301                 log_debug("%s already populated, ignoring.", where);
302                 return 0;
303         }
304
305         /* Let's check the partition type here, so that we know
306          * whether to do LUKS magic. */
307
308         errno = 0;
309         b = blkid_new_probe_from_filename(what);
310         if (!b) {
311                 if (errno == 0)
312                         return log_oom();
313                 log_error_errno(errno, "Failed to allocate prober: %m");
314                 return -errno;
315         }
316
317         blkid_probe_enable_superblocks(b, 1);
318         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
319
320         errno = 0;
321         r = blkid_do_safeprobe(b);
322         if (r == -2 || r == 1) /* no result or uncertain */
323                 return 0;
324         else if (r != 0)
325                 return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
326
327         /* add_mount is OK with fstype being NULL. */
328         (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
329
330         return add_mount(
331                         id,
332                         what,
333                         where,
334                         fstype,
335                         rw,
336                         description,
337                         post);
338 }
339
340 static int enumerate_partitions(dev_t devnum) {
341
342         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
343         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
344         _cleanup_blkid_free_probe_ blkid_probe b = NULL;
345         _cleanup_udev_unref_ struct udev *udev = NULL;
346         _cleanup_free_ char *home = NULL, *srv = NULL;
347         struct udev_list_entry *first, *item;
348         struct udev_device *parent = NULL;
349         const char *name, *node, *pttype, *devtype;
350         int home_nr = -1, srv_nr = -1;
351         bool home_rw = true, srv_rw = true;
352         blkid_partlist pl;
353         int r, k;
354         dev_t pn;
355
356         udev = udev_new();
357         if (!udev)
358                 return log_oom();
359
360         d = udev_device_new_from_devnum(udev, 'b', devnum);
361         if (!d)
362                 return log_oom();
363
364         name = udev_device_get_devnode(d);
365         if (!name)
366                 name = udev_device_get_syspath(d);
367         if (!name) {
368                 log_debug("Device %u:%u does not have a name, ignoring.",
369                           major(devnum), minor(devnum));
370                 return 0;
371         }
372
373         parent = udev_device_get_parent(d);
374         if (!parent) {
375                 log_debug("%s: not a partitioned device, ignoring.", name);
376                 return 0;
377         }
378
379         /* Does it have a devtype? */
380         devtype = udev_device_get_devtype(parent);
381         if (!devtype) {
382                 log_debug("%s: parent doesn't have a device type, ignoring.", name);
383                 return 0;
384         }
385
386         /* Is this a disk or a partition? We only care for disks... */
387         if (!streq(devtype, "disk")) {
388                 log_debug("%s: parent isn't a raw disk, ignoring.", name);
389                 return 0;
390         }
391
392         /* Does it have a device node? */
393         node = udev_device_get_devnode(parent);
394         if (!node) {
395                 log_debug("%s: parent device does not have device node, ignoring.", name);
396                 return 0;
397         }
398
399         log_debug("%s: root device %s.", name, node);
400
401         pn = udev_device_get_devnum(parent);
402         if (major(pn) == 0)
403                 return 0;
404
405         errno = 0;
406         b = blkid_new_probe_from_filename(node);
407         if (!b) {
408                 if (errno == 0)
409                         return log_oom();
410
411                 return log_error_errno(errno, "%s: failed to allocate prober: %m", node);
412         }
413
414         blkid_probe_enable_partitions(b, 1);
415         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
416
417         errno = 0;
418         r = blkid_do_safeprobe(b);
419         if (r == -2 || r == 1) /* no result or uncertain */
420                 return 0;
421         else if (r != 0)
422                 return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node);
423
424         errno = 0;
425         r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
426         if (r != 0)
427                 return log_error_errno(errno ?: EIO,
428                                        "%s: failed to determine partition table type: %m", node);
429
430         /* We only do this all for GPT... */
431         if (!streq_ptr(pttype, "gpt")) {
432                 log_debug("%s: not a GPT partition table, ignoring.", node);
433                 return 0;
434         }
435
436         errno = 0;
437         pl = blkid_probe_get_partitions(b);
438         if (!pl) {
439                 if (errno == 0)
440                         return log_oom();
441
442                 return log_error_errno(errno, "%s: failed to list partitions: %m", node);
443         }
444
445         e = udev_enumerate_new(udev);
446         if (!e)
447                 return log_oom();
448
449         r = udev_enumerate_add_match_parent(e, parent);
450         if (r < 0)
451                 return log_oom();
452
453         r = udev_enumerate_add_match_subsystem(e, "block");
454         if (r < 0)
455                 return log_oom();
456
457         r = udev_enumerate_scan_devices(e);
458         if (r < 0)
459                 return log_error_errno(r, "%s: failed to enumerate partitions: %m", node);
460
461         first = udev_enumerate_get_list_entry(e);
462         udev_list_entry_foreach(item, first) {
463                 _cleanup_udev_device_unref_ struct udev_device *q;
464                 const char *stype, *subnode;
465                 sd_id128_t type_id;
466                 blkid_partition pp;
467                 dev_t qn;
468                 int nr;
469                 unsigned long long flags;
470
471                 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
472                 if (!q)
473                         continue;
474
475                 qn = udev_device_get_devnum(q);
476                 if (major(qn) == 0)
477                         continue;
478
479                 if (qn == devnum)
480                         continue;
481
482                 if (qn == pn)
483                         continue;
484
485                 subnode = udev_device_get_devnode(q);
486                 if (!subnode)
487                         continue;
488
489                 pp = blkid_partlist_devno_to_partition(pl, qn);
490                 if (!pp)
491                         continue;
492
493                 flags = blkid_partition_get_flags(pp);
494
495                 /* Ignore partitions that are not marked for automatic
496                  * mounting on discovery */
497                 if (flags & GPT_FLAG_NO_AUTO)
498                         continue;
499
500                 nr = blkid_partition_get_partno(pp);
501                 if (nr < 0)
502                         continue;
503
504                 stype = blkid_partition_get_type_string(pp);
505                 if (!stype)
506                         continue;
507
508                 if (sd_id128_from_string(stype, &type_id) < 0)
509                         continue;
510
511                 if (sd_id128_equal(type_id, GPT_SWAP)) {
512
513                         if (flags & GPT_FLAG_READ_ONLY) {
514                                 log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode);
515                                 continue;
516                         }
517
518                         k = add_swap(subnode);
519                         if (k < 0)
520                                 r = k;
521
522                 } else if (sd_id128_equal(type_id, GPT_HOME)) {
523
524                         /* We only care for the first /home partition */
525                         if (home && nr >= home_nr)
526                                 continue;
527
528                         home_nr = nr;
529                         home_rw = !(flags & GPT_FLAG_READ_ONLY),
530
531                         free(home);
532                         home = strdup(subnode);
533                         if (!home)
534                                 return log_oom();
535
536                 } else if (sd_id128_equal(type_id, GPT_SRV)) {
537
538                         /* We only care for the first /srv partition */
539                         if (srv && nr >= srv_nr)
540                                 continue;
541
542                         srv_nr = nr;
543                         srv_rw = !(flags & GPT_FLAG_READ_ONLY),
544
545                         free(srv);
546                         srv = strdup(subnode);
547                         if (!srv)
548                                 return log_oom();
549                 }
550         }
551
552         if (home) {
553                 k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
554                 if (k < 0)
555                         r = k;
556         }
557
558         if (srv) {
559                 k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET);
560                 if (k < 0)
561                         r = k;
562         }
563
564         return r;
565 }
566
567 static int get_block_device(const char *path, dev_t *dev) {
568         struct stat st;
569         struct statfs sfs;
570
571         assert(path);
572         assert(dev);
573
574         if (lstat(path, &st))
575                 return -errno;
576
577         if (major(st.st_dev) != 0) {
578                 *dev = st.st_dev;
579                 return 1;
580         }
581
582         if (statfs(path, &sfs) < 0)
583                 return -errno;
584
585         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
586                 return btrfs_get_block_device(path, dev);
587
588         return 0;
589 }
590
591 static int parse_proc_cmdline_item(const char *key, const char *value) {
592         int r;
593
594         assert(key);
595
596         if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) {
597
598                 r = parse_boolean(value);
599                 if (r < 0)
600                         log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
601                 else
602                         arg_enabled = r;
603
604         } else if (streq(key, "root") && value) {
605
606                 /* Disable root disk logic if there's a root= value
607                  * specified (unless it happens to be "gpt-auto") */
608
609                 arg_root_enabled = streq(value, "gpt-auto");
610
611         } else if (streq(key, "rw") && !value)
612                 arg_root_rw = true;
613         else if (streq(key, "ro") && !value)
614                 arg_root_rw = false;
615
616         return 0;
617 }
618
619 static int add_root_mount(void) {
620
621 #ifdef ENABLE_EFI
622         int r;
623
624         if (!is_efi_boot()) {
625                 log_debug("Not a EFI boot, not creating root mount.");
626                 return 0;
627         }
628
629         r = efi_loader_get_device_part_uuid(NULL);
630         if (r == -ENOENT) {
631                 log_debug("EFI loader partition unknown, exiting.");
632                 return 0;
633         } else if (r < 0)
634                 return log_error_errno(r, "Failed to read ESP partition UUID: %m");
635
636         /* OK, we have an ESP partition, this is fantastic, so let's
637          * wait for a root device to show up. A udev rule will create
638          * the link for us under the right name. */
639
640         return add_mount(
641                         "root",
642                         "/dev/gpt-auto-root",
643                         in_initrd() ? "/sysroot" : "/",
644                         NULL,
645                         arg_root_rw,
646                         "Root Partition",
647                         in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
648 #else
649         return 0;
650 #endif
651 }
652
653 static int add_mounts(void) {
654         dev_t devno;
655         int r;
656
657         r = get_block_device("/", &devno);
658         if (r < 0)
659                 return log_error_errno(r, "Failed to determine block device of root file system: %m");
660         else if (r == 0) {
661                 log_debug("Root file system not on a (single) block device.");
662                 return 0;
663         }
664
665         return enumerate_partitions(devno);
666 }
667
668 int main(int argc, char *argv[]) {
669         int r = 0;
670
671         if (argc > 1 && argc != 4) {
672                 log_error("This program takes three or no arguments.");
673                 return EXIT_FAILURE;
674         }
675
676         if (argc > 1)
677                 arg_dest = argv[3];
678
679         log_set_target(LOG_TARGET_SAFE);
680         log_parse_environment();
681         log_open();
682
683         umask(0022);
684
685         if (detect_container(NULL) > 0) {
686                 log_debug("In a container, exiting.");
687                 return EXIT_SUCCESS;
688         }
689
690         r = parse_proc_cmdline(parse_proc_cmdline_item);
691         if (r < 0)
692                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
693
694         if (!arg_enabled) {
695                 log_debug("Disabled, exiting.");
696                 return EXIT_SUCCESS;
697         }
698
699         if (arg_root_enabled)
700                 r = add_root_mount();
701
702         if (!in_initrd()) {
703                 int k;
704
705                 k = add_mounts();
706                 if (k < 0)
707                         r = k;
708         }
709
710         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
711 }