]> git.ipfire.org Git - thirdparty/systemd.git/blame - udev_add.c
[PATCH] Writing udev rules docs update
[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>
f61d732a
KS
33#include <net/if.h>
34#include <sys/socket.h>
35#include <sys/ioctl.h>
36#include <linux/sockios.h>
ea733a2f 37
c80da508 38#include "libsysfs/sysfs/libsysfs.h"
57e1a277 39#include "udev_libc_wrapper.h"
ea733a2f 40#include "udev.h"
9af5bb2f 41#include "udev_utils.h"
7e720bd4 42#include "udev_sysfs.h"
ea733a2f 43#include "udev_version.h"
54988802 44#include "logging.h"
e5e322bc 45#include "udev_rules.h"
02fa9ae5 46#include "udev_db.h"
fbda4a34 47#include "udev_selinux.h"
9825617b 48
ea733a2f 49
7e720bd4 50int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid)
50e5de03 51{
49747bdc
KS
52 struct stat stats;
53 int retval = 0;
54
55 if (stat(file, &stats) != 0)
56 goto create;
57
58 /* preserve node with already correct numbers, to not change the inode number */
59 if (((stats.st_mode & S_IFMT) == S_IFBLK || (stats.st_mode & S_IFMT) == S_IFCHR) &&
7e720bd4 60 (stats.st_rdev == devt)) {
6b493a20 61 info("preserve file '%s', cause it has correct dev_t", file);
fbda4a34 62 selinux_setfilecon(file, udev->kernel_name, stats.st_mode);
49747bdc
KS
63 goto perms;
64 }
65
66 if (unlink(file) != 0)
67 dbg("unlink(%s) failed with error '%s'", file, strerror(errno));
68 else
69 dbg("already present file '%s' unlinked", file);
50e5de03 70
49747bdc 71create:
6a24dc74 72 switch (udev->type) {
e6764498 73 case DEV_BLOCK:
6a24dc74
KS
74 mode |= S_IFBLK;
75 break;
e6764498 76 case DEV_CLASS:
6a24dc74
KS
77 mode |= S_IFCHR;
78 break;
6a24dc74
KS
79 default:
80 dbg("unknown node type %c\n", udev->type);
81 return -EINVAL;
82 }
83
fbda4a34 84 selinux_setfscreatecon(file, udev->kernel_name, mode);
7e720bd4 85 retval = mknod(file, mode, devt);
4d772639 86 selinux_resetfscreatecon();
50e5de03 87 if (retval != 0) {
6b493a20 88 err("mknod(%s, %#o, %u, %u) failed with error '%s'",
7e720bd4 89 file, mode, major(devt), minor(devt), strerror(errno));
bbbe503e 90 goto exit;
50e5de03
KS
91 }
92
49747bdc
KS
93perms:
94 dbg("chmod(%s, %#o)", file, mode);
95 if (chmod(file, mode) != 0) {
96 dbg("chmod(%s, %#o) failed with error '%s'", file, mode, strerror(errno));
bbbe503e 97 goto exit;
50e5de03
KS
98 }
99
100 if (uid != 0 || gid != 0) {
49747bdc
KS
101 dbg("chown(%s, %u, %u)", file, uid, gid);
102 if (chown(file, uid, gid) != 0) {
50e5de03 103 dbg("chown(%s, %u, %u) failed with error '%s'",
49747bdc 104 file, uid, gid, strerror(errno));
bbbe503e 105 goto exit;
50e5de03
KS
106 }
107 }
108
bbbe503e
KS
109exit:
110 return retval;
50e5de03
KS
111}
112
6d564166 113static int create_node(struct udevice *udev, struct sysfs_class_device *class_dev)
97853b4f 114{
63f61c5c
KS
115 char filename[PATH_SIZE];
116 char partitionname[PATH_SIZE];
e48fc108 117 struct name_entry *name_loop;
783272f0
KS
118 uid_t uid;
119 gid_t gid;
3d150dfb 120 int tail;
e48fc108 121 int i;
3d150dfb 122
63f61c5c
KS
123 snprintf(filename, sizeof(filename), "%s/%s", udev_root, udev->name);
124 filename[sizeof(filename)-1] = '\0';
5840bc63 125
3d150dfb 126 /* create parent directories if needed */
6a24dc74 127 if (strchr(udev->name, '/'))
3d150dfb 128 create_path(filename);
218eae87 129
783272f0
KS
130 if (strcmp(udev->owner, "root") == 0)
131 uid = 0;
132 else {
c19a6b30 133 char *endptr;
783272f0 134 unsigned long id;
6d564166 135
783272f0 136 id = strtoul(udev->owner, &endptr, 10);
765cbd97 137 if (endptr[0] == '\0')
c19a6b30 138 uid = (uid_t) id;
57e1a277
KS
139 else
140 uid = lookup_user(udev->owner);
c19a6b30
KS
141 }
142
783272f0
KS
143 if (strcmp(udev->group, "root") == 0)
144 gid = 0;
145 else {
c19a6b30 146 char *endptr;
783272f0 147 unsigned long id;
6d564166 148
783272f0 149 id = strtoul(udev->group, &endptr, 10);
765cbd97 150 if (endptr[0] == '\0')
c19a6b30 151 gid = (gid_t) id;
57e1a277 152 else
68c2c0b5 153 gid = lookup_group(udev->group);
c19a6b30
KS
154 }
155
7a947ce5 156 if (!udev->test_run) {
50e5de03 157 info("creating device node '%s'", filename);
7e720bd4 158 if (udev_make_node(udev, filename, udev->devt, udev->mode, uid, gid) != 0)
bbbe503e 159 goto error;
ab2e5bd9
GKH
160 } else {
161 info("creating device node '%s', major = '%d', minor = '%d', "
162 "mode = '%#o', uid = '%d', gid = '%d'", filename,
7e720bd4 163 major(udev->devt), minor(udev->devt), udev->mode, uid, gid);
ab2e5bd9 164 }
50e5de03 165
bbbe503e 166 /* create all_partitions if requested */
fd9efc00 167 if (udev->partitions) {
6d564166
KS
168 struct sysfs_attribute *attr;
169 int range;
170
171 /* take the maximum registered minor range */
172 attr = sysfs_get_classdev_attr(class_dev, "range");
173 if (attr) {
174 range = atoi(attr->value);
175 if (range > 1)
176 udev->partitions = range-1;
177 }
7a947ce5
KS
178 info("creating device partition nodes '%s[1-%i]'", filename, udev->partitions);
179 if (!udev->test_run) {
180 for (i = 1; i <= udev->partitions; i++) {
7e720bd4
KS
181 dev_t part_devt;
182
63f61c5c
KS
183 snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i);
184 partitionname[sizeof(partitionname)-1] = '\0';
15139b8a 185 part_devt = makedev(major(udev->devt), minor(udev->devt) + i);
7e720bd4 186 udev_make_node(udev, partitionname, part_devt, udev->mode, uid, gid);
ab2e5bd9 187 }
50e5de03 188 }
3d150dfb
KS
189 }
190
bbbe503e 191 /* create symlink(s) if requested */
e48fc108 192 list_for_each_entry(name_loop, &udev->symlink_list, node) {
4d772639 193 int retval;
63f61c5c 194 char linktarget[PATH_SIZE];
2b41e68a 195
63f61c5c
KS
196 snprintf(filename, sizeof(filename), "%s/%s", udev_root, name_loop->name);
197 filename[sizeof(filename)-1] = '\0';
2b41e68a 198
7a947ce5
KS
199 dbg("symlink '%s' to node '%s' requested", filename, udev->name);
200 if (!udev->test_run)
e48fc108 201 if (strchr(filename, '/'))
9fe3f9a9
KS
202 create_path(filename);
203
204 /* optimize relative link */
205 linktarget[0] = '\0';
206 i = 0;
207 tail = 0;
e48fc108 208 while (udev->name[i] && (udev->name[i] == name_loop->name[i])) {
7a947ce5 209 if (udev->name[i] == '/')
9fe3f9a9
KS
210 tail = i+1;
211 i++;
212 }
e48fc108
KS
213 while (name_loop->name[i] != '\0') {
214 if (name_loop->name[i] == '/')
63f61c5c 215 strlcat(linktarget, "../", sizeof(linktarget));
9fe3f9a9
KS
216 i++;
217 }
3d150dfb 218
63f61c5c 219 strlcat(linktarget, &udev->name[tail], sizeof(linktarget));
3d150dfb 220
9fe3f9a9 221 dbg("symlink(%s, %s)", linktarget, filename);
7a947ce5 222 if (!udev->test_run) {
7eb92135 223 unlink(filename);
024780c2 224 selinux_setfscreatecon(filename, NULL, S_IFLNK);
4d772639
GKH
225 retval = symlink(linktarget, filename);
226 selinux_resetfscreatecon();
227 if (retval != 0)
9fe3f9a9
KS
228 dbg("symlink(%s, %s) failed with error '%s'",
229 linktarget, filename, strerror(errno));
4763256c 230 }
c19a6b30
KS
231 }
232
bbbe503e
KS
233 return 0;
234error:
235 return -1;
ea733a2f
GKH
236}
237
7a947ce5 238static int rename_net_if(struct udevice *udev)
f61d732a
KS
239{
240 int sk;
241 struct ifreq ifr;
242 int retval;
243
6b493a20 244 info("changing net interface name from '%s' to '%s'", udev->kernel_name, udev->name);
7a947ce5 245 if (udev->test_run)
bbbe503e
KS
246 return 0;
247
f61d732a
KS
248 sk = socket(PF_INET, SOCK_DGRAM, 0);
249 if (sk < 0) {
250 dbg("error opening socket");
251 return -1;
252 }
253
254 memset(&ifr, 0x00, sizeof(struct ifreq));
63f61c5c
KS
255 strlcpy(ifr.ifr_name, udev->kernel_name, IFNAMSIZ);
256 strlcpy(ifr.ifr_newname, udev->name, IFNAMSIZ);
f61d732a 257
f61d732a
KS
258 retval = ioctl(sk, SIOCSIFNAME, &ifr);
259 if (retval != 0)
260 dbg("error changing net interface name");
4a539daf 261 close(sk);
f61d732a
KS
262
263 return retval;
264}
265
7a947ce5 266int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)
ea733a2f 267{
bbbe503e 268 char *pos;
707680b1 269 int retval = 0;
ea733a2f 270
e6764498 271 if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS) {
7e720bd4
KS
272 udev->devt = get_devt(class_dev);
273 if (!udev->devt) {
707680b1 274 dbg("no dev-file found, do nothing");
5d24c6ca 275 return 0;
f61d732a 276 }
ea733a2f
GKH
277 }
278
821d0ec8
KS
279 udev_rules_get_name(udev, class_dev);
280 if (udev->ignore_device) {
281 dbg("device event will be ignored");
85a953c0 282 return 0;
821d0ec8 283 }
30defadd 284
7a947ce5 285 dbg("adding name='%s'", udev->name);
bbbe503e 286
9825617b 287 selinux_init();
5d24c6ca 288
e6764498 289 if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS) {
6d564166 290 retval = create_node(udev, class_dev);
c4603a07 291 if (retval != 0)
bbbe503e 292 goto exit;
9b28a52a 293
02fa9ae5
KS
294 if (udev_db_add_device(udev) != 0)
295 dbg("udev_db_add_dev failed, but we create the node anyway, "
5d24c6ca 296 "remove might not work for custom names");
f61d732a 297
5d24c6ca 298 /* use full path to the environment */
63f61c5c
KS
299 snprintf(udev->devname, sizeof(udev->devname), "%s/%s", udev_root, udev->name);
300 udev->devname[sizeof(udev->devname)-1] = '\0';
5d24c6ca 301
e6764498 302 } else if (udev->type == DEV_NET) {
5d24c6ca 303 /* look if we want to change the name of the netif */
7a947ce5
KS
304 if (strcmp(udev->name, udev->kernel_name) != 0) {
305 retval = rename_net_if(udev);
c4603a07 306 if (retval != 0)
bbbe503e 307 goto exit;
5d24c6ca 308
eabfc973
KS
309 /* we've changed the name, now fake the devpath, cause the
310 * original kernel name sleeps with the fishes and we don't
311 * get an event from the kernel with the new name
bbbe503e 312 */
7a947ce5 313 pos = strrchr(udev->devpath, '/');
bbbe503e
KS
314 if (pos != NULL) {
315 pos[1] = '\0';
63f61c5c 316 strlcat(udev->devpath, udev->name, sizeof(udev->devpath));
5d24c6ca 317 setenv("DEVPATH", udev->devpath, 1);
eabfc973 318 setenv("INTERFACE", udev->name, 1);
bbbe503e 319 }
9b28a52a 320
5d24c6ca 321 /* use netif name for the environment */
63f61c5c 322 strlcpy(udev->devname, udev->name, sizeof(udev->devname));
5d24c6ca 323 }
f61d732a 324 }
ea733a2f
GKH
325
326exit:
4d772639 327 selinux_exit();
30defadd 328
ea733a2f
GKH
329 return retval;
330}