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