]>
Commit | Line | Data |
---|---|---|
9935cf0f MG |
1 | /* |
2 | * mdadm - manage Linux "md" devices aka RAID arrays. | |
3 | * | |
4 | * Copyright (C) 2022 Mateusz Grzonka <mateusz.grzonka@intel.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | ||
21 | #include "mdadm.h" | |
22 | #include "udev.h" | |
23 | #include "md_p.h" | |
24 | #include "md_u.h" | |
25 | #include <sys/wait.h> | |
26 | #include <signal.h> | |
27 | #include <limits.h> | |
28 | #include <syslog.h> | |
1750758c MT |
29 | |
30 | #ifndef NO_LIBUDEV | |
9935cf0f | 31 | #include <libudev.h> |
1750758c | 32 | #endif |
9935cf0f | 33 | |
9f376da6 MG |
34 | static char *unblock_path; |
35 | ||
9935cf0f MG |
36 | /* |
37 | * udev_is_available() - Checks for udev in the system. | |
38 | * | |
39 | * Function looks whether udev directories are available and MDADM_NO_UDEV env defined. | |
40 | * | |
41 | * Return: | |
42 | * true if udev is available, | |
43 | * false if not | |
44 | */ | |
45 | bool udev_is_available(void) | |
46 | { | |
47 | struct stat stb; | |
48 | ||
49 | if (stat("/dev/.udev", &stb) != 0 && | |
50 | stat("/run/udev", &stb) != 0) | |
51 | return false; | |
52 | if (check_env("MDADM_NO_UDEV") == 1) | |
53 | return false; | |
54 | return true; | |
55 | } | |
56 | ||
57 | #ifndef NO_LIBUDEV | |
58 | ||
59 | static struct udev *udev; | |
60 | static struct udev_monitor *udev_monitor; | |
61 | ||
62 | /* | |
63 | * udev_release() - Drops references of udev and udev_monitor. | |
64 | */ | |
65 | static void udev_release(void) | |
66 | { | |
67 | udev_monitor_unref(udev_monitor); | |
68 | udev_unref(udev); | |
69 | } | |
70 | ||
71 | /* | |
72 | * udev_initialize() - Initializes udev and udev_monitor structures. | |
73 | * | |
74 | * Function initializes udev, udev_monitor, and sets udev_monitor filter for block devices. | |
75 | * | |
76 | * Return: | |
77 | * UDEV_STATUS_SUCCESS on success | |
78 | * UDEV_STATUS_ERROR on error | |
79 | * UDEV_STATUS_ERROR_NO_UDEV when udev not available | |
80 | */ | |
81 | static enum udev_status udev_initialize(void) | |
82 | { | |
83 | if (!udev_is_available()) { | |
84 | pr_err("No udev.\n"); | |
85 | return UDEV_STATUS_ERROR_NO_UDEV; | |
86 | } | |
87 | ||
88 | udev = udev_new(); | |
89 | if (!udev) { | |
90 | pr_err("Cannot initialize udev.\n"); | |
91 | return UDEV_STATUS_ERROR; | |
92 | } | |
93 | ||
94 | udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); | |
95 | if (!udev_monitor) { | |
96 | pr_err("Cannot initialize udev monitor.\n"); | |
97 | udev = udev_unref(udev); | |
98 | return UDEV_STATUS_ERROR; | |
99 | } | |
100 | ||
101 | if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", 0) < 0) { | |
102 | pr_err("Cannot add udev monitor event filter for md devices.\n"); | |
103 | udev_release(); | |
104 | return UDEV_STATUS_ERROR; | |
105 | } | |
106 | if (udev_monitor_enable_receiving(udev_monitor) < 0) { | |
107 | pr_err("Cannot enable receiving udev events through udev monitor.\n"); | |
108 | udev_release(); | |
109 | return UDEV_STATUS_ERROR; | |
110 | } | |
111 | atexit(udev_release); | |
112 | return UDEV_STATUS_SUCCESS; | |
113 | } | |
114 | ||
115 | /* | |
116 | * udev_wait_for_events() - Waits for events from udev. | |
117 | * @seconds: Timeout in seconds. | |
118 | * | |
119 | * Function waits udev events, wakes up on event or timeout. | |
120 | * | |
121 | * Return: | |
122 | * UDEV_STATUS_SUCCESS on detected event | |
123 | * UDEV_STATUS_TIMEOUT on timeout | |
124 | * UDEV_STATUS_ERROR on error | |
125 | */ | |
126 | enum udev_status udev_wait_for_events(int seconds) | |
127 | { | |
128 | int fd; | |
129 | fd_set readfds; | |
130 | struct timeval tv; | |
131 | int ret; | |
132 | ||
133 | if (!udev || !udev_monitor) { | |
134 | ret = udev_initialize(); | |
135 | if (ret != UDEV_STATUS_SUCCESS) | |
136 | return ret; | |
137 | } | |
138 | ||
139 | fd = udev_monitor_get_fd(udev_monitor); | |
140 | if (fd < 0) { | |
141 | pr_err("Cannot access file descriptor associated with udev monitor.\n"); | |
142 | return UDEV_STATUS_ERROR; | |
143 | } | |
144 | ||
145 | FD_ZERO(&readfds); | |
146 | FD_SET(fd, &readfds); | |
147 | tv.tv_sec = seconds; | |
148 | tv.tv_usec = 0; | |
149 | ||
150 | if (select(fd + 1, &readfds, NULL, NULL, &tv) > 0 && FD_ISSET(fd, &readfds)) | |
151 | if (udev_monitor_receive_device(udev_monitor)) | |
152 | return UDEV_STATUS_SUCCESS; /* event detected */ | |
153 | return UDEV_STATUS_TIMEOUT; | |
154 | } | |
155 | #endif | |
9f376da6 MG |
156 | |
157 | /* | |
158 | * udev_block() - Block udev from examining newly created arrays. | |
159 | * | |
160 | * When array is created, we don't want udev to examine it immediately. | |
161 | * Function creates /run/mdadm/creating-mdXXX and expects that udev rule | |
162 | * will notice it and act accordingly. | |
163 | * | |
164 | * Return: | |
165 | * UDEV_STATUS_SUCCESS when successfully blocked udev | |
166 | * UDEV_STATUS_ERROR on error | |
167 | */ | |
168 | enum udev_status udev_block(char *devnm) | |
169 | { | |
170 | int fd; | |
171 | char *path = xcalloc(1, BUFSIZ); | |
172 | ||
173 | snprintf(path, BUFSIZ, "/run/mdadm/creating-%s", devnm); | |
174 | ||
175 | fd = open(path, O_CREAT | O_RDWR, 0600); | |
176 | if (!is_fd_valid(fd)) { | |
177 | pr_err("Cannot block udev, error creating blocking file.\n"); | |
178 | pr_err("%s: %s\n", strerror(errno), path); | |
179 | free(path); | |
180 | return UDEV_STATUS_ERROR; | |
181 | } | |
182 | ||
183 | close(fd); | |
184 | unblock_path = path; | |
185 | return UDEV_STATUS_SUCCESS; | |
186 | } | |
187 | ||
188 | /* | |
189 | * udev_unblock() - Unblock udev. | |
190 | */ | |
191 | void udev_unblock(void) | |
192 | { | |
193 | if (unblock_path) | |
194 | unlink(unblock_path); | |
195 | free(unblock_path); | |
196 | unblock_path = NULL; | |
197 | } |