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