]>
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> |
c81b35c0 KS |
30 | #include <sys/stat.h> |
31 | #include <sys/mman.h> | |
d402af7d | 32 | #include <sys/utsname.h> |
c81b35c0 | 33 | |
c81b35c0 | 34 | #include "udev.h" |
4a539daf | 35 | #include "logging.h" |
9af5bb2f | 36 | #include "udev_utils.h" |
4a539daf | 37 | #include "list.h" |
c81b35c0 KS |
38 | |
39 | ||
9ed47a9f | 40 | int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem) |
f61d732a | 41 | { |
9ed47a9f KS |
42 | char *pos; |
43 | ||
fc2aa296 | 44 | memset(udev, 0x00, sizeof(struct udevice)); |
e48fc108 | 45 | INIT_LIST_HEAD(&udev->symlink_list); |
8825e9e7 | 46 | |
03cfa139 KS |
47 | if (subsystem) |
48 | strfieldcpy(udev->subsystem, subsystem); | |
49 | ||
9ed47a9f | 50 | if (devpath) { |
fc2aa296 | 51 | strfieldcpy(udev->devpath, devpath); |
9ed47a9f | 52 | no_trailing_slash(udev->devpath); |
7b9b1839 | 53 | |
03cfa139 KS |
54 | if (strncmp(udev->devpath, "/block/", 7) == 0) |
55 | udev->type = BLOCK; | |
56 | else if (strncmp(udev->devpath, "/class/net/", 11) == 0) | |
57 | udev->type = NET; | |
58 | else if (strncmp(udev->devpath, "/class/", 7) == 0) | |
59 | udev->type = CLASS; | |
60 | else if (strncmp(udev->devpath, "/devices/", 9) == 0) | |
61 | udev->type = PHYSDEV; | |
62 | ||
63 | /* get kernel name */ | |
64 | pos = strrchr(udev->devpath, '/'); | |
65 | if (pos) { | |
66 | strfieldcpy(udev->kernel_name, &pos[1]); | |
67 | dbg("kernel_name='%s'", udev->kernel_name); | |
68 | ||
69 | /* Some block devices have '!' in their name, change that to '/' */ | |
70 | pos = udev->kernel_name; | |
71 | while (pos[0] != '\0') { | |
72 | if (pos[0] == '!') | |
73 | pos[0] = '/'; | |
74 | pos++; | |
75 | } | |
76 | ||
77 | /* get kernel number */ | |
78 | pos = &udev->kernel_name[strlen(udev->kernel_name)]; | |
79 | while (isdigit(pos[-1])) | |
80 | pos--; | |
81 | strfieldcpy(udev->kernel_number, pos); | |
82 | dbg("kernel_number='%s'", udev->kernel_number); | |
83 | } | |
84 | } | |
65ab1334 | 85 | |
b9a8c482 KS |
86 | udev->mode = 0660; |
87 | strcpy(udev->owner, "root"); | |
88 | strcpy(udev->group, "root"); | |
9ed47a9f | 89 | |
9ed47a9f | 90 | return 0; |
7a947ce5 KS |
91 | } |
92 | ||
e48fc108 KS |
93 | void udev_cleanup_device(struct udevice *udev) |
94 | { | |
95 | struct name_entry *name_loop; | |
96 | struct name_entry *temp_loop; | |
97 | ||
98 | list_for_each_entry_safe(name_loop, temp_loop, &udev->symlink_list, node) { | |
99 | list_del(&name_loop->node); | |
100 | free(name_loop); | |
101 | } | |
102 | } | |
103 | ||
85925517 | 104 | int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, unsigned int sublevel) |
d402af7d | 105 | { |
85925517 MB |
106 | static unsigned int kversion = 0; |
107 | static unsigned int kpatchlevel; | |
108 | static unsigned int ksublevel; | |
d402af7d KS |
109 | |
110 | if (kversion == 0) { | |
a4f0cc79 | 111 | struct utsname uts; |
d402af7d KS |
112 | if (uname(&uts) != 0) |
113 | return -1; | |
114 | ||
115 | if (sscanf (uts.release, "%u.%u.%u", &kversion, &kpatchlevel, &ksublevel) != 3) { | |
116 | kversion = 0; | |
117 | return -1; | |
118 | } | |
119 | } | |
120 | ||
121 | if (kversion >= version && kpatchlevel >= patchlevel && ksublevel >= sublevel) | |
122 | return 1; | |
123 | else | |
124 | return 0; | |
125 | } | |
126 | ||
2b41e68a KS |
127 | int create_path(const char *path) |
128 | { | |
129 | char p[NAME_SIZE]; | |
130 | char *pos; | |
131 | struct stat stats; | |
132 | ||
133 | strcpy (p, path); | |
134 | pos = strrchr(p, '/'); | |
135 | if (pos == p || pos == NULL) | |
136 | return 0; | |
137 | ||
138 | while (pos[-1] == '/') | |
139 | pos--; | |
140 | ||
141 | pos[0] = '\0'; | |
142 | ||
143 | dbg("stat '%s'\n", p); | |
144 | if (stat (p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) | |
145 | return 0; | |
146 | ||
147 | if (create_path (p) != 0) | |
148 | return -1; | |
149 | ||
150 | dbg("mkdir '%s'\n", p); | |
151 | return mkdir(p, 0755); | |
152 | } | |
153 | ||
c1ab0461 | 154 | /* Reset permissions on the device node, before unlinking it to make sure, |
866bb547 | 155 | * that permisions of possible hard links will be removed too. |
c1ab0461 KS |
156 | */ |
157 | int unlink_secure(const char *filename) | |
158 | { | |
159 | int retval; | |
160 | ||
161 | retval = chown(filename, 0, 0); | |
162 | if (retval) | |
163 | dbg("chown(%s, 0, 0) failed with error '%s'", filename, strerror(errno)); | |
164 | ||
165 | retval = chmod(filename, 0000); | |
166 | if (retval) | |
167 | dbg("chmod(%s, 0000) failed with error '%s'", filename, strerror(errno)); | |
168 | ||
169 | retval = unlink(filename); | |
170 | if (errno == ENOENT) | |
171 | retval = 0; | |
172 | ||
173 | if (retval) | |
174 | dbg("unlink(%s) failed with error '%s'", filename, strerror(errno)); | |
175 | ||
176 | return retval; | |
177 | } | |
178 | ||
9f8dfa19 KS |
179 | int parse_get_pair(char **orig_string, char **left, char **right) |
180 | { | |
181 | char *temp; | |
182 | char *string = *orig_string; | |
183 | ||
184 | if (!string) | |
185 | return -ENODEV; | |
186 | ||
187 | /* eat any whitespace */ | |
188 | while (isspace(*string) || *string == ',') | |
189 | ++string; | |
190 | ||
191 | /* split based on '=' */ | |
192 | temp = strsep(&string, "="); | |
193 | *left = temp; | |
194 | if (!string) | |
195 | return -ENODEV; | |
196 | ||
197 | /* take the right side and strip off the '"' */ | |
198 | while (isspace(*string)) | |
199 | ++string; | |
200 | if (*string == '"') | |
201 | ++string; | |
202 | else | |
203 | return -ENODEV; | |
204 | ||
205 | temp = strsep(&string, "\""); | |
206 | if (!string || *temp == '\0') | |
207 | return -ENODEV; | |
208 | *right = temp; | |
209 | *orig_string = string; | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
c81b35c0 KS |
214 | int file_map(const char *filename, char **buf, size_t *bufsize) |
215 | { | |
216 | struct stat stats; | |
217 | int fd; | |
218 | ||
219 | fd = open(filename, O_RDONLY); | |
220 | if (fd < 0) { | |
221 | return -1; | |
222 | } | |
223 | ||
224 | if (fstat(fd, &stats) < 0) { | |
686cecf2 | 225 | close(fd); |
c81b35c0 KS |
226 | return -1; |
227 | } | |
228 | ||
229 | *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); | |
230 | if (*buf == MAP_FAILED) { | |
686cecf2 | 231 | close(fd); |
c81b35c0 KS |
232 | return -1; |
233 | } | |
234 | *bufsize = stats.st_size; | |
235 | ||
236 | close(fd); | |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
241 | void file_unmap(char *buf, size_t bufsize) | |
242 | { | |
243 | munmap(buf, bufsize); | |
244 | } | |
245 | ||
9f8dfa19 KS |
246 | /* return number of chars until the next newline, skip escaped newline */ |
247 | size_t buf_get_line(const char *buf, size_t buflen, size_t cur) | |
c81b35c0 | 248 | { |
9f8dfa19 KS |
249 | int escape = 0; |
250 | size_t count; | |
251 | ||
252 | for (count = cur; count < buflen; count++) { | |
253 | if (!escape && buf[count] == '\n') | |
254 | break; | |
c81b35c0 | 255 | |
9f8dfa19 KS |
256 | if (buf[count] == '\\') |
257 | escape = 1; | |
258 | else | |
259 | escape = 0; | |
260 | } | |
c81b35c0 KS |
261 | |
262 | return count - cur; | |
263 | } | |
264 | ||
2b41e68a | 265 | void no_trailing_slash(char *path) |
aef6bb13 | 266 | { |
e39515ac | 267 | size_t len; |
aef6bb13 KS |
268 | |
269 | len = strlen(path); | |
e39515ac MB |
270 | while (len > 0 && path[len-1] == '/') |
271 | path[--len] = '\0'; | |
aef6bb13 KS |
272 | } |
273 | ||
f0308095 | 274 | int name_list_add(struct list_head *name_list, const char *name, int sort) |
4a539daf | 275 | { |
9ed47a9f KS |
276 | struct name_entry *loop_name; |
277 | struct name_entry *new_name; | |
278 | ||
279 | list_for_each_entry(loop_name, name_list, node) { | |
280 | /* avoid doubles */ | |
281 | if (strcmp(loop_name->name, name) == 0) { | |
282 | dbg("'%s' is already in the list", name); | |
283 | return 0; | |
4a539daf | 284 | } |
9ed47a9f KS |
285 | if (sort && strcmp(loop_name->name, name) > 0) |
286 | break; | |
4a539daf KS |
287 | } |
288 | ||
9ed47a9f KS |
289 | new_name = malloc(sizeof(struct name_entry)); |
290 | if (new_name == NULL) { | |
4a539daf KS |
291 | dbg("error malloc"); |
292 | return -ENOMEM; | |
293 | } | |
294 | ||
9ed47a9f KS |
295 | strfieldcpy(new_name->name, name); |
296 | list_add_tail(&new_name->node, &loop_name->node); | |
f0308095 | 297 | |
4a539daf KS |
298 | return 0; |
299 | } | |
300 | ||
aef6bb13 | 301 | /* calls function for every file found in specified directory */ |
f0308095 KS |
302 | int call_foreach_file(int (*handler_function)(struct udevice *udev, const char *string), |
303 | struct udevice *udev, const char *dirname, const char *suffix) | |
4a539daf KS |
304 | { |
305 | struct dirent *ent; | |
306 | DIR *dir; | |
307 | char *ext; | |
9ed47a9f KS |
308 | struct name_entry *loop_file; |
309 | struct name_entry *tmp_file; | |
4a539daf KS |
310 | LIST_HEAD(file_list); |
311 | ||
312 | dbg("open directory '%s'", dirname); | |
313 | dir = opendir(dirname); | |
314 | if (dir == NULL) { | |
315 | dbg("unable to open '%s'", dirname); | |
316 | return -1; | |
317 | } | |
318 | ||
319 | while (1) { | |
320 | ent = readdir(dir); | |
321 | if (ent == NULL || ent->d_name[0] == '\0') | |
322 | break; | |
323 | ||
324 | if ((ent->d_name[0] == '.') || (ent->d_name[0] == COMMENT_CHARACTER)) | |
325 | continue; | |
326 | ||
327 | /* look for file with specified suffix */ | |
328 | ext = strrchr(ent->d_name, '.'); | |
329 | if (ext == NULL) | |
330 | continue; | |
331 | ||
332 | if (strcmp(ext, suffix) != 0) | |
333 | continue; | |
334 | ||
335 | dbg("put file '%s/%s' in list", dirname, ent->d_name); | |
9ed47a9f | 336 | name_list_add(&file_list, ent->d_name, 1); |
4a539daf KS |
337 | } |
338 | ||
339 | /* call function for every file in the list */ | |
9ed47a9f | 340 | list_for_each_entry_safe(loop_file, tmp_file, &file_list, node) { |
af4b05d4 | 341 | char filename[NAME_SIZE]; |
4a539daf | 342 | |
8673dcb8 | 343 | snprintf(filename, NAME_SIZE, "%s/%s", dirname, loop_file->name); |
af4b05d4 KS |
344 | filename[NAME_SIZE-1] = '\0'; |
345 | ||
f0308095 | 346 | handler_function(udev, filename); |
4a539daf | 347 | |
9ed47a9f | 348 | list_del(&loop_file->node); |
4a539daf KS |
349 | free(loop_file); |
350 | } | |
351 | ||
352 | closedir(dir); | |
353 | return 0; | |
354 | } |