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