chiark / gitweb /
2d7f8dc758f5bd595e304db9198b2800858ad2ba
[elogind.git] / libudev / libudev-util-private.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2003-2009 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  */
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <stddef.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <errno.h>
19 #include <ctype.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <sys/param.h>
23
24 #include "libudev.h"
25 #include "libudev-private.h"
26
27 static int create_path(struct udev *udev, const char *path, bool selinux)
28 {
29         char p[UTIL_PATH_SIZE];
30         char *pos;
31         struct stat stats;
32         int err;
33
34         util_strscpy(p, sizeof(p), path);
35         pos = strrchr(p, '/');
36         if (pos == NULL)
37                 return 0;
38         while (pos != p && pos[-1] == '/')
39                 pos--;
40         if (pos == p)
41                 return 0;
42         pos[0] = '\0';
43
44         dbg(udev, "stat '%s'\n", p);
45         if (stat(p, &stats) == 0) {
46                 if ((stats.st_mode & S_IFMT) == S_IFDIR)
47                         return 0;
48                 else
49                         return -ENOTDIR;
50         }
51
52         err = util_create_path(udev, p);
53         if (err != 0)
54                 return err;
55
56         dbg(udev, "mkdir '%s'\n", p);
57         if (selinux)
58                 udev_selinux_setfscreatecon(udev, p, S_IFDIR|0755);
59         err = mkdir(p, 0755);
60         if (err != 0) {
61                 err = -errno;
62                 if (err == -EEXIST && stat(p, &stats) == 0) {
63                         if ((stats.st_mode & S_IFMT) == S_IFDIR)
64                                 err = 0;
65                         else
66                                 err = -ENOTDIR;
67                 }
68         }
69         if (selinux)
70                 udev_selinux_resetfscreatecon(udev);
71         return err;
72 }
73
74 int util_create_path(struct udev *udev, const char *path)
75 {
76         return create_path(udev, path, false);
77 }
78
79 int util_create_path_selinux(struct udev *udev, const char *path)
80 {
81         return create_path(udev, path, true);
82 }
83
84 int util_delete_path(struct udev *udev, const char *path)
85 {
86         char p[UTIL_PATH_SIZE];
87         char *pos;
88         int err = 0;
89
90         if (path[0] == '/')
91                 while(path[1] == '/')
92                         path++;
93         util_strscpy(p, sizeof(p), path);
94         pos = strrchr(p, '/');
95         if (pos == p || pos == NULL)
96                 return 0;
97
98         for (;;) {
99                 *pos = '\0';
100                 pos = strrchr(p, '/');
101
102                 /* don't remove the last one */
103                 if ((pos == p) || (pos == NULL))
104                         break;
105
106                 err = rmdir(p);
107                 if (err < 0) {
108                         if (errno == ENOENT)
109                                 err = 0;
110                         break;
111                 }
112         }
113         return err;
114 }
115
116 /* Reset permissions on the device node, before unlinking it to make sure,
117  * that permissions of possible hard links will be removed too.
118  */
119 int util_unlink_secure(struct udev *udev, const char *filename)
120 {
121         int err;
122
123         chown(filename, 0, 0);
124         chmod(filename, 0000);
125         err = unlink(filename);
126         if (errno == ENOENT)
127                 err = 0;
128         if (err)
129                 err(udev, "unlink(%s) failed: %m\n", filename);
130         return err;
131 }
132
133 uid_t util_lookup_user(struct udev *udev, const char *user)
134 {
135         char *endptr;
136         size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
137         char buf[buflen];
138         struct passwd pwbuf;
139         struct passwd *pw;
140         uid_t uid;
141
142         if (strcmp(user, "root") == 0)
143                 return 0;
144         uid = strtoul(user, &endptr, 10);
145         if (endptr[0] == '\0')
146                 return uid;
147
148         errno = getpwnam_r(user, &pwbuf, buf, buflen, &pw);
149         if (pw != NULL)
150                 return pw->pw_uid;
151         if (errno == 0 || errno == ENOENT || errno == ESRCH)
152                 err(udev, "specified user '%s' unknown\n", user);
153         else
154                 err(udev, "error resolving user '%s': %m\n", user);
155         return 0;
156 }
157
158 gid_t util_lookup_group(struct udev *udev, const char *group)
159 {
160         char *endptr;
161         size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
162         char *buf;
163         struct group grbuf;
164         struct group *gr;
165         gid_t gid = 0;
166
167         if (strcmp(group, "root") == 0)
168                 return 0;
169         gid = strtoul(group, &endptr, 10);
170         if (endptr[0] == '\0')
171                 return gid;
172         buf = NULL;
173         gid = 0;
174         for (;;) {
175                 char *newbuf;
176
177                 newbuf = realloc(buf, buflen);
178                 if (!newbuf)
179                         break;
180                 buf = newbuf;
181                 errno = getgrnam_r(group, &grbuf, buf, buflen, &gr);
182                 if (gr != NULL) {
183                         gid = gr->gr_gid;
184                 } else if (errno == ERANGE) {
185                         buflen *= 2;
186                         continue;
187                 } else if (errno == 0 || errno == ENOENT || errno == ESRCH) {
188                         err(udev, "specified group '%s' unknown\n", group);
189                 } else {
190                         err(udev, "error resolving group '%s': %m\n", group);
191                 }
192                 break;
193         }
194         free(buf);
195         return gid;
196 }
197
198 /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
199 int util_resolve_subsys_kernel(struct udev *udev, const char *string,
200                                char *result, size_t maxsize, int read_value)
201 {
202         char temp[UTIL_PATH_SIZE];
203         char *subsys;
204         char *sysname;
205         struct udev_device *dev;
206         char *attr;
207
208         if (string[0] != '[')
209                 return -1;
210
211         util_strscpy(temp, sizeof(temp), string);
212
213         subsys = &temp[1];
214
215         sysname = strchr(subsys, '/');
216         if (sysname == NULL)
217                 return -1;
218         sysname[0] = '\0';
219         sysname = &sysname[1];
220
221         attr = strchr(sysname, ']');
222         if (attr == NULL)
223                 return -1;
224         attr[0] = '\0';
225         attr = &attr[1];
226         if (attr[0] == '/')
227                 attr = &attr[1];
228         if (attr[0] == '\0')
229                 attr = NULL;
230
231         if (read_value && attr == NULL)
232                 return -1;
233
234         dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname);
235         if (dev == NULL)
236                 return -1;
237
238         if (read_value) {
239                 const char *val;
240
241                 val = udev_device_get_sysattr_value(dev, attr);
242                 if (val != NULL)
243                         util_strscpy(result, maxsize, val);
244                 else
245                         result[0] = '\0';
246                 info(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result);
247         } else {
248                 size_t l;
249                 char *s;
250
251                 s = result;
252                 l = util_strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL);
253                 if (attr != NULL)
254                         util_strpcpyl(&s, l, "/", attr, NULL);
255                 info(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result);
256         }
257         udev_device_unref(dev);
258         return 0;
259 }