chiark / gitweb /
bus: expose priority field of messages, in preparation for prioq support
[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 "path-util.h"
34 #include "util.h"
35 #include "mkdir.h"
36 #include "missing.h"
37 #include "sd-id128.h"
38 #include "libudev.h"
39 #include "udev-util.h"
40 #include "special.h"
41 #include "unit-name.h"
42 #include "virt.h"
43
44 /* TODO:
45  *
46  * - Properly handle cryptsetup partitions
47  * - Define new partition type for encrypted swap
48  * - Make /home automount rather than mount
49  *
50  */
51
52 #define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
53 #define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
54
55 static const char *arg_dest = "/tmp";
56
57 DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
58 #define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
59
60 static int verify_gpt_partition(const char *node, sd_id128_t *type, unsigned *nr, char **fstype) {
61         _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
62         const char *v;
63         int r;
64
65         errno = 0;
66         b = blkid_new_probe_from_filename(node);
67         if (!b)
68                 return errno != 0 ? -errno : -ENOMEM;
69
70         blkid_probe_enable_superblocks(b, 1);
71         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
72         blkid_probe_enable_partitions(b, 1);
73         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
74
75         errno = 0;
76         r = blkid_do_safeprobe(b);
77         if (r == -2 || r == 1) /* no result or uncertain */
78                 return -EBADSLT;
79         else if (r != 0)
80                 return errno ? -errno : -EIO;
81
82         errno = 0;
83         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
84         if (r != 0)
85                 /* return 0 if we're not on GPT */
86                 return errno ? -errno : 0;
87
88         if (strcmp(v, "gpt") != 0)
89                 return 0;
90
91         if (type) {
92                 errno = 0;
93                 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
94                 if (r != 0)
95                         return errno ? -errno : -EIO;
96
97                 r = sd_id128_from_string(v, type);
98                 if (r < 0)
99                         return r;
100         }
101
102         if (nr) {
103                 errno = 0;
104                 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
105                 if (r != 0)
106                         return errno ? -errno : -EIO;
107
108                 r = safe_atou(v, nr);
109                 if (r < 0)
110                         return r;
111         }
112
113
114         if (fstype) {
115                 errno = 0;
116                 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
117                 if (r != 0)
118                         *fstype = NULL;
119                 else {
120                         char *fst;
121
122                         fst = strdup(v);
123                         if (!fst)
124                                 return -ENOMEM;
125
126                         *fstype = fst;
127                 }
128         }
129
130         return 1;
131 }
132
133 static int add_swap(const char *path, const char *fstype) {
134         _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
135         _cleanup_fclose_ FILE *f = NULL;
136
137         log_debug("Adding swap: %s %s", path, fstype);
138
139         name = unit_name_from_path(path, ".swap");
140         if (!name)
141                 return log_oom();
142
143         unit = strjoin(arg_dest, "/", name, NULL);
144         if (!unit)
145                 return log_oom();
146
147         f = fopen(unit, "wxe");
148         if (!f) {
149                 log_error("Failed to create unit file %s: %m", unit);
150                 return -errno;
151         }
152
153         fprintf(f,
154                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
155                 "[Unit]\n"
156                 "DefaultDependencies=no\n"
157                 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
158                 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_SWAP_TARGET "\n\n"
159                 "[Swap]\n"
160                 "What=%s\n",
161                 path);
162
163         fflush(f);
164         if (ferror(f)) {
165                 log_error("Failed to write unit file %s: %m", unit);
166                 return -errno;
167         }
168
169         lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
170         if (!lnk)
171                 return log_oom();
172
173         mkdir_parents_label(lnk, 0755);
174         if (symlink(unit, lnk) < 0) {
175                 log_error("Failed to create symlink %s: %m", lnk);
176                 return -errno;
177         }
178
179         return 0;
180 }
181
182 static int add_home(const char *path, const char *fstype) {
183         _cleanup_free_ char *unit = NULL, *lnk = NULL, *fsck = NULL;
184         _cleanup_fclose_ FILE *f = NULL;
185
186         if (dir_is_empty("/home") <= 0)
187                 return 0;
188
189         log_debug("Adding home: %s %s", path, fstype);
190
191         unit = strappend(arg_dest, "/home.mount");
192         if (!unit)
193                 return log_oom();
194
195         f = fopen(unit, "wxe");
196         if (!f) {
197                 log_error("Failed to create unit file %s: %m", unit);
198                 return -errno;
199         }
200
201         fsck = unit_name_from_path_instance("systemd-fsck", path, ".service");
202         if (!fsck)
203                 return log_oom();
204
205         fprintf(f,
206                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
207                 "[Unit]\n"
208                 "DefaultDependencies=no\n"
209                 "Requires=%s\n"
210                 "After=" SPECIAL_LOCAL_FS_PRE_TARGET " %s\n"
211                 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
212                 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_LOCAL_FS_TARGET "\n\n"
213                 "[Mount]\n"
214                 "What=%s\n"
215                 "Where=/home\n"
216                 "Type=%s\n",
217                 fsck, fsck, path, fstype);
218
219         fflush(f);
220         if (ferror(f)) {
221                 log_error("Failed to write unit file %s: %m", unit);
222                 return -errno;
223         }
224
225         lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/home.mount", NULL);
226         if (!lnk)
227                 return log_oom();
228
229         mkdir_parents_label(lnk, 0755);
230         if (symlink(unit, lnk) < 0) {
231                 log_error("Failed to create symlink %s: %m", lnk);
232                 return -errno;
233         }
234
235         return 0;
236 }
237
238 static int enumerate_partitions(struct udev *udev, dev_t dev) {
239         struct udev_device *parent = NULL;
240         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
241         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
242         struct udev_list_entry *first, *item;
243         unsigned home_nr = (unsigned) -1;
244         _cleanup_free_ char *home = NULL, *home_fstype = NULL;
245         int r;
246
247         e = udev_enumerate_new(udev);
248         if (!e)
249                 return log_oom();
250
251         d = udev_device_new_from_devnum(udev, 'b', dev);
252         if (!d)
253                 return log_oom();
254
255         parent = udev_device_get_parent(d);
256         if (!parent)
257                 return log_oom();
258
259         r = udev_enumerate_add_match_parent(e, parent);
260         if (r < 0)
261                 return log_oom();
262
263         r = udev_enumerate_add_match_subsystem(e, "block");
264         if (r < 0)
265                 return log_oom();
266
267         r = udev_enumerate_scan_devices(e);
268         if (r < 0) {
269                 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
270                           major(dev), minor(dev), strerror(-r));
271                 return r;
272         }
273
274         first = udev_enumerate_get_list_entry(e);
275         udev_list_entry_foreach(item, first) {
276                 _cleanup_free_ char *fstype = NULL;
277                 const char *node = NULL;
278                 _cleanup_udev_device_unref_ struct udev_device *q;
279                 sd_id128_t type_id;
280                 unsigned nr;
281
282                 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
283                 if (!q)
284                         return log_oom();
285
286                 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
287                         continue;
288
289                 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
290                         continue;
291
292                 node = udev_device_get_devnode(q);
293                 if (!node)
294                         return log_oom();
295
296                 r = verify_gpt_partition(node, &type_id, &nr, &fstype);
297                 if (r < 0) {
298                         /* skip child devices which are not detected properly */
299                         if (r == -EBADSLT)
300                                 continue;
301                         log_error("Failed to verify GPT partition %s: %s",
302                                   node, strerror(-r));
303                         return r;
304                 }
305                 if (r == 0)
306                         continue;
307
308                 if (sd_id128_equal(type_id, GPT_SWAP))
309                         add_swap(node, fstype);
310                 else if (sd_id128_equal(type_id, GPT_HOME)) {
311                         if (!home || nr < home_nr) {
312                                 free(home);
313                                 home = strdup(node);
314                                 if (!home)
315                                         return log_oom();
316
317                                 home_nr = nr;
318
319                                 free(home_fstype);
320                                 home_fstype = fstype;
321                                 fstype = NULL;
322                         }
323                 }
324         }
325
326         if (home && home_fstype)
327                 add_home(home, home_fstype);
328
329         return r;
330 }
331
332 static int get_btrfs_block_device(const char *path, dev_t *dev) {
333         struct btrfs_ioctl_fs_info_args fsi = {};
334         _cleanup_close_ int fd = -1;
335         uint64_t id;
336
337         assert(path);
338         assert(dev);
339
340         fd = open(path, O_DIRECTORY|O_CLOEXEC);
341         if (fd < 0)
342                 return -errno;
343
344         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
345                 return -errno;
346
347         /* We won't do this for btrfs RAID */
348         if (fsi.num_devices != 1)
349                 return 0;
350
351         for (id = 1; id <= fsi.max_id; id++) {
352                 struct btrfs_ioctl_dev_info_args di = {
353                         .devid = id,
354                 };
355                 struct stat st;
356
357                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
358                         if (errno == ENODEV)
359                                 continue;
360
361                         return -errno;
362                 }
363
364                 if (stat((char*) di.path, &st) < 0)
365                         return -errno;
366
367                 if (!S_ISBLK(st.st_mode))
368                         return -ENODEV;
369
370                 if (major(st.st_rdev) == 0)
371                         return -ENODEV;
372
373                 *dev = st.st_rdev;
374                 return 1;
375         }
376
377         return -ENODEV;
378 }
379
380 static int get_block_device(const char *path, dev_t *dev) {
381         struct stat st;
382         struct statfs sfs;
383
384         assert(path);
385         assert(dev);
386
387         if (lstat("/", &st))
388                 return -errno;
389
390         if (major(st.st_dev) != 0) {
391                 *dev = st.st_dev;
392                 return 1;
393         }
394
395         if (statfs("/", &sfs) < 0)
396                 return -errno;
397
398         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
399                 return get_btrfs_block_device(path, dev);
400
401         return 0;
402 }
403
404 static int devno_to_devnode(struct udev *udev, dev_t devno, char **ret) {
405         _cleanup_udev_device_unref_ struct udev_device *d;
406         const char *t;
407         char *n;
408
409         d = udev_device_new_from_devnum(udev, 'b', devno);
410         if (!d)
411                 return log_oom();
412
413         t = udev_device_get_devnode(d);
414         if (!t)
415                 return -ENODEV;
416
417         n = strdup(t);
418         if (!n)
419                 return -ENOMEM;
420
421         *ret = n;
422         return 0;
423 }
424
425 int main(int argc, char *argv[]) {
426         _cleanup_free_ char *node = NULL;
427         _cleanup_udev_unref_ struct udev *udev = NULL;
428         dev_t devno;
429         int r = 0;
430
431         if (argc > 1 && argc != 4) {
432                 log_error("This program takes three or no arguments.");
433                 r = -EINVAL;
434                 goto finish;
435         }
436
437         if (argc > 1)
438                 arg_dest = argv[3];
439
440         log_set_target(LOG_TARGET_SAFE);
441         log_parse_environment();
442         log_open();
443
444         umask(0022);
445
446         if (in_initrd()) {
447                 log_debug("In initrd, exiting.");
448                 goto finish;
449         }
450
451         if (detect_container(NULL) > 0) {
452                 log_debug("In a container, exiting.");
453                 goto finish;
454         }
455
456         r = get_block_device("/", &devno);
457         if (r < 0) {
458                 log_error("Failed to determine block device of root file system: %s", strerror(-r));
459                 goto finish;
460         }
461         if (r == 0) {
462                 log_debug("Root file system not on a (single) block device.");
463                 goto finish;
464         }
465
466         udev = udev_new();
467         if (!udev) {
468                 r = log_oom();
469                 goto finish;
470         }
471
472         r = devno_to_devnode(udev, devno, &node);
473         if (r < 0) {
474                 log_error("Failed to determine block device node from major/minor: %s", strerror(-r));
475                 goto finish;
476         }
477
478         log_debug("Root device %s.", node);
479
480         r = verify_gpt_partition(node, NULL, NULL, NULL);
481         if (r < 0) {
482                 log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
483                 goto finish;
484         }
485         if (r == 0)
486                 goto finish;
487
488         r = enumerate_partitions(udev, devno);
489
490 finish:
491         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
492 }