chiark / gitweb /
dbus: fix introspection for TimerSlackNSec
[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.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 "special.h"
40 #include "unit-name.h"
41
42 /* TODO:
43  *
44  * - Properly handle cryptsetup partitions
45  * - Define new partition type for encrypted swap
46  * - Make /home automount rather than mount
47  *
48  */
49
50 static const char *arg_dest = "/tmp";
51
52 static inline void blkid_free_probep(blkid_probe *b) {
53         if (*b)
54                 blkid_free_probe(*b);
55 }
56 #define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
57
58 static int verify_gpt_partition(const char *node, sd_id128_t *type, unsigned *nr, char **fstype) {
59         _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
60         const char *v;
61         int r;
62
63         errno = 0;
64         b = blkid_new_probe_from_filename(node);
65         if (!b)
66                 return errno != 0 ? -errno : -ENOMEM;
67
68         blkid_probe_enable_superblocks(b, 1);
69         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
70         blkid_probe_enable_partitions(b, 1);
71         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
72
73         errno = 0;
74         r = blkid_do_safeprobe(b);
75         if (r == -2)
76                 return -ENODEV;
77         else if (r == 1)
78                 return -ENODEV;
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                 char *fst;
116
117                 errno = 0;
118                 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
119                 if (r != 0)
120                         *fstype = NULL;
121                 else {
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;
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         fprintf(f,
202                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
203                 "[Unit]\n"
204                 "DefaultDependencies=no\n"
205                 "After=" SPECIAL_LOCAL_FS_PRE_TARGET "\n"
206                 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
207                 "Before=" SPECIAL_UMOUNT_TARGET " " SPECIAL_LOCAL_FS_TARGET "\n\n"
208                 "[Mount]\n"
209                 "What=%s\n"
210                 "Where=/home\n"
211                 "Type=%s\n"
212                 "FsckPassNo=2\n",
213                 path, fstype);
214
215         fflush(f);
216         if (ferror(f)) {
217                 log_error("Failed to write unit file %s: %m", unit);
218                 return -errno;
219         }
220
221         lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/home.mount", NULL);
222         if (!lnk)
223                 return log_oom();
224
225
226         mkdir_parents_label(lnk, 0755);
227         if (symlink(unit, lnk) < 0) {
228                 log_error("Failed to create symlink %s: %m", lnk);
229                 return -errno;
230         }
231
232         return 0;
233 }
234
235 static int enumerate_partitions(struct udev *udev, dev_t dev) {
236         struct udev_enumerate *e = NULL;
237         struct udev_device *parent = NULL, *d = NULL;
238         struct udev_list_entry *first, *item;
239         unsigned home_nr = (unsigned) -1;
240         _cleanup_free_ char *home = NULL, *home_fstype = NULL;
241         int r;
242
243         e = udev_enumerate_new(udev);
244         if (!e) {
245                 r = log_oom();
246                 goto finish;
247         }
248
249         d = udev_device_new_from_devnum(udev, 'b', dev);
250         if (!d) {
251                 r = log_oom();
252                 goto finish;
253         }
254
255         parent = udev_device_get_parent(d);
256         if (!parent) {
257                 r = log_oom();
258                 goto finish;
259         }
260
261         r = udev_enumerate_add_match_parent(e, parent);
262         if (r < 0) {
263                 r = log_oom();
264                 goto finish;
265         }
266
267         r = udev_enumerate_add_match_subsystem(e, "block");
268         if (r < 0) {
269                 r = log_oom();
270                 goto finish;
271         }
272
273         r = udev_enumerate_scan_devices(e);
274         if (r < 0) {
275                 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s",
276                           major(dev), minor(dev), strerror(-r));
277                 goto finish;
278         }
279
280         first = udev_enumerate_get_list_entry(e);
281         udev_list_entry_foreach(item, first) {
282                 _cleanup_free_ char *fstype = NULL;
283                 const char *node = NULL;
284                 struct udev_device *q;
285                 sd_id128_t type_id;
286                 unsigned nr;
287
288                 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
289                 if (!q) {
290                         r = log_oom();
291                         goto finish;
292                 }
293
294                 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
295                         goto skip;
296
297                 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
298                         goto skip;
299
300                 node = udev_device_get_devnode(q);
301                 if (!node) {
302                         r = log_oom();
303                         goto finish;
304                 }
305
306                 r = verify_gpt_partition(node, &type_id, &nr, &fstype);
307                 if (r < 0) {
308                         log_error("Failed to verify GPT partition %s: %s",
309                                   node, strerror(-r));
310                         udev_device_unref(q);
311                         goto finish;
312                 }
313                 if (r == 0)
314                         goto skip;
315
316                 if (sd_id128_equal(type_id, SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)))
317                         add_swap(node, fstype);
318                 else if (sd_id128_equal(type_id, SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15))) {
319
320                         if (!home || nr < home_nr) {
321                                 free(home);
322                                 home = strdup(node);
323                                 if (!home) {
324                                         r = log_oom();
325                                         goto finish;
326                                 }
327
328                                 home_nr = nr;
329
330                                 free(home_fstype);
331                                 home_fstype = fstype;
332                                 fstype = NULL;
333                         }
334                 }
335
336         skip:
337                 udev_device_unref(q);
338         }
339
340         if (home && home_fstype)
341                 add_home(home, home_fstype);
342
343 finish:
344         if (d)
345                 udev_device_unref(d);
346
347         if (e)
348                 udev_enumerate_unref(e);
349
350
351         return r;
352 }
353
354 static int get_btrfs_block_device(const char *path, dev_t *dev) {
355         struct btrfs_ioctl_fs_info_args fsi;
356         _cleanup_close_ int fd = -1;
357         uint64_t id;
358
359         assert(path);
360         assert(dev);
361
362         fd = open(path, O_DIRECTORY|O_CLOEXEC);
363         if (fd < 0)
364                 return -errno;
365
366         zero(fsi);
367         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
368                 return -errno;
369
370         /* We won't do this for btrfs RAID */
371         if (fsi.num_devices != 1)
372                 return 0;
373
374         for (id = 1; id <= fsi.max_id; id++) {
375                 struct btrfs_ioctl_dev_info_args di;
376                 struct stat st;
377
378                 zero(di);
379                 di.devid = id;
380
381                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
382                         if (errno == ENODEV)
383                                 continue;
384
385                         return -errno;
386                 }
387
388                 if (stat((char*) di.path, &st) < 0)
389                         return -errno;
390
391                 if (!S_ISBLK(st.st_mode))
392                         return -ENODEV;
393
394                 if (major(st.st_rdev) == 0)
395                         return -ENODEV;
396
397                 *dev = st.st_rdev;
398                 return 1;
399         }
400
401         return -ENODEV;
402 }
403
404 static int get_block_device(const char *path, dev_t *dev) {
405         struct stat st;
406         struct statfs sfs;
407
408         assert(path);
409         assert(dev);
410
411         if (lstat("/", &st))
412                 return -errno;
413
414         if (major(st.st_dev) != 0) {
415                 *dev = st.st_dev;
416                 return 1;
417         }
418
419         if (statfs("/", &sfs) < 0)
420                 return -errno;
421
422         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
423                 return get_btrfs_block_device(path, dev);
424
425         return 0;
426 }
427
428 static int devno_to_devnode(struct udev *udev, dev_t devno, char **ret) {
429         struct udev_device *d = NULL;
430         const char *t;
431         char *n;
432         int r;
433
434         d = udev_device_new_from_devnum(udev, 'b', devno);
435         if (!d) {
436                 r = log_oom();
437                 goto finish;
438         }
439
440         t = udev_device_get_devnode(d);
441         if (!t) {
442                 r = -ENODEV;
443                 goto finish;
444         }
445
446         n = strdup(t);
447         if (!n) {
448                 r = -ENOMEM;
449                 goto finish;
450         }
451
452         *ret = n;
453         r = 0;
454
455 finish:
456         if (d)
457                 udev_device_unref(d);
458
459         return r;
460 }
461
462 int main(int argc, char *argv[]) {
463         _cleanup_free_ char *node = NULL;
464         struct udev *udev = NULL;
465         dev_t devno;
466         int r;
467
468         if (argc > 1 && argc != 4) {
469                 log_error("This program takes three or no arguments.");
470                 r = -EINVAL;
471                 goto finish;
472         }
473
474         if (argc > 1)
475                 arg_dest = argv[3];
476
477         log_set_target(LOG_TARGET_SAFE);
478         log_parse_environment();
479         log_open();
480
481         umask(0022);
482
483         if (in_initrd()) {
484                 r = 0;
485                 goto finish;
486         }
487
488         r = get_block_device("/", &devno);
489         if (r < 0) {
490                 log_error("Failed to determine block device of root file system: %s", strerror(-r));
491                 goto finish;
492         }
493         if (r == 0) {
494                 log_debug("Root file system not on a (single) block device.");
495                 goto finish;
496         }
497
498         udev = udev_new();
499         if (!udev) {
500                 r = log_oom();
501                 goto finish;
502         }
503
504         r = devno_to_devnode(udev, devno, &node);
505         if (r < 0) {
506                 log_error("Failed to determine block device node from major/minor: %s", strerror(-r));
507                 goto finish;
508         }
509
510         log_debug("Root device %s.", node);
511
512         r = verify_gpt_partition(node, NULL, NULL, NULL);
513         if (r < 0) {
514                 log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
515                 goto finish;
516         }
517         if (r == 0)
518                 goto finish;
519
520         r = enumerate_partitions(udev, devno);
521
522 finish:
523         if (udev)
524                 udev_unref(udev);
525
526         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
527 }