chiark / gitweb /
generators: add Documentation= fields that point to the generator man pages
[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 <fcntl.h>
25 #include <sys/ioctl.h>
26 #include <sys/statfs.h>
27 #include <blkid/blkid.h>
28
29 #ifdef HAVE_LINUX_BTRFS_H
30 #include <linux/btrfs.h>
31 #endif
32
33 #include "sd-id128.h"
34 #include "libudev.h"
35 #include "path-util.h"
36 #include "util.h"
37 #include "mkdir.h"
38 #include "missing.h"
39 #include "udev-util.h"
40 #include "special.h"
41 #include "unit-name.h"
42 #include "virt.h"
43 #include "generator.h"
44 #include "gpt.h"
45 #include "fileio.h"
46
47 static const char *arg_dest = "/tmp";
48
49 DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
50 #define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
51
52 static int verify_gpt_partition(const char *node, sd_id128_t *type, unsigned *nr, char **fstype) {
53         _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
54         const char *v;
55         int r;
56
57         errno = 0;
58         b = blkid_new_probe_from_filename(node);
59         if (!b)
60                 return errno != 0 ? -errno : -ENOMEM;
61
62         blkid_probe_enable_superblocks(b, 1);
63         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
64         blkid_probe_enable_partitions(b, 1);
65         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
66
67         errno = 0;
68         r = blkid_do_safeprobe(b);
69         if (r == -2 || r == 1) /* no result or uncertain */
70                 return -EBADSLT;
71         else if (r != 0)
72                 return errno ? -errno : -EIO;
73
74         errno = 0;
75         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
76         if (r != 0)
77                 /* return 0 if we're not on GPT */
78                 return errno ? -errno : 0;
79
80         if (strcmp(v, "gpt") != 0)
81                 return 0;
82
83         if (type) {
84                 errno = 0;
85                 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
86                 if (r != 0)
87                         return errno ? -errno : -EIO;
88
89                 r = sd_id128_from_string(v, type);
90                 if (r < 0)
91                         return r;
92         }
93
94         if (nr) {
95                 errno = 0;
96                 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
97                 if (r != 0)
98                         return errno ? -errno : -EIO;
99
100                 r = safe_atou(v, nr);
101                 if (r < 0)
102                         return r;
103         }
104
105
106         if (fstype) {
107                 errno = 0;
108                 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
109                 if (r != 0)
110                         *fstype = NULL;
111                 else {
112                         char *fst;
113
114                         fst = strdup(v);
115                         if (!fst)
116                                 return -ENOMEM;
117
118                         *fstype = fst;
119                 }
120         }
121
122         return 1;
123 }
124
125 static int add_swap(const char *path, const char *fstype) {
126         _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
127         _cleanup_fclose_ FILE *f = NULL;
128
129         log_debug("Adding swap: %s %s", path, fstype);
130
131         name = unit_name_from_path(path, ".swap");
132         if (!name)
133                 return log_oom();
134
135         unit = strjoin(arg_dest, "/", name, NULL);
136         if (!unit)
137                 return log_oom();
138
139         f = fopen(unit, "wxe");
140         if (!f) {
141                 log_error("Failed to create unit file %s: %m", unit);
142                 return -errno;
143         }
144
145         fprintf(f,
146                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
147                 "[Unit]\n"
148                 "Description=Swap Partition\n"
149                 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
150                 "[Swap]\n"
151                 "What=%s\n",
152                 path);
153
154         fflush(f);
155         if (ferror(f)) {
156                 log_error("Failed to write unit file %s: %m", unit);
157                 return -errno;
158         }
159
160         lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
161         if (!lnk)
162                 return log_oom();
163
164         mkdir_parents_label(lnk, 0755);
165         if (symlink(unit, lnk) < 0) {
166                 log_error("Failed to create symlink %s: %m", lnk);
167                 return -errno;
168         }
169
170         return 0;
171 }
172
173 static int add_cryptsetup(const char *id, const char *what, char **device) {
174         _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
175         _cleanup_fclose_ FILE *f = NULL;
176         char *from, *ret;
177         int r;
178
179         assert(id);
180         assert(what);
181         assert(device);
182
183         d = unit_name_from_path(what, ".device");
184         if (!d)
185                 return log_oom();
186
187         e = unit_name_escape(id);
188         if (!e)
189                 return log_oom();
190
191         n = unit_name_build("systemd-cryptsetup", e, ".service");
192         if (!n)
193                 return log_oom();
194
195         p = strjoin(arg_dest, "/", n, NULL);
196         if (!n)
197                 return log_oom();
198
199         f = fopen(p, "wxe");
200         if (!f) {
201                 log_error("Failed to create unit file %s: %m", p);
202                 return -errno;
203         }
204
205         fprintf(f,
206                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
207                 "[Unit]\n"
208                 "Description=Cryptography Setup for %%I\n"
209                 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
210                 "DefaultDependencies=no\n"
211                 "Conflicts=umount.target\n"
212                 "BindsTo=dev-mapper-%%i.device %s\n"
213                 "Before=umount.target cryptsetup.target\n"
214                 "After=%s\n"
215                 "IgnoreOnIsolate=true\n"
216                 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n\n"
217                 "[Service]\n"
218                 "Type=oneshot\n"
219                 "RemainAfterExit=yes\n"
220                 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
221                 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s'\n"
222                 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
223                 d, d,
224                 id, what,
225                 id);
226
227         fflush(f);
228         if (ferror(f)) {
229                 log_error("Failed to write file %s: %m", p);
230                 return -errno;
231         }
232
233         from = strappenda("../", n);
234
235         to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
236         if (!to)
237                 return log_oom();
238
239         mkdir_parents_label(to, 0755);
240         if (symlink(from, to) < 0) {
241                 log_error("Failed to create symlink %s: %m", to);
242                 return -errno;
243         }
244
245         free(to);
246         to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
247         if (!to)
248                 return log_oom();
249
250         mkdir_parents_label(to, 0755);
251         if (symlink(from, to) < 0) {
252                 log_error("Failed to create symlink %s: %m", to);
253                 return -errno;
254         }
255
256         free(to);
257         to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
258         if (!to)
259                 return log_oom();
260
261         mkdir_parents_label(to, 0755);
262         if (symlink(from, to) < 0) {
263                 log_error("Failed to create symlink %s: %m", to);
264                 return -errno;
265         }
266
267         free(p);
268         p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
269         if (!p)
270                 return log_oom();
271
272         mkdir_parents_label(p, 0755);
273         r = write_string_file(p,
274                         "# Automatically generated by systemd-gpt-auto-generator\n\n"
275                         "[Unit]\n"
276                         "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
277         if (r < 0) {
278                 log_error("Failed to write device drop-in: %s", strerror(-r));
279                 return r;
280         }
281
282         ret = strappend("/dev/mapper/", id);
283         if (!ret)
284                 return log_oom();
285
286         *device = ret;
287         return 0;
288 }
289
290 static int add_mount(const char *id, const char *what, const char *where, const char *fstype, const char *description) {
291         _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL;
292         _cleanup_fclose_ FILE *f = NULL;
293         int r;
294
295         assert(id);
296         assert(what);
297         assert(where);
298         assert(fstype);
299         assert(description);
300
301         if (dir_is_empty(where) <= 0) {
302                 log_debug("%s already populated, ignoring.", where);
303                 return 0;
304         }
305
306         log_debug("Adding %s: %s %s", where, what, fstype);
307
308         if (streq(fstype, "crypto_LUKS")) {
309
310                 r = add_cryptsetup(id, what, &crypto_what);
311                 if (r < 0)
312                         return r;
313
314                 what = crypto_what;
315                 fstype = NULL;
316         }
317
318         unit = unit_name_from_path(where, ".mount");
319         if (!unit)
320                 return log_oom();
321
322         p = strjoin(arg_dest, "/", unit, NULL);
323         if (!p)
324                 return log_oom();
325
326         f = fopen(p, "wxe");
327         if (!f) {
328                 log_error("Failed to create unit file %s: %m", unit);
329                 return -errno;
330         }
331
332         fprintf(f,
333                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
334                 "[Unit]\n"
335                 "Description=%s\n"
336                 "Documentation=man:systemd-gpt-auto-generator(8)\n",
337                 description);
338
339         r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
340         if (r < 0)
341                 return r;
342
343         fprintf(f,
344                 "\n"
345                 "[Mount]\n"
346                 "What=%s\n"
347                 "Where=%s\n",
348                 what, where);
349
350         if (fstype) {
351                 fprintf(f,
352                         "Type=%s\n",
353                         fstype);
354         }
355
356         fflush(f);
357         if (ferror(f)) {
358                 log_error("Failed to write unit file %s: %m", unit);
359                 return -errno;
360         }
361
362         lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/", unit, NULL);
363         if (!lnk)
364                 return log_oom();
365
366         mkdir_parents_label(lnk, 0755);
367         if (symlink(unit, lnk) < 0) {
368                 log_error("Failed to create symlink %s: %m", lnk);
369                 return -errno;
370         }
371
372         return 0;
373 }
374
375 static int enumerate_partitions(struct udev *udev, dev_t dev) {
376         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
377         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
378         _cleanup_free_ char *home = NULL, *home_fstype = NULL, *srv = NULL, *srv_fstype = NULL;
379         unsigned home_nr = (unsigned) -1, srv_nr = (unsigned )-1;
380         struct udev_list_entry *first, *item;
381         struct udev_device *parent = NULL;
382         int r;
383
384         e = udev_enumerate_new(udev);
385         if (!e)
386                 return log_oom();
387
388         d = udev_device_new_from_devnum(udev, 'b', dev);
389         if (!d)
390                 return log_oom();
391
392         parent = udev_device_get_parent(d);
393         if (!parent)
394                 return log_oom();
395
396         r = udev_enumerate_add_match_parent(e, parent);
397         if (r < 0)
398                 return log_oom();
399
400         r = udev_enumerate_add_match_subsystem(e, "block");
401         if (r < 0)
402                 return log_oom();
403
404         r = udev_enumerate_scan_devices(e);
405         if (r < 0) {
406                 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s", major(dev), minor(dev), strerror(-r));
407                 return r;
408         }
409
410         first = udev_enumerate_get_list_entry(e);
411         udev_list_entry_foreach(item, first) {
412                 _cleanup_udev_device_unref_ struct udev_device *q;
413                 _cleanup_free_ char *fstype = NULL;
414                 const char *node = NULL;
415                 sd_id128_t type_id;
416                 unsigned nr;
417
418                 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
419                 if (!q)
420                         return log_oom();
421
422                 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
423                         continue;
424
425                 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
426                         continue;
427
428                 node = udev_device_get_devnode(q);
429                 if (!node)
430                         return log_oom();
431
432                 r = verify_gpt_partition(node, &type_id, &nr, &fstype);
433                 if (r < 0) {
434                         /* skip child devices which are not detected properly */
435                         if (r == -EBADSLT)
436                                 continue;
437                         log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
438                         return r;
439                 }
440                 if (r == 0)
441                         continue;
442
443                 if (sd_id128_equal(type_id, GPT_SWAP))
444                         add_swap(node, fstype);
445
446                 else if (sd_id128_equal(type_id, GPT_HOME)) {
447
448                         /* We only care for the first /home partition */
449                         if (home && nr >= home_nr)
450                                 continue;
451
452                         home_nr = nr;
453
454                         free(home);
455                         home = strdup(node);
456                         if (!home)
457                                 return log_oom();
458
459                         free(home_fstype);
460                         home_fstype = fstype;
461                         fstype = NULL;
462
463                 } else if (sd_id128_equal(type_id, GPT_SRV)) {
464
465                         /* We only care for the first /srv partition */
466                         if (srv && nr >= srv_nr)
467                                 continue;
468
469                         srv_nr = nr;
470
471                         free(srv);
472                         srv = strdup(node);
473                         if (!srv)
474                                 return log_oom();
475
476                         free(srv_fstype);
477                         srv_fstype = fstype;
478                         fstype = NULL;
479                 }
480         }
481
482         if (home && home_fstype)
483                 add_mount("home", home, "/home", home_fstype, "Home Partition");
484
485         if (srv && srv_fstype)
486                 add_mount("srv", srv, "/srv", srv_fstype, "Server Data Partition");
487
488         return r;
489 }
490
491 static int get_btrfs_block_device(const char *path, dev_t *dev) {
492         struct btrfs_ioctl_fs_info_args fsi = {};
493         _cleanup_close_ int fd = -1;
494         uint64_t id;
495
496         assert(path);
497         assert(dev);
498
499         fd = open(path, O_DIRECTORY|O_CLOEXEC);
500         if (fd < 0)
501                 return -errno;
502
503         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
504                 return -errno;
505
506         /* We won't do this for btrfs RAID */
507         if (fsi.num_devices != 1)
508                 return 0;
509
510         for (id = 1; id <= fsi.max_id; id++) {
511                 struct btrfs_ioctl_dev_info_args di = {
512                         .devid = id,
513                 };
514                 struct stat st;
515
516                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
517                         if (errno == ENODEV)
518                                 continue;
519
520                         return -errno;
521                 }
522
523                 if (stat((char*) di.path, &st) < 0)
524                         return -errno;
525
526                 if (!S_ISBLK(st.st_mode))
527                         return -ENODEV;
528
529                 if (major(st.st_rdev) == 0)
530                         return -ENODEV;
531
532                 *dev = st.st_rdev;
533                 return 1;
534         }
535
536         return -ENODEV;
537 }
538
539 static int get_block_device(const char *path, dev_t *dev) {
540         struct stat st;
541         struct statfs sfs;
542
543         assert(path);
544         assert(dev);
545
546         if (lstat(path, &st))
547                 return -errno;
548
549         if (major(st.st_dev) != 0) {
550                 *dev = st.st_dev;
551                 return 1;
552         }
553
554         if (statfs(path, &sfs) < 0)
555                 return -errno;
556
557         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
558                 return get_btrfs_block_device(path, dev);
559
560         return 0;
561 }
562
563 static int devno_to_devnode(struct udev *udev, dev_t devno, char **ret) {
564         _cleanup_udev_device_unref_ struct udev_device *d;
565         const char *t;
566         char *n;
567
568         d = udev_device_new_from_devnum(udev, 'b', devno);
569         if (!d)
570                 return log_oom();
571
572         t = udev_device_get_devnode(d);
573         if (!t)
574                 return -ENODEV;
575
576         n = strdup(t);
577         if (!n)
578                 return -ENOMEM;
579
580         *ret = n;
581         return 0;
582 }
583
584 int main(int argc, char *argv[]) {
585         _cleanup_udev_unref_ struct udev *udev = NULL;
586         _cleanup_free_ char *node = NULL;
587         dev_t devno;
588         int r = 0;
589
590         if (argc > 1 && argc != 4) {
591                 log_error("This program takes three or no arguments.");
592                 return EXIT_FAILURE;
593         }
594
595         if (argc > 1)
596                 arg_dest = argv[3];
597
598         log_set_target(LOG_TARGET_SAFE);
599         log_parse_environment();
600         log_open();
601
602         umask(0022);
603
604         if (in_initrd()) {
605                 log_debug("In initrd, exiting.");
606                 return EXIT_SUCCESS;
607         }
608
609         if (detect_container(NULL) > 0) {
610                 log_debug("In a container, exiting.");
611                 return EXIT_SUCCESS;
612         }
613
614         r = get_block_device("/", &devno);
615         if (r < 0) {
616                 log_error("Failed to determine block device of root file system: %s", strerror(-r));
617                 return EXIT_FAILURE;
618         } else if (r == 0) {
619                 log_debug("Root file system not on a (single) block device.");
620                 return EXIT_SUCCESS;
621         }
622
623         udev = udev_new();
624         if (!udev) {
625                 log_oom();
626                 return EXIT_FAILURE;
627         }
628
629         r = devno_to_devnode(udev, devno, &node);
630         if (r < 0) {
631                 log_error("Failed to determine block device node from major/minor: %s", strerror(-r));
632                 return EXIT_FAILURE;
633         }
634
635         log_debug("Root device %s.", node);
636
637         r = verify_gpt_partition(node, NULL, NULL, NULL);
638         if (r < 0) {
639                 log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
640                 return EXIT_FAILURE;
641         }
642         if (r == 0) {
643                 log_debug("Not a GPT disk, exiting.");
644                 return EXIT_SUCCESS;
645         }
646
647         r = enumerate_partitions(udev, devno);
648         if (r < 0)
649                 return EXIT_FAILURE;
650
651         return EXIT_SUCCESS;
652 }