]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shutdown/detach-md.c
device-util: Declare iterator variables inline
[thirdparty/systemd.git] / src / shutdown / detach-md.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2010 ProFUSION embedded systems
4 ***/
5
6 #include <linux/major.h>
7 #include <linux/raid/md_u.h>
8 #include <sys/ioctl.h>
9 #include <unistd.h>
10
11 #include "sd-device.h"
12
13 #include "alloc-util.h"
14 #include "blockdev-util.h"
15 #include "detach-md.h"
16 #include "device-util.h"
17 #include "devnum-util.h"
18 #include "errno-util.h"
19 #include "fd-util.h"
20 #include "string-util.h"
21
22 typedef struct RaidDevice {
23 char *path;
24 dev_t devnum;
25 LIST_FIELDS(struct RaidDevice, raid_device);
26 } RaidDevice;
27
28 static void raid_device_free(RaidDevice **head, RaidDevice *m) {
29 assert(head);
30 assert(m);
31
32 LIST_REMOVE(raid_device, *head, m);
33
34 free(m->path);
35 free(m);
36 }
37
38 static void raid_device_list_free(RaidDevice **head) {
39 assert(head);
40
41 while (*head)
42 raid_device_free(head, *head);
43 }
44
45 static int md_list_get(RaidDevice **head) {
46 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
47 int r;
48
49 assert(head);
50
51 r = sd_device_enumerator_new(&e);
52 if (r < 0)
53 return r;
54
55 r = sd_device_enumerator_allow_uninitialized(e);
56 if (r < 0)
57 return r;
58
59 r = sd_device_enumerator_add_match_subsystem(e, "block", true);
60 if (r < 0)
61 return r;
62
63 r = sd_device_enumerator_add_match_sysname(e, "md*");
64 if (r < 0)
65 return r;
66
67 /* Filter out partitions. */
68 r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "disk");
69 if (r < 0)
70 return r;
71
72 FOREACH_DEVICE(e, d) {
73 _cleanup_free_ char *p = NULL;
74 const char *dn, *md_level;
75 RaidDevice *m;
76 dev_t devnum;
77
78 if (sd_device_get_devnum(d, &devnum) < 0 ||
79 sd_device_get_devname(d, &dn) < 0)
80 continue;
81
82 r = sd_device_get_property_value(d, "MD_LEVEL", &md_level);
83 if (r < 0) {
84 log_warning_errno(r, "Failed to get MD_LEVEL property for %s, ignoring: %m", dn);
85 continue;
86 }
87
88 /* MD "containers" are a special type of MD devices, used for external metadata. Since it
89 * doesn't provide RAID functionality in itself we don't need to stop it. */
90 if (streq(md_level, "container"))
91 continue;
92
93 p = strdup(dn);
94 if (!p)
95 return -ENOMEM;
96
97 m = new(RaidDevice, 1);
98 if (!m)
99 return -ENOMEM;
100
101 *m = (RaidDevice) {
102 .path = TAKE_PTR(p),
103 .devnum = devnum,
104 };
105
106 LIST_PREPEND(raid_device, *head, m);
107 }
108
109 return 0;
110 }
111
112 static int delete_md(RaidDevice *m) {
113 _cleanup_close_ int fd = -EBADF;
114
115 assert(m);
116 assert(major(m->devnum) != 0);
117 assert(m->path);
118
119 fd = open(m->path, O_RDONLY|O_CLOEXEC|O_EXCL);
120 if (fd < 0)
121 return -errno;
122
123 if (fsync(fd) < 0)
124 log_debug_errno(errno, "Failed to sync MD block device %s, ignoring: %m", m->path);
125
126 return RET_NERRNO(ioctl(fd, STOP_ARRAY, NULL));
127 }
128
129 static int md_points_list_detach(RaidDevice **head, bool *changed, bool last_try) {
130 int n_failed = 0, r;
131 dev_t rootdev = 0, usrdev = 0;
132
133 assert(head);
134 assert(changed);
135
136 (void) get_block_device("/", &rootdev);
137 (void) get_block_device("/usr", &usrdev);
138
139 LIST_FOREACH(raid_device, m, *head) {
140 if ((major(rootdev) != 0 && rootdev == m->devnum) ||
141 (major(usrdev) != 0 && usrdev == m->devnum)) {
142 log_debug("Not detaching MD %s that backs the OS itself, skipping.", m->path);
143 n_failed ++;
144 continue;
145 }
146
147 log_info("Stopping MD %s (" DEVNUM_FORMAT_STR ").", m->path, DEVNUM_FORMAT_VAL(m->devnum));
148 r = delete_md(m);
149 if (r < 0) {
150 log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not stop MD %s: %m", m->path);
151 n_failed++;
152 continue;
153 }
154
155 *changed = true;
156 raid_device_free(head, m);
157 }
158
159 return n_failed;
160 }
161
162 int md_detach_all(bool *changed, bool last_try) {
163 _cleanup_(raid_device_list_free) LIST_HEAD(RaidDevice, md_list_head);
164 int r;
165
166 assert(changed);
167
168 LIST_HEAD_INIT(md_list_head);
169
170 r = md_list_get(&md_list_head);
171 if (r < 0)
172 return r;
173
174 return md_points_list_detach(&md_list_head, changed, last_try);
175 }