]>
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> | |
29 | #include <libudev.h> | |
30 | ||
9f376da6 MG |
31 | static char *unblock_path; |
32 | ||
9935cf0f MG |
33 | /* |
34 | * udev_is_available() - Checks for udev in the system. | |
35 | * | |
36 | * Function looks whether udev directories are available and MDADM_NO_UDEV env defined. | |
37 | * | |
38 | * Return: | |
39 | * true if udev is available, | |
40 | * false if not | |
41 | */ | |
42 | bool udev_is_available(void) | |
43 | { | |
44 | struct stat stb; | |
45 | ||
46 | if (stat("/dev/.udev", &stb) != 0 && | |
47 | stat("/run/udev", &stb) != 0) | |
48 | return false; | |
49 | if (check_env("MDADM_NO_UDEV") == 1) | |
50 | return false; | |
51 | return true; | |
52 | } | |
53 | ||
54 | #ifndef NO_LIBUDEV | |
55 | ||
56 | static struct udev *udev; | |
57 | static struct udev_monitor *udev_monitor; | |
58 | ||
59 | /* | |
60 | * udev_release() - Drops references of udev and udev_monitor. | |
61 | */ | |
62 | static void udev_release(void) | |
63 | { | |
64 | udev_monitor_unref(udev_monitor); | |
65 | udev_unref(udev); | |
66 | } | |
67 | ||
68 | /* | |
69 | * udev_initialize() - Initializes udev and udev_monitor structures. | |
70 | * | |
71 | * Function initializes udev, udev_monitor, and sets udev_monitor filter for block devices. | |
72 | * | |
73 | * Return: | |
74 | * UDEV_STATUS_SUCCESS on success | |
75 | * UDEV_STATUS_ERROR on error | |
76 | * UDEV_STATUS_ERROR_NO_UDEV when udev not available | |
77 | */ | |
78 | static enum udev_status udev_initialize(void) | |
79 | { | |
80 | if (!udev_is_available()) { | |
81 | pr_err("No udev.\n"); | |
82 | return UDEV_STATUS_ERROR_NO_UDEV; | |
83 | } | |
84 | ||
85 | udev = udev_new(); | |
86 | if (!udev) { | |
87 | pr_err("Cannot initialize udev.\n"); | |
88 | return UDEV_STATUS_ERROR; | |
89 | } | |
90 | ||
91 | udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); | |
92 | if (!udev_monitor) { | |
93 | pr_err("Cannot initialize udev monitor.\n"); | |
94 | udev = udev_unref(udev); | |
95 | return UDEV_STATUS_ERROR; | |
96 | } | |
97 | ||
98 | if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", 0) < 0) { | |
99 | pr_err("Cannot add udev monitor event filter for md devices.\n"); | |
100 | udev_release(); | |
101 | return UDEV_STATUS_ERROR; | |
102 | } | |
103 | if (udev_monitor_enable_receiving(udev_monitor) < 0) { | |
104 | pr_err("Cannot enable receiving udev events through udev monitor.\n"); | |
105 | udev_release(); | |
106 | return UDEV_STATUS_ERROR; | |
107 | } | |
108 | atexit(udev_release); | |
109 | return UDEV_STATUS_SUCCESS; | |
110 | } | |
111 | ||
112 | /* | |
113 | * udev_wait_for_events() - Waits for events from udev. | |
114 | * @seconds: Timeout in seconds. | |
115 | * | |
116 | * Function waits udev events, wakes up on event or timeout. | |
117 | * | |
118 | * Return: | |
119 | * UDEV_STATUS_SUCCESS on detected event | |
120 | * UDEV_STATUS_TIMEOUT on timeout | |
121 | * UDEV_STATUS_ERROR on error | |
122 | */ | |
123 | enum udev_status udev_wait_for_events(int seconds) | |
124 | { | |
125 | int fd; | |
126 | fd_set readfds; | |
127 | struct timeval tv; | |
128 | int ret; | |
129 | ||
130 | if (!udev || !udev_monitor) { | |
131 | ret = udev_initialize(); | |
132 | if (ret != UDEV_STATUS_SUCCESS) | |
133 | return ret; | |
134 | } | |
135 | ||
136 | fd = udev_monitor_get_fd(udev_monitor); | |
137 | if (fd < 0) { | |
138 | pr_err("Cannot access file descriptor associated with udev monitor.\n"); | |
139 | return UDEV_STATUS_ERROR; | |
140 | } | |
141 | ||
142 | FD_ZERO(&readfds); | |
143 | FD_SET(fd, &readfds); | |
144 | tv.tv_sec = seconds; | |
145 | tv.tv_usec = 0; | |
146 | ||
147 | if (select(fd + 1, &readfds, NULL, NULL, &tv) > 0 && FD_ISSET(fd, &readfds)) | |
148 | if (udev_monitor_receive_device(udev_monitor)) | |
149 | return UDEV_STATUS_SUCCESS; /* event detected */ | |
150 | return UDEV_STATUS_TIMEOUT; | |
151 | } | |
152 | #endif | |
9f376da6 MG |
153 | |
154 | /* | |
155 | * udev_block() - Block udev from examining newly created arrays. | |
156 | * | |
157 | * When array is created, we don't want udev to examine it immediately. | |
158 | * Function creates /run/mdadm/creating-mdXXX and expects that udev rule | |
159 | * will notice it and act accordingly. | |
160 | * | |
161 | * Return: | |
162 | * UDEV_STATUS_SUCCESS when successfully blocked udev | |
163 | * UDEV_STATUS_ERROR on error | |
164 | */ | |
165 | enum udev_status udev_block(char *devnm) | |
166 | { | |
167 | int fd; | |
168 | char *path = xcalloc(1, BUFSIZ); | |
169 | ||
170 | snprintf(path, BUFSIZ, "/run/mdadm/creating-%s", devnm); | |
171 | ||
172 | fd = open(path, O_CREAT | O_RDWR, 0600); | |
173 | if (!is_fd_valid(fd)) { | |
174 | pr_err("Cannot block udev, error creating blocking file.\n"); | |
175 | pr_err("%s: %s\n", strerror(errno), path); | |
176 | free(path); | |
177 | return UDEV_STATUS_ERROR; | |
178 | } | |
179 | ||
180 | close(fd); | |
181 | unblock_path = path; | |
182 | return UDEV_STATUS_SUCCESS; | |
183 | } | |
184 | ||
185 | /* | |
186 | * udev_unblock() - Unblock udev. | |
187 | */ | |
188 | void udev_unblock(void) | |
189 | { | |
190 | if (unblock_path) | |
191 | unlink(unblock_path); | |
192 | free(unblock_path); | |
193 | unblock_path = NULL; | |
194 | } |