]> git.ipfire.org Git - thirdparty/systemd.git/blame - udev-add.c
[PATCH] Add initial SELinux support for udev
[thirdparty/systemd.git] / udev-add.c
CommitLineData
ea733a2f
GKH
1/*
2 * udev-add.c
3 *
4 * Userspace devfs
5 *
6 * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 */
23
24#include <stdlib.h>
25#include <string.h>
26#include <stdio.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <errno.h>
32ff5bca 30#include <sys/stat.h>
10950dfe
GKH
31#include <sys/types.h>
32#include <grp.h>
33#ifndef __KLIBC__
34#include <pwd.h>
35#endif
ea733a2f 36
c80da508 37#include "libsysfs/sysfs/libsysfs.h"
ea733a2f
GKH
38#include "udev.h"
39#include "udev_version.h"
7ac0feeb 40#include "udev_dbus.h"
8481f8ce 41#include "udev_selinux.h"
54988802 42#include "logging.h"
ea733a2f 43#include "namedev.h"
8e41d35d 44#include "udevdb.h"
10950dfe 45#include "klibc_fixups.h"
ea733a2f 46
ea733a2f
GKH
47/*
48 * Right now the major/minor of a device is stored in a file called
49 * "dev" in sysfs.
50 * The number is stored as:
727d1ba5 51 * MM:mm
ea733a2f
GKH
52 * MM is the major
53 * mm is the minor
727d1ba5 54 * The value is in decimal.
ea733a2f 55 */
30defadd 56static int get_major_minor(struct sysfs_class_device *class_dev, struct udevice *udev)
ea733a2f 57{
ea256f90 58 int retval = -ENODEV;
5d4754f1 59 struct sysfs_attribute *attr = NULL;
ea733a2f 60
5d4754f1
DS
61 attr = sysfs_get_classdev_attr(class_dev, "dev");
62 if (attr == NULL)
ea256f90 63 goto exit;
5d4754f1 64 dbg("dev='%s'", attr->value);
ea733a2f 65
5d4754f1 66 if (sscanf(attr->value, "%u:%u", &udev->major, &udev->minor) != 2)
ea256f90 67 goto exit;
f7b4eca4 68 dbg("found major=%d, minor=%d", udev->major, udev->minor);
ea733a2f
GKH
69
70 retval = 0;
ea256f90 71exit:
ea733a2f
GKH
72 return retval;
73}
74
3d150dfb
KS
75static int create_path(char *file)
76{
77 char p[NAME_SIZE];
78 char *pos;
79 int retval;
80 struct stat stats;
81
c472e3c8 82 strfieldcpy(p, file);
3d150dfb
KS
83 pos = strchr(p+1, '/');
84 while (1) {
85 pos = strchr(pos+1, '/');
86 if (pos == NULL)
87 break;
88 *pos = 0x00;
89 if (stat(p, &stats)) {
90 retval = mkdir(p, 0755);
c5118442 91 if (retval != 0) {
3d150dfb
KS
92 dbg("mkdir(%s) failed with error '%s'",
93 p, strerror(errno));
94 return retval;
95 }
96 dbg("created '%s'", p);
97 }
98 *pos = '/';
99 }
100 return 0;
101}
102
50e5de03
KS
103static int make_node(char *filename, int major, int minor, unsigned int mode, uid_t uid, gid_t gid)
104{
105 int retval;
106
107 retval = mknod(filename, mode, makedev(major, minor));
108 if (retval != 0) {
109 dbg("mknod(%s, %#o, %u, %u) failed with error '%s'",
110 filename, mode, major, minor, strerror(errno));
111 return retval;
112 }
113
114 dbg("chmod(%s, %#o)", filename, mode);
115 retval = chmod(filename, mode);
116 if (retval != 0) {
117 dbg("chmod(%s, %#o) failed with error '%s'",
118 filename, mode, strerror(errno));
119 return retval;
120 }
121
122 if (uid != 0 || gid != 0) {
123 dbg("chown(%s, %u, %u)", filename, uid, gid);
124 retval = chown(filename, uid, gid);
125 if (retval != 0) {
126 dbg("chown(%s, %u, %u) failed with error '%s'",
127 filename, uid, gid, strerror(errno));
128 return retval;
129 }
130 }
131
132 return 0;
133}
134
eb10f97f 135static int create_node(struct udevice *dev, int fake)
ea733a2f 136{
3f09184b 137 struct stat stats;
ea733a2f 138 char filename[255];
3d150dfb 139 char linktarget[255];
50e5de03 140 char partitionname[255];
4763256c
KS
141 char *linkname;
142 char *symlinks;
ea733a2f 143 int retval = 0;
e308ebb7
GKH
144 uid_t uid = 0;
145 gid_t gid = 0;
3d150dfb
KS
146 int i;
147 int tail;
148
c472e3c8
KS
149 strfieldcpy(filename, udev_root);
150 strfieldcat(filename, dev->name);
5840bc63
GKH
151
152 switch (dev->type) {
1331c889 153 case 'b':
5840bc63 154 dev->mode |= S_IFBLK;
1331c889
GKH
155 break;
156 case 'c':
157 case 'u':
5840bc63 158 dev->mode |= S_IFCHR;
1331c889
GKH
159 break;
160 case 'p':
5840bc63 161 dev->mode |= S_IFIFO;
1331c889
GKH
162 break;
163 default:
5840bc63 164 dbg("unknown node type %c\n", dev->type);
1331c889
GKH
165 return -EINVAL;
166 }
0abf54fc 167
3d150dfb
KS
168 /* create parent directories if needed */
169 if (strrchr(dev->name, '/'))
170 create_path(filename);
218eae87 171
c5118442 172 if (dev->owner[0] != '\0') {
c19a6b30
KS
173 char *endptr;
174 unsigned long id = strtoul(dev->owner, &endptr, 10);
765cbd97 175 if (endptr[0] == '\0')
c19a6b30 176 uid = (uid_t) id;
10950dfe
GKH
177 else {
178 struct passwd *pw = getpwnam(dev->owner);
765cbd97
KS
179 if (pw == NULL)
180 dbg("specified user unknown '%s'", dev->owner);
10950dfe
GKH
181 else
182 uid = pw->pw_uid;
183 }
c19a6b30
KS
184 }
185
c5118442 186 if (dev->group[0] != '\0') {
c19a6b30
KS
187 char *endptr;
188 unsigned long id = strtoul(dev->group, &endptr, 10);
765cbd97 189 if (endptr[0] == '\0')
c19a6b30 190 gid = (gid_t) id;
10950dfe
GKH
191 else {
192 struct group *gr = getgrnam(dev->group);
765cbd97
KS
193 if (gr == NULL)
194 dbg("specified group unknown '%s'", dev->group);
10950dfe
GKH
195 else
196 gid = gr->gr_gid;
197 }
c19a6b30
KS
198 }
199
ab2e5bd9 200 if (!fake) {
50e5de03
KS
201 info("creating device node '%s'", filename);
202 make_node(filename, dev->major, dev->minor, dev->mode, uid, gid);
ab2e5bd9
GKH
203 } else {
204 info("creating device node '%s', major = '%d', minor = '%d', "
205 "mode = '%#o', uid = '%d', gid = '%d'", filename,
206 dev->major, dev->minor, (mode_t)dev->mode, uid, gid);
207 }
50e5de03
KS
208
209 /* create partitions if requested */
210 if (dev->partitions > 0) {
211 info("creating device partition nodes '%s[1-%i]'", filename, dev->partitions);
ab2e5bd9
GKH
212 if (!fake) {
213 for (i = 1; i <= dev->partitions; i++) {
214 sprintf(partitionname, "%s%i", filename, i);
215 make_node(partitionname, dev->major,
216 dev->minor + i, dev->mode, uid, gid);
217 }
50e5de03 218 }
3d150dfb
KS
219 }
220
8481f8ce
GKH
221 if (!fake)
222 selinux_add_node(filename);
223
3d150dfb 224 /* create symlink if requested */
c5118442 225 if (dev->symlink[0] != '\0') {
4763256c
KS
226 symlinks = dev->symlink;
227 while (1) {
228 linkname = strsep(&symlinks, " ");
0529e2ed 229 if (linkname == NULL || linkname[0] == '\0')
4763256c
KS
230 break;
231
c472e3c8
KS
232 strfieldcpy(filename, udev_root);
233 strfieldcat(filename, linkname);
4763256c 234 dbg("symlink '%s' to node '%s' requested", filename, dev->name);
eb10f97f
GKH
235 if (!fake)
236 if (strrchr(linkname, '/'))
237 create_path(filename);
4763256c
KS
238
239 /* optimize relative link */
240 linktarget[0] = '\0';
241 i = 0;
242 tail = 0;
243 while ((dev->name[i] == linkname[i]) && dev->name[i]) {
244 if (dev->name[i] == '/')
245 tail = i+1;
246 i++;
247 }
c5118442 248 while (linkname[i] != '\0') {
4763256c 249 if (linkname[i] == '/')
c472e3c8 250 strfieldcat(linktarget, "../");
4763256c
KS
251 i++;
252 }
3d150dfb 253
c5118442 254 if (linktarget[0] == '\0')
c472e3c8
KS
255 strfieldcpy(linktarget, "./");
256 strfieldcat(linktarget, &dev->name[tail]);
3d150dfb 257
50e5de03 258 /* unlink existing files to ensure that our symlink is created */
eb10f97f 259 if (!fake && (lstat(filename, &stats) == 0)) {
3f09184b 260 if ((stats.st_mode & S_IFMT) != S_IFDIR) {
261 if (unlink(filename))
262 dbg("unlink(%s) failed with error '%s'",
263 filename, strerror(errno));
264 }
265 }
266
4763256c 267 dbg("symlink(%s, %s)", linktarget, filename);
eb10f97f
GKH
268 if (!fake) {
269 retval = symlink(linktarget, filename);
270 if (retval != 0)
271 dbg("symlink(%s, %s) failed with error '%s'",
272 linktarget, filename, strerror(errno));
273 }
4763256c 274 }
c19a6b30
KS
275 }
276
ea733a2f
GKH
277 return retval;
278}
279
d7e954a4 280static struct sysfs_class_device *get_class_dev(char *device_name)
ea733a2f
GKH
281{
282 char dev_path[SYSFS_PATH_MAX];
63dde9f8
GKH
283 struct sysfs_class_device *class_dev = NULL;
284
c472e3c8
KS
285 strfieldcpy(dev_path, sysfs_path);
286 strfieldcat(dev_path, device_name);
f7b4eca4 287 dbg("looking at '%s'", dev_path);
ea733a2f
GKH
288
289 /* open up the sysfs class device for this thing... */
bcbe2d8e 290 class_dev = sysfs_open_class_device_path(dev_path);
ea733a2f 291 if (class_dev == NULL) {
bcbe2d8e 292 dbg ("sysfs_open_class_device_path failed");
63dde9f8 293 goto exit;
ea733a2f 294 }
f7b4eca4 295 dbg("class_dev->name='%s'", class_dev->name);
ea733a2f 296
63dde9f8 297exit:
ea733a2f
GKH
298 return class_dev;
299}
300
29fd7e67
GKH
301/* wait for the "dev" file to show up in the directory in sysfs.
302 * If it doesn't happen in about 10 seconds, give up.
303 */
304#define SECONDS_TO_WAIT_FOR_DEV 10
6089318c 305static int sleep_for_dev(char *path)
29fd7e67
GKH
306{
307 char filename[SYSFS_PATH_MAX + 6];
c332cfc7
RL
308 int loop = SECONDS_TO_WAIT_FOR_DEV;
309 int retval;
29fd7e67 310
c472e3c8
KS
311 strfieldcpy(filename, sysfs_path);
312 strfieldcat(filename, path);
313 strfieldcat(filename, "/dev");
29fd7e67 314
c332cfc7
RL
315 while (loop--) {
316 struct stat buf;
317
f7b4eca4 318 dbg("looking for '%s'", filename);
29fd7e67 319 retval = stat(filename, &buf);
c5118442 320 if (retval == 0)
29fd7e67 321 goto exit;
29fd7e67 322
f7b4eca4 323 /* sleep to give the kernel a chance to create the dev file */
29fd7e67
GKH
324 sleep(1);
325 }
326 retval = -ENODEV;
327exit:
328 return retval;
329}
330
eb10f97f 331int udev_add_device(char *path, char *subsystem, int fake)
ea733a2f 332{
30defadd 333 struct sysfs_class_device *class_dev = NULL;
5840bc63 334 struct udevice dev;
ea733a2f
GKH
335 int retval = -EINVAL;
336
f3b04a2e
GKH
337 memset(&dev, 0x00, sizeof(dev));
338
ea733a2f
GKH
339 /* for now, the block layer is the only place where block devices are */
340 if (strcmp(subsystem, "block") == 0)
5840bc63 341 dev.type = 'b';
ea733a2f 342 else
5840bc63 343 dev.type = 'c';
ea733a2f 344
5840bc63 345 retval = sleep_for_dev(path);
c5118442 346 if (retval != 0)
29fd7e67 347 goto exit;
63dde9f8 348
5840bc63 349 class_dev = get_class_dev(path);
ea733a2f
GKH
350 if (class_dev == NULL)
351 goto exit;
352
30defadd 353 retval = get_major_minor(class_dev, &dev);
c5118442 354 if (retval != 0) {
ca999860 355 dbg("get_major_minor failed");
ea733a2f
GKH
356 goto exit;
357 }
358
30defadd 359 retval = namedev_name_device(class_dev, &dev);
c5118442 360 if (retval != 0)
30defadd
GKH
361 goto exit;
362
eb10f97f
GKH
363 if (!fake) {
364 retval = udevdb_add_dev(path, &dev);
365 if (retval != 0)
366 dbg("udevdb_add_dev failed, but we are going to try "
367 "to create the node anyway. But remove might not "
368 "work properly for this device.");
ca999860 369
eb10f97f 370 }
f7b4eca4 371 dbg("name='%s'", dev.name);
eb10f97f 372 retval = create_node(&dev, fake);
ea733a2f 373
eb10f97f 374 if ((retval == 0) && (!fake))
7ac0feeb 375 sysbus_send_create(&dev, path);
5aebfbcb 376
ea733a2f 377exit:
30defadd
GKH
378 if (class_dev)
379 sysfs_close_class_device(class_dev);
380
ea733a2f
GKH
381 return retval;
382}