]>
Commit | Line | Data |
---|---|---|
c81b35c0 | 1 | /* |
55e9959b | 2 | * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org> |
c81b35c0 | 3 | * |
55e9959b KS |
4 | * This program is free software: you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation, either version 2 of the License, or | |
7 | * (at your option) any later version. | |
c81b35c0 | 8 | * |
55e9959b KS |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
c81b35c0 KS |
16 | */ |
17 | ||
c81b35c0 KS |
18 | #include <stdlib.h> |
19 | #include <stdio.h> | |
4a539daf | 20 | #include <stddef.h> |
c81b35c0 | 21 | #include <unistd.h> |
01618658 | 22 | #include <string.h> |
c81b35c0 | 23 | #include <fcntl.h> |
4a539daf | 24 | #include <errno.h> |
9f8dfa19 | 25 | #include <ctype.h> |
3a020a85 KS |
26 | #include <pwd.h> |
27 | #include <grp.h> | |
d0db192f | 28 | #include <sys/wait.h> |
c81b35c0 | 29 | |
c81b35c0 | 30 | #include "udev.h" |
c81b35c0 | 31 | |
54808d77 | 32 | int util_create_path(struct udev *udev, const char *path) |
4a539daf | 33 | { |
a390e6f7 KS |
34 | char p[UTIL_PATH_SIZE]; |
35 | char *pos; | |
36 | struct stat stats; | |
37 | int ret; | |
38 | ||
39 | util_strlcpy(p, path, sizeof(p)); | |
40 | pos = strrchr(p, '/'); | |
41 | if (pos == p || pos == NULL) | |
42 | return 0; | |
43 | ||
44 | while (pos[-1] == '/') | |
45 | pos--; | |
46 | pos[0] = '\0'; | |
47 | ||
48 | dbg(udev, "stat '%s'\n", p); | |
49 | if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) | |
50 | return 0; | |
51 | ||
54808d77 | 52 | if (util_create_path(udev, p) != 0) |
a390e6f7 KS |
53 | return -1; |
54 | ||
55 | dbg(udev, "mkdir '%s'\n", p); | |
56 | udev_selinux_setfscreatecon(udev, p, S_IFDIR|0755); | |
57 | ret = mkdir(p, 0755); | |
58 | udev_selinux_resetfscreatecon(udev); | |
59 | if (ret == 0) | |
60 | return 0; | |
61 | ||
62 | if (errno == EEXIST) | |
63 | if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) | |
64 | return 0; | |
65 | return -1; | |
66 | } | |
4a539daf | 67 | |
54808d77 | 68 | int util_delete_path(struct udev *udev, const char *path) |
a390e6f7 KS |
69 | { |
70 | char p[UTIL_PATH_SIZE]; | |
71 | char *pos; | |
72 | int retval; | |
73 | ||
74 | strcpy (p, path); | |
75 | pos = strrchr(p, '/'); | |
76 | if (pos == p || pos == NULL) | |
77 | return 0; | |
78 | ||
79 | while (1) { | |
80 | *pos = '\0'; | |
81 | pos = strrchr(p, '/'); | |
82 | ||
83 | /* don't remove the last one */ | |
84 | if ((pos == p) || (pos == NULL)) | |
85 | break; | |
86 | ||
87 | /* remove if empty */ | |
88 | retval = rmdir(p); | |
89 | if (errno == ENOENT) | |
90 | retval = 0; | |
91 | if (retval) { | |
92 | if (errno == ENOTEMPTY) | |
93 | return 0; | |
94 | err(udev, "rmdir(%s) failed: %m\n", p); | |
95 | break; | |
b8476286 | 96 | } |
a390e6f7 KS |
97 | dbg(udev, "removed '%s'\n", p); |
98 | } | |
99 | return 0; | |
b8476286 KS |
100 | } |
101 | ||
a390e6f7 KS |
102 | /* Reset permissions on the device node, before unlinking it to make sure, |
103 | * that permisions of possible hard links will be removed too. | |
104 | */ | |
54808d77 | 105 | int util_unlink_secure(struct udev *udev, const char *filename) |
b8476286 | 106 | { |
a390e6f7 | 107 | int retval; |
b8476286 | 108 | |
a390e6f7 KS |
109 | retval = chown(filename, 0, 0); |
110 | if (retval) | |
111 | err(udev, "chown(%s, 0, 0) failed: %m\n", filename); | |
f0308095 | 112 | |
a390e6f7 KS |
113 | retval = chmod(filename, 0000); |
114 | if (retval) | |
115 | err(udev, "chmod(%s, 0000) failed: %m\n", filename); | |
4a539daf | 116 | |
a390e6f7 KS |
117 | retval = unlink(filename); |
118 | if (errno == ENOENT) | |
119 | retval = 0; | |
db6e59df | 120 | |
a390e6f7 KS |
121 | if (retval) |
122 | err(udev, "unlink(%s) failed: %m\n", filename); | |
fb179207 | 123 | |
a390e6f7 | 124 | return retval; |
fb179207 KS |
125 | } |
126 | ||
54808d77 | 127 | uid_t util_lookup_user(struct udev *udev, const char *user) |
3a020a85 | 128 | { |
a391f49d | 129 | char *endptr; |
aaff3d02 AJ |
130 | int buflen = sysconf(_SC_GETPW_R_SIZE_MAX); |
131 | char buf[buflen]; | |
132 | struct passwd pwbuf; | |
3a020a85 | 133 | struct passwd *pw; |
a391f49d | 134 | uid_t uid; |
3a020a85 | 135 | |
20254eb0 KS |
136 | if (strcmp(user, "root") == 0) |
137 | return 0; | |
90024521 KS |
138 | uid = strtoul(user, &endptr, 10); |
139 | if (endptr[0] == '\0') | |
140 | return uid; | |
141 | ||
142 | errno = 0; | |
143 | getpwnam_r(user, &pwbuf, buf, buflen, &pw); | |
144 | if (pw != NULL) | |
145 | return pw->pw_uid; | |
146 | if (errno == 0 || errno == ENOENT || errno == ESRCH) | |
147 | err(udev, "specified user '%s' unknown\n", user); | |
148 | else | |
149 | err(udev, "error resolving user '%s': %m\n", user); | |
150 | return 0; | |
151 | } | |
3a020a85 | 152 | |
90024521 KS |
153 | extern gid_t util_lookup_group(struct udev *udev, const char *group) |
154 | { | |
155 | char *endptr; | |
156 | int buflen = sysconf(_SC_GETGR_R_SIZE_MAX); | |
157 | char buf[buflen]; | |
158 | struct group grbuf; | |
159 | struct group *gr; | |
160 | gid_t gid = 0; | |
3a020a85 | 161 | |
90024521 | 162 | if (strcmp(group, "root") == 0) |
a391f49d | 163 | return 0; |
90024521 KS |
164 | gid = strtoul(group, &endptr, 10); |
165 | if (endptr[0] == '\0') | |
166 | return gid; | |
167 | ||
168 | errno = 0; | |
169 | getgrnam_r(group, &grbuf, buf, buflen, &gr); | |
170 | if (gr != NULL) | |
171 | return gr->gr_gid; | |
172 | if (errno == 0 || errno == ENOENT || errno == ESRCH) | |
173 | err(udev, "specified group '%s' unknown\n", group); | |
174 | else | |
175 | err(udev, "error resolving group '%s': %m\n", group); | |
176 | return 0; | |
177 | } | |
d0db192f | 178 | |
14f40256 KS |
179 | /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */ |
180 | int util_resolve_subsys_kernel(struct udev *udev, const char *string, | |
181 | char *result, size_t maxsize, int read_value) | |
182 | { | |
183 | char temp[UTIL_PATH_SIZE]; | |
184 | char *subsys; | |
185 | char *sysname; | |
186 | struct udev_device *dev; | |
187 | char *attr; | |
188 | ||
189 | if (string == NULL) | |
190 | string = result; | |
191 | if (string[0] != '[') | |
192 | return -1; | |
193 | ||
194 | util_strlcpy(temp, string, sizeof(temp)); | |
195 | ||
196 | subsys = &temp[1]; | |
197 | ||
198 | sysname = strchr(subsys, '/'); | |
199 | if (sysname == NULL) | |
200 | return -1; | |
201 | sysname[0] = '\0'; | |
202 | sysname = &sysname[1]; | |
203 | ||
204 | attr = strchr(sysname, ']'); | |
205 | if (attr == NULL) | |
206 | return -1; | |
207 | attr[0] = '\0'; | |
208 | attr = &attr[1]; | |
209 | if (attr[0] == '/') | |
210 | attr = &attr[1]; | |
211 | if (attr[0] == '\0') | |
212 | attr = NULL; | |
213 | ||
214 | if (read_value && attr == NULL) | |
215 | return -1; | |
216 | ||
217 | dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname); | |
218 | if (dev == NULL) | |
219 | return -1; | |
220 | ||
221 | if (read_value) { | |
222 | const char *val; | |
223 | ||
224 | val = udev_device_get_sysattr_value(dev, attr); | |
225 | if (val != NULL) | |
226 | util_strlcpy(result, val, maxsize); | |
227 | else | |
228 | result[0] = '\0'; | |
229 | info(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); | |
230 | } else { | |
231 | util_strlcpy(result, udev_device_get_syspath(dev), maxsize); | |
232 | if (attr != NULL) { | |
233 | util_strlcat(result, "/", maxsize); | |
234 | util_strlcat(result, attr, maxsize); | |
235 | } | |
236 | info(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); | |
237 | } | |
238 | udev_device_unref(dev); | |
239 | return 0; | |
240 | } | |
241 | ||
54808d77 KS |
242 | int util_run_program(struct udev *udev, const char *command, char **envp, |
243 | char *result, size_t ressize, size_t *reslen) | |
d0db192f KS |
244 | { |
245 | int status; | |
246 | int outpipe[2] = {-1, -1}; | |
247 | int errpipe[2] = {-1, -1}; | |
248 | pid_t pid; | |
249 | char arg[UTIL_PATH_SIZE]; | |
250 | char program[UTIL_PATH_SIZE]; | |
251 | char *argv[(sizeof(arg) / 2) + 1]; | |
252 | int devnull; | |
253 | int i; | |
254 | int err = 0; | |
255 | ||
256 | /* build argv from command */ | |
257 | util_strlcpy(arg, command, sizeof(arg)); | |
258 | i = 0; | |
259 | if (strchr(arg, ' ') != NULL) { | |
260 | char *pos = arg; | |
261 | ||
262 | while (pos != NULL && pos[0] != '\0') { | |
263 | if (pos[0] == '\'') { | |
264 | /* do not separate quotes */ | |
265 | pos++; | |
266 | argv[i] = strsep(&pos, "\'"); | |
267 | while (pos != NULL && pos[0] == ' ') | |
268 | pos++; | |
269 | } else { | |
270 | argv[i] = strsep(&pos, " "); | |
271 | } | |
272 | dbg(udev, "arg[%i] '%s'\n", i, argv[i]); | |
273 | i++; | |
274 | } | |
275 | argv[i] = NULL; | |
276 | } else { | |
277 | argv[0] = arg; | |
278 | argv[1] = NULL; | |
279 | } | |
280 | info(udev, "'%s'\n", command); | |
281 | ||
282 | /* prepare pipes from child to parent */ | |
283 | if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { | |
284 | if (pipe(outpipe) != 0) { | |
285 | err(udev, "pipe failed: %m\n"); | |
286 | return -1; | |
287 | } | |
288 | } | |
289 | if (udev_get_log_priority(udev) >= LOG_INFO) { | |
290 | if (pipe(errpipe) != 0) { | |
291 | err(udev, "pipe failed: %m\n"); | |
292 | return -1; | |
293 | } | |
294 | } | |
295 | ||
296 | /* allow programs in /lib/udev/ to be called without the path */ | |
297 | if (strchr(argv[0], '/') == NULL) { | |
298 | util_strlcpy(program, UDEV_PREFIX "/lib/udev/", sizeof(program)); | |
299 | util_strlcat(program, argv[0], sizeof(program)); | |
300 | argv[0] = program; | |
301 | } | |
302 | ||
303 | pid = fork(); | |
304 | switch(pid) { | |
305 | case 0: | |
306 | /* child closes parent ends of pipes */ | |
307 | if (outpipe[READ_END] > 0) | |
308 | close(outpipe[READ_END]); | |
309 | if (errpipe[READ_END] > 0) | |
310 | close(errpipe[READ_END]); | |
311 | ||
312 | /* discard child output or connect to pipe */ | |
313 | devnull = open("/dev/null", O_RDWR); | |
314 | if (devnull > 0) { | |
315 | dup2(devnull, STDIN_FILENO); | |
316 | if (outpipe[WRITE_END] < 0) | |
317 | dup2(devnull, STDOUT_FILENO); | |
318 | if (errpipe[WRITE_END] < 0) | |
319 | dup2(devnull, STDERR_FILENO); | |
320 | close(devnull); | |
321 | } else | |
322 | err(udev, "open /dev/null failed: %m\n"); | |
323 | if (outpipe[WRITE_END] > 0) { | |
324 | dup2(outpipe[WRITE_END], STDOUT_FILENO); | |
325 | close(outpipe[WRITE_END]); | |
326 | } | |
327 | if (errpipe[WRITE_END] > 0) { | |
328 | dup2(errpipe[WRITE_END], STDERR_FILENO); | |
329 | close(errpipe[WRITE_END]); | |
330 | } | |
331 | execve(argv[0], argv, envp); | |
332 | if (errno == ENOENT || errno == ENOTDIR) { | |
333 | /* may be on a filesytem which is not mounted right now */ | |
334 | info(udev, "program '%s' not found\n", argv[0]); | |
335 | } else { | |
336 | /* other problems */ | |
337 | err(udev, "exec of program '%s' failed\n", argv[0]); | |
338 | } | |
339 | _exit(1); | |
340 | case -1: | |
341 | err(udev, "fork of '%s' failed: %m\n", argv[0]); | |
342 | return -1; | |
343 | default: | |
344 | /* read from child if requested */ | |
345 | if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { | |
346 | ssize_t count; | |
347 | size_t respos = 0; | |
348 | ||
349 | /* parent closes child ends of pipes */ | |
350 | if (outpipe[WRITE_END] > 0) | |
351 | close(outpipe[WRITE_END]); | |
352 | if (errpipe[WRITE_END] > 0) | |
353 | close(errpipe[WRITE_END]); | |
354 | ||
355 | /* read child output */ | |
356 | while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { | |
357 | int fdcount; | |
358 | fd_set readfds; | |
359 | ||
360 | FD_ZERO(&readfds); | |
361 | if (outpipe[READ_END] > 0) | |
362 | FD_SET(outpipe[READ_END], &readfds); | |
363 | if (errpipe[READ_END] > 0) | |
364 | FD_SET(errpipe[READ_END], &readfds); | |
365 | fdcount = select(UDEV_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL); | |
366 | if (fdcount < 0) { | |
367 | if (errno == EINTR) | |
368 | continue; | |
369 | err = -1; | |
370 | break; | |
371 | } | |
372 | ||
373 | /* get stdout */ | |
374 | if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) { | |
375 | char inbuf[1024]; | |
376 | char *pos; | |
377 | char *line; | |
378 | ||
379 | count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1); | |
380 | if (count <= 0) { | |
381 | close(outpipe[READ_END]); | |
382 | outpipe[READ_END] = -1; | |
383 | if (count < 0) { | |
384 | err(udev, "stdin read failed: %m\n"); | |
385 | err = -1; | |
386 | } | |
387 | continue; | |
388 | } | |
389 | inbuf[count] = '\0'; | |
390 | ||
391 | /* store result for rule processing */ | |
392 | if (result) { | |
393 | if (respos + count < ressize) { | |
394 | memcpy(&result[respos], inbuf, count); | |
395 | respos += count; | |
396 | } else { | |
397 | err(udev, "ressize %ld too short\n", (long)ressize); | |
398 | err = -1; | |
399 | } | |
400 | } | |
401 | pos = inbuf; | |
402 | while ((line = strsep(&pos, "\n"))) | |
403 | if (pos || line[0] != '\0') | |
404 | info(udev, "'%s' (stdout) '%s'\n", argv[0], line); | |
405 | } | |
406 | ||
407 | /* get stderr */ | |
408 | if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) { | |
409 | char errbuf[1024]; | |
410 | char *pos; | |
411 | char *line; | |
412 | ||
413 | count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1); | |
414 | if (count <= 0) { | |
415 | close(errpipe[READ_END]); | |
416 | errpipe[READ_END] = -1; | |
417 | if (count < 0) | |
418 | err(udev, "stderr read failed: %m\n"); | |
419 | continue; | |
420 | } | |
421 | errbuf[count] = '\0'; | |
422 | pos = errbuf; | |
423 | while ((line = strsep(&pos, "\n"))) | |
424 | if (pos || line[0] != '\0') | |
425 | info(udev, "'%s' (stderr) '%s'\n", argv[0], line); | |
426 | } | |
427 | } | |
428 | if (outpipe[READ_END] > 0) | |
429 | close(outpipe[READ_END]); | |
430 | if (errpipe[READ_END] > 0) | |
431 | close(errpipe[READ_END]); | |
432 | ||
433 | /* return the childs stdout string */ | |
434 | if (result) { | |
435 | result[respos] = '\0'; | |
436 | dbg(udev, "result='%s'\n", result); | |
437 | if (reslen) | |
438 | *reslen = respos; | |
439 | } | |
440 | } | |
441 | waitpid(pid, &status, 0); | |
442 | if (WIFEXITED(status)) { | |
443 | info(udev, "'%s' returned with status %i\n", argv[0], WEXITSTATUS(status)); | |
444 | if (WEXITSTATUS(status) != 0) | |
445 | err = -1; | |
446 | } else { | |
c4e63d54 | 447 | err(udev, "'%s' abnormal exit\n", command); |
d0db192f KS |
448 | err = -1; |
449 | } | |
450 | } | |
451 | return err; | |
452 | } |