chiark / gitweb /
add ID_TYPE to the id probers
[elogind.git] / udev_utils.c
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>
24 #include <stddef.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <dirent.h>
30 #include <syslog.h>
31 #include <sys/wait.h>
32 #include <sys/stat.h>
33 #include <sys/mman.h>
34 #include <sys/utsname.h>
35
36 #include "udev_libc_wrapper.h"
37 #include "udev.h"
38 #include "logging.h"
39 #include "udev_utils.h"
40 #include "list.h"
41
42
43 int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem, const char *action)
44 {
45         char *pos;
46
47         memset(udev, 0x00, sizeof(struct udevice));
48         INIT_LIST_HEAD(&udev->symlink_list);
49         INIT_LIST_HEAD(&udev->run_list);
50         INIT_LIST_HEAD(&udev->env_list);
51
52         if (subsystem)
53                 strlcpy(udev->subsystem, subsystem, sizeof(udev->subsystem));
54
55         if (action)
56                 strlcpy(udev->action, action, sizeof(udev->action));
57
58         if (devpath) {
59                 strlcpy(udev->devpath, devpath, sizeof(udev->devpath));
60                 remove_trailing_char(udev->devpath, '/');
61
62                 if (strncmp(udev->devpath, "/block/", 7) == 0)
63                         udev->type = DEV_BLOCK;
64                 else if (strncmp(udev->devpath, "/class/net/", 11) == 0)
65                         udev->type = DEV_NET;
66                 else if (strncmp(udev->devpath, "/class/", 7) == 0)
67                         udev->type = DEV_CLASS;
68                 else if (strncmp(udev->devpath, "/devices/", 9) == 0)
69                         udev->type = DEV_DEVICE;
70
71                 /* get kernel name */
72                 pos = strrchr(udev->devpath, '/');
73                 if (pos) {
74                         strlcpy(udev->kernel_name, &pos[1], sizeof(udev->kernel_name));
75                         dbg("kernel_name='%s'", udev->kernel_name);
76
77                         /* Some block devices have '!' in their name, change that to '/' */
78                         pos = udev->kernel_name;
79                         while (pos[0] != '\0') {
80                                 if (pos[0] == '!')
81                                         pos[0] = '/';
82                                 pos++;
83                         }
84
85                         /* get kernel number */
86                         pos = &udev->kernel_name[strlen(udev->kernel_name)];
87                         while (isdigit(pos[-1]))
88                                 pos--;
89                         strlcpy(udev->kernel_number, pos, sizeof(udev->kernel_number));
90                         dbg("kernel_number='%s'", udev->kernel_number);
91                 }
92         }
93
94         if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS) {
95                 udev->mode = 0660;
96                 strcpy(udev->owner, "root");
97                 strcpy(udev->group, "root");
98         }
99
100         return 0;
101 }
102
103 void udev_cleanup_device(struct udevice *udev)
104 {
105         struct name_entry *name_loop;
106         struct name_entry *temp_loop;
107
108         list_for_each_entry_safe(name_loop, temp_loop, &udev->symlink_list, node) {
109                 list_del(&name_loop->node);
110                 free(name_loop);
111         }
112         list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) {
113                 list_del(&name_loop->node);
114                 free(name_loop);
115         }
116         list_for_each_entry_safe(name_loop, temp_loop, &udev->env_list, node) {
117                 list_del(&name_loop->node);
118                 free(name_loop);
119         }
120 }
121
122 int string_is_true(const char *str)
123 {
124         if (strcasecmp(str, "true") == 0)
125                 return 1;
126         if (strcasecmp(str, "yes") == 0)
127                 return 1;
128         if (strcasecmp(str, "1") == 0)
129                 return 1;
130         return 0;
131 }
132
133 int log_priority(const char *priority)
134 {
135         char *endptr;
136         int prio;
137
138         prio = strtol(priority, &endptr, 10);
139         if (endptr[0] == '\0')
140                 return prio;
141         if (strncasecmp(priority, "err", 3) == 0)
142                 return LOG_ERR;
143         if (strcasecmp(priority, "info") == 0)
144                 return LOG_INFO;
145         if (strcasecmp(priority, "debug") == 0)
146                 return LOG_DEBUG;
147         if (string_is_true(priority))
148                 return LOG_ERR;
149
150         return 0;
151 }
152
153 int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, unsigned int sublevel)
154 {
155         static unsigned int kversion = 0;
156         static unsigned int kpatchlevel;
157         static unsigned int ksublevel;
158
159         if (kversion == 0) {
160                 struct utsname uts;
161                 if (uname(&uts) != 0)
162                         return -1;
163
164                 if (sscanf (uts.release, "%u.%u.%u", &kversion, &kpatchlevel, &ksublevel) != 3) {
165                         kversion = 0;
166                         return -1;
167                 }
168         }
169
170         if (kversion >= version && kpatchlevel >= patchlevel && ksublevel >= sublevel)
171                 return 1;
172         else
173                 return 0;
174 }
175
176 int create_path(const char *path)
177 {
178         char p[PATH_SIZE];
179         char *pos;
180         struct stat stats;
181
182         strcpy (p, path);
183         pos = strrchr(p, '/');
184         if (pos == p || pos == NULL)
185                 return 0;
186
187         while (pos[-1] == '/')
188                 pos--;
189
190         pos[0] = '\0';
191
192         dbg("stat '%s'\n", p);
193         if (stat (p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR)
194                 return 0;
195
196         if (create_path (p) != 0)
197                 return -1;
198
199         dbg("mkdir '%s'\n", p);
200         return mkdir(p, 0755);
201 }
202
203 /* Reset permissions on the device node, before unlinking it to make sure,
204  * that permisions of possible hard links will be removed too.
205  */
206 int unlink_secure(const char *filename)
207 {
208         int retval;
209
210         retval = chown(filename, 0, 0);
211         if (retval)
212                 dbg("chown(%s, 0, 0) failed with error '%s'", filename, strerror(errno));
213
214         retval = chmod(filename, 0000);
215         if (retval)
216                 dbg("chmod(%s, 0000) failed with error '%s'", filename, strerror(errno));
217
218         retval = unlink(filename);
219         if (errno == ENOENT)
220                 retval = 0;
221
222         if (retval)
223                 dbg("unlink(%s) failed with error '%s'", filename, strerror(errno));
224
225         return retval;
226 }
227
228 int file_map(const char *filename, char **buf, size_t *bufsize)
229 {
230         struct stat stats;
231         int fd;
232
233         fd = open(filename, O_RDONLY);
234         if (fd < 0) {
235                 return -1;
236         }
237
238         if (fstat(fd, &stats) < 0) {
239                 close(fd);
240                 return -1;
241         }
242
243         *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
244         if (*buf == MAP_FAILED) {
245                 close(fd);
246                 return -1;
247         }
248         *bufsize = stats.st_size;
249
250         close(fd);
251
252         return 0;
253 }
254
255 void file_unmap(void *buf, size_t bufsize)
256 {
257         munmap(buf, bufsize);
258 }
259
260 /* return number of chars until the next newline, skip escaped newline */
261 size_t buf_get_line(const char *buf, size_t buflen, size_t cur)
262 {
263         int escape = 0;
264         size_t count;
265
266         for (count = cur; count < buflen; count++) {
267                 if (!escape && buf[count] == '\n')
268                         break;
269
270                 if (buf[count] == '\\')
271                         escape = 1;
272                 else
273                         escape = 0;
274         }
275
276         return count - cur;
277 }
278
279 void replace_untrusted_chars(char *string)
280 {
281         size_t len;
282
283         for (len = 0; string[len] != '\0'; len++) {
284                 if (strchr(";,~\\()\'", string[len])) {
285                         info("replace '%c' in '%s'", string[len], string);
286                         string[len] = '_';
287                 }
288         }
289 }
290
291 void remove_trailing_char(char *path, char c)
292 {
293         size_t len;
294
295         len = strlen(path);
296         while (len > 0 && path[len-1] == c)
297                 path[--len] = '\0';
298 }
299
300 int name_list_add(struct list_head *name_list, const char *name, int sort)
301 {
302         struct name_entry *loop_name;
303         struct name_entry *new_name;
304
305         list_for_each_entry(loop_name, name_list, node) {
306                 /* avoid doubles */
307                 if (strcmp(loop_name->name, name) == 0) {
308                         dbg("'%s' is already in the list", name);
309                         return 0;
310                 }
311         }
312
313         if (sort)
314                 list_for_each_entry(loop_name, name_list, node) {
315                         if (sort && strcmp(loop_name->name, name) > 0)
316                                 break;
317                 }
318
319         new_name = malloc(sizeof(struct name_entry));
320         if (new_name == NULL) {
321                 dbg("error malloc");
322                 return -ENOMEM;
323         }
324
325         strlcpy(new_name->name, name, sizeof(new_name->name));
326         dbg("adding '%s'", new_name->name);
327         list_add_tail(&new_name->node, &loop_name->node);
328
329         return 0;
330 }
331
332 int name_list_key_add(struct list_head *name_list, const char *key, const char *value)
333 {
334         struct name_entry *loop_name;
335         struct name_entry *new_name;
336
337         list_for_each_entry(loop_name, name_list, node) {
338                 if (strncmp(loop_name->name, key, strlen(key)) == 0) {
339                         dbg("key already present '%s', replace it", loop_name->name);
340                         snprintf(loop_name->name, sizeof(loop_name->name), "%s=%s", key, value);
341                         loop_name->name[sizeof(loop_name->name)-1] = '\0';
342                         return 0;
343                 }
344         }
345
346         new_name = malloc(sizeof(struct name_entry));
347         if (new_name == NULL) {
348                 dbg("error malloc");
349                 return -ENOMEM;
350         }
351
352         snprintf(new_name->name, sizeof(new_name->name), "%s=%s", key, value);
353         new_name->name[sizeof(new_name->name)-1] = '\0';
354         dbg("adding '%s'", new_name->name);
355         list_add_tail(&new_name->node, &loop_name->node);
356
357         return 0;
358 }
359
360 /* calls function for every file found in specified directory */
361 int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix)
362 {
363         struct dirent *ent;
364         DIR *dir;
365         char *ext;
366         char filename[PATH_SIZE];
367
368         dbg("open directory '%s'", dirname);
369         dir = opendir(dirname);
370         if (dir == NULL) {
371                 dbg("unable to open '%s'", dirname);
372                 return -1;
373         }
374
375         while (1) {
376                 ent = readdir(dir);
377                 if (ent == NULL || ent->d_name[0] == '\0')
378                         break;
379
380                 if ((ent->d_name[0] == '.') || (ent->d_name[0] == COMMENT_CHARACTER))
381                         continue;
382
383                 /* look for file matching with specified suffix */
384                 ext = strrchr(ent->d_name, '.');
385                 if (ext == NULL)
386                         continue;
387
388                 if (strcmp(ext, suffix) != 0)
389                         continue;
390
391                 dbg("put file '%s/%s' in list", dirname, ent->d_name);
392
393                 snprintf(filename, sizeof(filename), "%s/%s", dirname, ent->d_name);
394                 filename[sizeof(filename)-1] = '\0';
395                 name_list_add(name_list, filename, 1);
396         }
397
398         closedir(dir);
399         return 0;
400 }
401
402 int execute_program(const char *command, const char *subsystem,
403                     char *result, size_t ressize, size_t *reslen)
404 {
405         int retval = 0;
406         int count;
407         int status;
408         int pipefds[2];
409         pid_t pid;
410         char *pos;
411         char arg[PATH_SIZE];
412         char *argv[(sizeof(arg) / 2) + 1];
413         int devnull;
414         int i;
415         size_t len;
416
417         strlcpy(arg, command, sizeof(arg));
418         i = 0;
419         if (strchr(arg, ' ')) {
420                 pos = arg;
421                 while (pos != NULL) {
422                         if (pos[0] == '\'') {
423                                 /* don't separate if in apostrophes */
424                                 pos++;
425                                 argv[i] = strsep(&pos, "\'");
426                                 while (pos && pos[0] == ' ')
427                                         pos++;
428                         } else {
429                                 argv[i] = strsep(&pos, " ");
430                         }
431                         dbg("arg[%i] '%s'", i, argv[i]);
432                         i++;
433                 }
434                 argv[i] =  NULL;
435                 dbg("execute '%s' with parsed arguments", arg);
436         } else {
437                 argv[0] = arg;
438                 argv[1] = (char *) subsystem;
439                 argv[2] = NULL;
440                 dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]);
441         }
442
443         if (result) {
444                 if (pipe(pipefds) != 0) {
445                         err("pipe failed");
446                         return -1;
447                 }
448         }
449
450         pid = fork();
451         switch(pid) {
452         case 0:
453                 /* child dup2 write side of pipe to STDOUT */
454                 devnull = open("/dev/null", O_RDWR);
455                 if (devnull >= 0) {
456                         dup2(devnull, STDIN_FILENO);
457                         if (!result)
458                                 dup2(devnull, STDOUT_FILENO);
459                         dup2(devnull, STDERR_FILENO);
460                         close(devnull);
461                 }
462                 if (result)
463                         dup2(pipefds[1], STDOUT_FILENO);
464                 execv(arg, argv);
465                 err("exec of program failed");
466                 _exit(1);
467         case -1:
468                 err("fork of '%s' failed", arg);
469                 return -1;
470         default:
471                 /* parent reads from pipefds[0] */
472                 if (result) {
473                         close(pipefds[1]);
474                         len = 0;
475                         while (1) {
476                                 count = read(pipefds[0], result + len, ressize - len-1);
477                                 if (count < 0) {
478                                         err("read failed with '%s'", strerror(errno));
479                                         retval = -1;
480                                         break;
481                                 }
482
483                                 if (count == 0)
484                                         break;
485
486                                 len += count;
487                                 if (len >= ressize-1) {
488                                         err("ressize %ld too short", (long)ressize);
489                                         retval = -1;
490                                         break;
491                                 }
492                         }
493                         result[len] = '\0';
494                         close(pipefds[0]);
495                         if (reslen)
496                                 *reslen = len;
497                 }
498                 waitpid(pid, &status, 0);
499
500                 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
501                         dbg("exec program status 0x%x", status);
502                         retval = -1;
503                 }
504         }
505
506         return retval;
507 }