]>
Commit | Line | Data |
---|---|---|
c81b35c0 KS |
1 | /* |
2 | * udev_lib - generic stuff used by udev | |
3 | * | |
4 | * Copyright (C) 2004 Kay Sievers <kay@vrfy.org> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation version 2 of the License. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; if not, write to the Free Software Foundation, Inc., | |
17 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | * | |
19 | */ | |
20 | ||
21 | ||
22 | #include <stdlib.h> | |
23 | #include <stdio.h> | |
4a539daf | 24 | #include <stddef.h> |
c81b35c0 KS |
25 | #include <unistd.h> |
26 | #include <fcntl.h> | |
4a539daf | 27 | #include <errno.h> |
9f8dfa19 | 28 | #include <ctype.h> |
4a539daf | 29 | #include <dirent.h> |
fb39f056 | 30 | #include <sys/wait.h> |
c81b35c0 KS |
31 | #include <sys/stat.h> |
32 | #include <sys/mman.h> | |
d402af7d | 33 | #include <sys/utsname.h> |
c81b35c0 | 34 | |
63f61c5c | 35 | #include "udev_libc_wrapper.h" |
c81b35c0 | 36 | #include "udev.h" |
4a539daf | 37 | #include "logging.h" |
9af5bb2f | 38 | #include "udev_utils.h" |
4a539daf | 39 | #include "list.h" |
c81b35c0 KS |
40 | |
41 | ||
fb39f056 | 42 | int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem, const char *action) |
f61d732a | 43 | { |
9ed47a9f KS |
44 | char *pos; |
45 | ||
fc2aa296 | 46 | memset(udev, 0x00, sizeof(struct udevice)); |
e48fc108 | 47 | INIT_LIST_HEAD(&udev->symlink_list); |
821d0ec8 | 48 | INIT_LIST_HEAD(&udev->run_list); |
8825e9e7 | 49 | |
03cfa139 | 50 | if (subsystem) |
63f61c5c | 51 | strlcpy(udev->subsystem, subsystem, sizeof(udev->subsystem)); |
03cfa139 | 52 | |
821d0ec8 KS |
53 | if (action) |
54 | strlcpy(udev->action, action, sizeof(udev->action)); | |
55 | ||
9ed47a9f | 56 | if (devpath) { |
63f61c5c | 57 | strlcpy(udev->devpath, devpath, sizeof(udev->devpath)); |
18614ab2 | 58 | remove_trailing_char(udev->devpath, '/'); |
7b9b1839 | 59 | |
03cfa139 | 60 | if (strncmp(udev->devpath, "/block/", 7) == 0) |
e6764498 | 61 | udev->type = DEV_BLOCK; |
03cfa139 | 62 | else if (strncmp(udev->devpath, "/class/net/", 11) == 0) |
e6764498 | 63 | udev->type = DEV_NET; |
03cfa139 | 64 | else if (strncmp(udev->devpath, "/class/", 7) == 0) |
e6764498 | 65 | udev->type = DEV_CLASS; |
03cfa139 | 66 | else if (strncmp(udev->devpath, "/devices/", 9) == 0) |
e6764498 | 67 | udev->type = DEV_DEVICE; |
03cfa139 KS |
68 | |
69 | /* get kernel name */ | |
70 | pos = strrchr(udev->devpath, '/'); | |
71 | if (pos) { | |
63f61c5c | 72 | strlcpy(udev->kernel_name, &pos[1], sizeof(udev->kernel_name)); |
03cfa139 KS |
73 | dbg("kernel_name='%s'", udev->kernel_name); |
74 | ||
75 | /* Some block devices have '!' in their name, change that to '/' */ | |
76 | pos = udev->kernel_name; | |
77 | while (pos[0] != '\0') { | |
78 | if (pos[0] == '!') | |
79 | pos[0] = '/'; | |
80 | pos++; | |
81 | } | |
82 | ||
83 | /* get kernel number */ | |
84 | pos = &udev->kernel_name[strlen(udev->kernel_name)]; | |
85 | while (isdigit(pos[-1])) | |
86 | pos--; | |
63f61c5c | 87 | strlcpy(udev->kernel_number, pos, sizeof(udev->kernel_number)); |
03cfa139 KS |
88 | dbg("kernel_number='%s'", udev->kernel_number); |
89 | } | |
90 | } | |
65ab1334 | 91 | |
1bbff4f0 KS |
92 | if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS) { |
93 | udev->mode = 0660; | |
94 | strcpy(udev->owner, "root"); | |
95 | strcpy(udev->group, "root"); | |
96 | } | |
9ed47a9f | 97 | |
9ed47a9f | 98 | return 0; |
7a947ce5 KS |
99 | } |
100 | ||
e48fc108 KS |
101 | void udev_cleanup_device(struct udevice *udev) |
102 | { | |
103 | struct name_entry *name_loop; | |
104 | struct name_entry *temp_loop; | |
105 | ||
106 | list_for_each_entry_safe(name_loop, temp_loop, &udev->symlink_list, node) { | |
107 | list_del(&name_loop->node); | |
108 | free(name_loop); | |
109 | } | |
110 | } | |
111 | ||
85925517 | 112 | int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, unsigned int sublevel) |
d402af7d | 113 | { |
85925517 MB |
114 | static unsigned int kversion = 0; |
115 | static unsigned int kpatchlevel; | |
116 | static unsigned int ksublevel; | |
d402af7d KS |
117 | |
118 | if (kversion == 0) { | |
a4f0cc79 | 119 | struct utsname uts; |
d402af7d KS |
120 | if (uname(&uts) != 0) |
121 | return -1; | |
122 | ||
123 | if (sscanf (uts.release, "%u.%u.%u", &kversion, &kpatchlevel, &ksublevel) != 3) { | |
124 | kversion = 0; | |
125 | return -1; | |
126 | } | |
127 | } | |
128 | ||
129 | if (kversion >= version && kpatchlevel >= patchlevel && ksublevel >= sublevel) | |
130 | return 1; | |
131 | else | |
132 | return 0; | |
133 | } | |
134 | ||
2b41e68a KS |
135 | int create_path(const char *path) |
136 | { | |
63f61c5c | 137 | char p[PATH_SIZE]; |
2b41e68a KS |
138 | char *pos; |
139 | struct stat stats; | |
140 | ||
141 | strcpy (p, path); | |
142 | pos = strrchr(p, '/'); | |
143 | if (pos == p || pos == NULL) | |
144 | return 0; | |
145 | ||
146 | while (pos[-1] == '/') | |
147 | pos--; | |
148 | ||
149 | pos[0] = '\0'; | |
150 | ||
151 | dbg("stat '%s'\n", p); | |
152 | if (stat (p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) | |
153 | return 0; | |
154 | ||
155 | if (create_path (p) != 0) | |
156 | return -1; | |
157 | ||
158 | dbg("mkdir '%s'\n", p); | |
159 | return mkdir(p, 0755); | |
160 | } | |
161 | ||
c1ab0461 | 162 | /* Reset permissions on the device node, before unlinking it to make sure, |
866bb547 | 163 | * that permisions of possible hard links will be removed too. |
c1ab0461 KS |
164 | */ |
165 | int unlink_secure(const char *filename) | |
166 | { | |
167 | int retval; | |
168 | ||
169 | retval = chown(filename, 0, 0); | |
170 | if (retval) | |
171 | dbg("chown(%s, 0, 0) failed with error '%s'", filename, strerror(errno)); | |
172 | ||
173 | retval = chmod(filename, 0000); | |
174 | if (retval) | |
175 | dbg("chmod(%s, 0000) failed with error '%s'", filename, strerror(errno)); | |
176 | ||
177 | retval = unlink(filename); | |
178 | if (errno == ENOENT) | |
179 | retval = 0; | |
180 | ||
181 | if (retval) | |
182 | dbg("unlink(%s) failed with error '%s'", filename, strerror(errno)); | |
183 | ||
184 | return retval; | |
185 | } | |
186 | ||
c81b35c0 KS |
187 | int file_map(const char *filename, char **buf, size_t *bufsize) |
188 | { | |
189 | struct stat stats; | |
190 | int fd; | |
191 | ||
192 | fd = open(filename, O_RDONLY); | |
193 | if (fd < 0) { | |
194 | return -1; | |
195 | } | |
196 | ||
197 | if (fstat(fd, &stats) < 0) { | |
686cecf2 | 198 | close(fd); |
c81b35c0 KS |
199 | return -1; |
200 | } | |
201 | ||
202 | *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); | |
203 | if (*buf == MAP_FAILED) { | |
686cecf2 | 204 | close(fd); |
c81b35c0 KS |
205 | return -1; |
206 | } | |
207 | *bufsize = stats.st_size; | |
208 | ||
209 | close(fd); | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
214 | void file_unmap(char *buf, size_t bufsize) | |
215 | { | |
216 | munmap(buf, bufsize); | |
217 | } | |
218 | ||
9f8dfa19 KS |
219 | /* return number of chars until the next newline, skip escaped newline */ |
220 | size_t buf_get_line(const char *buf, size_t buflen, size_t cur) | |
c81b35c0 | 221 | { |
9f8dfa19 KS |
222 | int escape = 0; |
223 | size_t count; | |
224 | ||
225 | for (count = cur; count < buflen; count++) { | |
226 | if (!escape && buf[count] == '\n') | |
227 | break; | |
c81b35c0 | 228 | |
9f8dfa19 KS |
229 | if (buf[count] == '\\') |
230 | escape = 1; | |
231 | else | |
232 | escape = 0; | |
233 | } | |
c81b35c0 KS |
234 | |
235 | return count - cur; | |
236 | } | |
237 | ||
18614ab2 KS |
238 | void replace_untrusted_chars(char *string) |
239 | { | |
240 | size_t len; | |
241 | ||
242 | for (len = 0; string[len] != '\0'; len++) { | |
243 | if (strchr(";,~\\()\'", string[len])) { | |
244 | info("replace '%c' in '%s'", string[len], string); | |
245 | string[len] = '_'; | |
246 | } | |
247 | } | |
248 | } | |
249 | ||
250 | void remove_trailing_char(char *path, char c) | |
aef6bb13 | 251 | { |
e39515ac | 252 | size_t len; |
aef6bb13 KS |
253 | |
254 | len = strlen(path); | |
18614ab2 | 255 | while (len > 0 && path[len-1] == c) |
e39515ac | 256 | path[--len] = '\0'; |
aef6bb13 KS |
257 | } |
258 | ||
f0308095 | 259 | int name_list_add(struct list_head *name_list, const char *name, int sort) |
4a539daf | 260 | { |
9ed47a9f KS |
261 | struct name_entry *loop_name; |
262 | struct name_entry *new_name; | |
263 | ||
264 | list_for_each_entry(loop_name, name_list, node) { | |
265 | /* avoid doubles */ | |
266 | if (strcmp(loop_name->name, name) == 0) { | |
267 | dbg("'%s' is already in the list", name); | |
268 | return 0; | |
4a539daf | 269 | } |
9ed47a9f KS |
270 | if (sort && strcmp(loop_name->name, name) > 0) |
271 | break; | |
4a539daf KS |
272 | } |
273 | ||
9ed47a9f KS |
274 | new_name = malloc(sizeof(struct name_entry)); |
275 | if (new_name == NULL) { | |
4a539daf KS |
276 | dbg("error malloc"); |
277 | return -ENOMEM; | |
278 | } | |
279 | ||
63f61c5c | 280 | strlcpy(new_name->name, name, sizeof(new_name->name)); |
9ed47a9f | 281 | list_add_tail(&new_name->node, &loop_name->node); |
f0308095 | 282 | |
4a539daf KS |
283 | return 0; |
284 | } | |
285 | ||
aef6bb13 | 286 | /* calls function for every file found in specified directory */ |
67747e1d | 287 | int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix) |
4a539daf KS |
288 | { |
289 | struct dirent *ent; | |
290 | DIR *dir; | |
291 | char *ext; | |
67747e1d | 292 | char filename[PATH_SIZE]; |
4a539daf KS |
293 | |
294 | dbg("open directory '%s'", dirname); | |
295 | dir = opendir(dirname); | |
296 | if (dir == NULL) { | |
297 | dbg("unable to open '%s'", dirname); | |
298 | return -1; | |
299 | } | |
300 | ||
301 | while (1) { | |
302 | ent = readdir(dir); | |
303 | if (ent == NULL || ent->d_name[0] == '\0') | |
304 | break; | |
305 | ||
306 | if ((ent->d_name[0] == '.') || (ent->d_name[0] == COMMENT_CHARACTER)) | |
307 | continue; | |
308 | ||
67747e1d | 309 | /* look for file matching with specified suffix */ |
4a539daf KS |
310 | ext = strrchr(ent->d_name, '.'); |
311 | if (ext == NULL) | |
312 | continue; | |
313 | ||
314 | if (strcmp(ext, suffix) != 0) | |
315 | continue; | |
316 | ||
317 | dbg("put file '%s/%s' in list", dirname, ent->d_name); | |
4a539daf | 318 | |
67747e1d | 319 | snprintf(filename, sizeof(filename), "%s/%s", dirname, ent->d_name); |
63f61c5c | 320 | filename[sizeof(filename)-1] = '\0'; |
67747e1d | 321 | name_list_add(name_list, filename, 1); |
4a539daf KS |
322 | } |
323 | ||
324 | closedir(dir); | |
325 | return 0; | |
326 | } | |
fb39f056 KS |
327 | |
328 | int execute_command(const char *command, const char *subsystem) | |
329 | { | |
330 | int retval; | |
331 | pid_t pid; | |
332 | char arg[PATH_SIZE]; | |
333 | char *argv[(PATH_SIZE / 2) + 1]; | |
334 | char *pos; | |
335 | int devnull; | |
336 | int i; | |
337 | ||
338 | strlcpy(arg, command, sizeof(arg)); | |
339 | i = 0; | |
340 | if (strchr(arg, ' ')) { | |
341 | pos = arg; | |
342 | while (pos != NULL) { | |
343 | if (pos[0] == '\'') { | |
344 | /* don't separate if in apostrophes */ | |
345 | pos++; | |
346 | argv[i] = strsep(&pos, "\'"); | |
347 | while (pos && pos[0] == ' ') | |
348 | pos++; | |
349 | } else { | |
350 | argv[i] = strsep(&pos, " "); | |
351 | } | |
352 | dbg("arg[%i] '%s'", i, argv[i]); | |
353 | i++; | |
354 | } | |
355 | argv[i] = NULL; | |
356 | dbg("execute '%s' with parsed arguments", arg); | |
357 | } else { | |
358 | argv[0] = arg; | |
359 | argv[1] = (char *) subsystem; | |
360 | argv[2] = NULL; | |
361 | dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]); | |
362 | } | |
363 | ||
364 | pid = fork(); | |
365 | switch (pid) { | |
366 | case 0: | |
367 | /* child */ | |
368 | devnull = open("/dev/null", O_RDWR); | |
369 | if (devnull >= 0) { | |
370 | dup2(devnull, STDIN_FILENO); | |
371 | dup2(devnull, STDOUT_FILENO); | |
372 | dup2(devnull, STDERR_FILENO); | |
373 | close(devnull); | |
374 | } | |
375 | retval = execv(arg, argv); | |
376 | err("exec of child failed"); | |
377 | _exit(1); | |
378 | case -1: | |
379 | dbg("fork of child failed"); | |
380 | break; | |
381 | return -1; | |
382 | default: | |
383 | waitpid(pid, NULL, 0); | |
384 | } | |
385 | ||
386 | return 0; | |
387 | } |