]> git.ipfire.org Git - thirdparty/systemd.git/blame - udev_node.c
Makefile: do not require GNU install
[thirdparty/systemd.git] / udev_node.c
CommitLineData
ea733a2f 1/*
ea733a2f 2 * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
27b77df4 3 * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
ea733a2f
GKH
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
27b77df4 16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
ea733a2f
GKH
17 *
18 */
19
20#include <stdlib.h>
21#include <string.h>
22#include <stdio.h>
1aa1e248 23#include <stddef.h>
ea733a2f
GKH
24#include <fcntl.h>
25#include <unistd.h>
26#include <errno.h>
67f69ae1 27#include <grp.h>
24f0605c 28#include <dirent.h>
32ff5bca 29#include <sys/stat.h>
10950dfe 30#include <sys/types.h>
ea733a2f
GKH
31
32#include "udev.h"
e5e322bc 33#include "udev_rules.h"
fbda4a34 34#include "udev_selinux.h"
9825617b 35
e54f0b4a 36#define TMP_FILE_EXT ".udev-tmp"
ea733a2f 37
a4d5ca64 38int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid)
50e5de03 39{
e54f0b4a 40 char file_tmp[PATH_SIZE + sizeof(TMP_FILE_EXT)];
49747bdc
KS
41 struct stat stats;
42 int retval = 0;
43
1aa1e248 44 if (major(devt) != 0 && strcmp(udev->dev->subsystem, "block") == 0)
57663b36 45 mode |= S_IFBLK;
1aa1e248 46 else
57663b36 47 mode |= S_IFCHR;
57663b36 48
e54f0b4a
KS
49 if (lstat(file, &stats) == 0) {
50 if ((stats.st_mode & S_IFMT) == (mode & S_IFMT) && (stats.st_rdev == devt)) {
51 info("preserve file '%s', because it has correct dev_t", file);
52 selinux_setfilecon(file, udev->dev->kernel, stats.st_mode);
53 goto perms;
54 }
678484af
KS
55 } else {
56 selinux_setfscreatecon(file, udev->dev->kernel, mode);
57 retval = mknod(file, mode, devt);
58 selinux_resetfscreatecon();
59 if (retval == 0)
60 goto perms;
49747bdc
KS
61 }
62
e54f0b4a
KS
63 info("atomically replace '%s'", file);
64 strlcpy(file_tmp, file, sizeof(file_tmp));
65 strlcat(file_tmp, TMP_FILE_EXT, sizeof(file_tmp));
14c79942 66 unlink(file_tmp);
e54f0b4a
KS
67 selinux_setfscreatecon(file_tmp, udev->dev->kernel, mode);
68 retval = mknod(file_tmp, mode, devt);
69 selinux_resetfscreatecon();
50e5de03 70 if (retval != 0) {
ff3e4bed 71 err("mknod(%s, %#o, %u, %u) failed: %s",
e54f0b4a
KS
72 file_tmp, mode, major(devt), minor(devt), strerror(errno));
73 goto exit;
74 }
75 retval = rename(file_tmp, file);
76 if (retval != 0) {
77 err("rename(%s, %s) failed: %s",
78 file_tmp, file, strerror(errno));
79 unlink(file_tmp);
bbbe503e 80 goto exit;
50e5de03
KS
81 }
82
49747bdc
KS
83perms:
84 dbg("chmod(%s, %#o)", file, mode);
85 if (chmod(file, mode) != 0) {
df4e89bf 86 err("chmod(%s, %#o) failed: %s", file, mode, strerror(errno));
bbbe503e 87 goto exit;
50e5de03
KS
88 }
89
90 if (uid != 0 || gid != 0) {
49747bdc
KS
91 dbg("chown(%s, %u, %u)", file, uid, gid);
92 if (chown(file, uid, gid) != 0) {
df4e89bf 93 err("chown(%s, %u, %u) failed: %s",
49747bdc 94 file, uid, gid, strerror(errno));
bbbe503e 95 goto exit;
50e5de03
KS
96 }
97 }
bbbe503e
KS
98exit:
99 return retval;
50e5de03
KS
100}
101
27d4bf18 102static int node_symlink(const char *node, const char *slink)
fa33d857 103{
e54f0b4a 104 struct stat stats;
27d4bf18 105 char target[PATH_SIZE] = "";
e54f0b4a 106 char slink_tmp[PATH_SIZE + sizeof(TMP_FILE_EXT)];
27d4bf18
KS
107 int i = 0;
108 int tail = 0;
fa33d857 109 int len;
e54f0b4a 110 int retval = 0;
fa33d857 111
27d4bf18
KS
112 /* use relative link */
113 while (node[i] && (node[i] == slink[i])) {
114 if (node[i] == '/')
115 tail = i+1;
116 i++;
117 }
118 while (slink[i] != '\0') {
119 if (slink[i] == '/')
120 strlcat(target, "../", sizeof(target));
121 i++;
122 }
123 strlcat(target, &node[tail], sizeof(target));
124
e54f0b4a
KS
125 /* preserve link with correct target, do not replace node of other device */
126 if (lstat(slink, &stats) == 0) {
127 if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) {
128 struct stat stats2;
129
130 info("found existing node instead of symlink '%s'", slink);
131 if (lstat(node, &stats2) == 0) {
132 if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) &&
133 stats.st_rdev == stats2.st_rdev) {
134 info("replace device node '%s' with symlink to our node '%s'", slink, node);
135 } else {
89e0a022 136 err("device node '%s' already exists, link to '%s' will not overwrite it", slink, node);
e54f0b4a
KS
137 goto exit;
138 }
139 }
140 } else if (S_ISLNK(stats.st_mode)) {
141 char buf[PATH_SIZE];
142
143 info("found existing symlink '%s'", slink);
144 len = readlink(slink, buf, sizeof(buf));
145 if (len > 0) {
146 buf[len] = '\0';
147 if (strcmp(target, buf) == 0) {
148 info("preserve already existing symlink '%s' to '%s'", slink, target);
149 selinux_setfilecon(slink, NULL, S_IFLNK);
150 goto exit;
151 }
152 }
fa33d857 153 }
678484af
KS
154 } else {
155 info("creating symlink '%s' to '%s'", slink, target);
156 selinux_setfscreatecon(slink, NULL, S_IFLNK);
157 retval = symlink(target, slink);
158 selinux_resetfscreatecon();
159 if (retval == 0)
160 goto exit;
fa33d857
KS
161 }
162
e54f0b4a
KS
163 info("atomically replace '%s'", slink);
164 strlcpy(slink_tmp, slink, sizeof(slink_tmp));
165 strlcat(slink_tmp, TMP_FILE_EXT, sizeof(slink_tmp));
14c79942 166 unlink(slink_tmp);
35ea39e2 167 selinux_setfscreatecon(slink, NULL, S_IFLNK);
e54f0b4a
KS
168 retval = symlink(target, slink_tmp);
169 selinux_resetfscreatecon();
170 if (retval != 0) {
171 err("symlink(%s, %s) failed: %s", target, slink_tmp, strerror(errno));
172 goto exit;
173 }
174 retval = rename(slink_tmp, slink);
175 if (retval != 0) {
176 err("rename(%s, %s) failed: %s", slink_tmp, slink, strerror(errno));
177 unlink(slink_tmp);
178 goto exit;
179 }
fa33d857 180exit:
e54f0b4a 181 return retval;
fa33d857
KS
182}
183
24f0605c
KS
184static int update_link(struct udevice *udev, const char *name)
185{
186 LIST_HEAD(name_list);
187 char slink[PATH_SIZE];
188 char node[PATH_SIZE];
189 struct udevice *udev_db;
190 struct name_entry *device;
191 char target[PATH_MAX] = "";
192 int count;
193 int priority = 0;
194 int rc = 0;
195
196 strlcpy(slink, udev_root, sizeof(slink));
197 strlcat(slink, "/", sizeof(slink));
198 strlcat(slink, name, sizeof(slink));
199
200 count = udev_db_get_devices_by_name(name, &name_list);
201 info("found %i devices with name '%s'", count, name);
202
eff4a673 203 /* if we don't have a reference, delete it */
24f0605c
KS
204 if (count <= 0) {
205 info("no reference left, remove '%s'", name);
206 if (!udev->test_run) {
207 unlink(slink);
208 delete_path(slink);
209 }
210 goto out;
211 }
212
213 /* find the device with the highest priority */
214 list_for_each_entry(device, &name_list, node) {
215 info("found '%s' for '%s'", device->name, name);
216
217 /* did we find ourself? we win, if we have the same priority */
218 if (strcmp(udev->dev->devpath, device->name) == 0) {
219 info("compare (our own) priority of '%s' %i >= %i",
220 udev->dev->devpath, udev->link_priority, priority);
b2d1ae72
KS
221 if (strcmp(udev->name, name) == 0) {
222 info("'%s' is our device node, database inconsistent, skip link update", udev->name);
223 } else if (target[0] == '\0' || udev->link_priority >= priority) {
24f0605c
KS
224 priority = udev->link_priority;
225 strlcpy(target, udev->name, sizeof(target));
226 }
227 continue;
228 }
229
b2d1ae72 230 /* another device, read priority from database */
24f0605c
KS
231 udev_db = udev_device_init(NULL);
232 if (udev_db == NULL)
233 continue;
234 if (udev_db_get_device(udev_db, device->name) == 0) {
25c208d6
KS
235 if (strcmp(udev_db->name, name) == 0) {
236 info("'%s' is a device node of '%s', skip link update", udev_db->name, device->name);
237 } else {
238 info("compare priority of '%s' %i > %i",
239 udev_db->dev->devpath, udev_db->link_priority, priority);
240 if (target[0] == '\0' || udev_db->link_priority > priority) {
241 priority = udev_db->link_priority;
242 strlcpy(target, udev_db->name, sizeof(target));
243 }
24f0605c
KS
244 }
245 }
246 udev_device_cleanup(udev_db);
247 }
248 name_list_cleanup(&name_list);
249
250 if (target[0] == '\0') {
25c208d6
KS
251 info("no current target for '%s' found", name);
252 rc = 1;
24f0605c
KS
253 goto out;
254 }
255
256 /* create symlink to the target with the highest priority */
257 strlcpy(node, udev_root, sizeof(node));
258 strlcat(node, "/", sizeof(node));
259 strlcat(node, target, sizeof(node));
260 info("'%s' with target '%s' has the highest priority %i, create it", name, target, priority);
261 if (!udev->test_run) {
262 create_path(slink);
263 node_symlink(node, slink);
264 }
265out:
266 return rc;
267}
268
269void udev_node_update_symlinks(struct udevice *udev, struct udevice *udev_old)
270{
271 struct name_entry *name_loop;
272 char symlinks[PATH_SIZE] = "";
273
274 list_for_each_entry(name_loop, &udev->symlink_list, node) {
275 info("update symlink '%s' of '%s'", name_loop->name, udev->dev->devpath);
276 update_link(udev, name_loop->name);
277 strlcat(symlinks, udev_root, sizeof(symlinks));
278 strlcat(symlinks, "/", sizeof(symlinks));
279 strlcat(symlinks, name_loop->name, sizeof(symlinks));
280 strlcat(symlinks, " ", sizeof(symlinks));
281 }
282
283 /* export symlinks to environment */
284 remove_trailing_chars(symlinks, ' ');
285 if (symlinks[0] != '\0')
286 setenv("DEVLINKS", symlinks, 1);
287
288 /* update possible left-over symlinks (device metadata changed) */
289 if (udev_old != NULL) {
290 struct name_entry *link_loop;
291 struct name_entry *link_old_loop;
24f0605c
KS
292 int found;
293
294 /* remove current symlinks from old list */
eff4a673 295 list_for_each_entry(link_old_loop, &udev_old->symlink_list, node) {
24f0605c
KS
296 found = 0;
297 list_for_each_entry(link_loop, &udev->symlink_list, node) {
298 if (strcmp(link_old_loop->name, link_loop->name) == 0) {
299 found = 1;
300 break;
301 }
302 }
303 if (!found) {
304 /* link does no longer belong to this device */
305 info("update old symlink '%s' no longer belonging to '%s'",
306 link_old_loop->name, udev->dev->devpath);
307 update_link(udev, link_old_loop->name);
308 }
309 }
604f104a 310
eff4a673
KS
311 /*
312 * if the node name has changed, delete the node,
313 * or possibly restore a symlink of another device
314 */
315 if (strcmp(udev->name, udev_old->name) != 0)
316 update_link(udev, udev_old->name);
24f0605c
KS
317 }
318}
319
320int udev_node_add(struct udevice *udev)
97853b4f 321{
63f61c5c 322 char filename[PATH_SIZE];
783272f0
KS
323 uid_t uid;
324 gid_t gid;
e48fc108 325 int i;
a4d5ca64
KS
326 int retval = 0;
327
24f0605c
KS
328 strlcpy(filename, udev_root, sizeof(filename));
329 strlcat(filename, "/", sizeof(filename));
330 strlcat(filename, udev->name, sizeof(filename));
27d4bf18 331 create_path(filename);
218eae87 332
783272f0
KS
333 if (strcmp(udev->owner, "root") == 0)
334 uid = 0;
335 else {
c19a6b30 336 char *endptr;
783272f0 337 unsigned long id;
6d564166 338
783272f0 339 id = strtoul(udev->owner, &endptr, 10);
765cbd97 340 if (endptr[0] == '\0')
c19a6b30 341 uid = (uid_t) id;
57e1a277
KS
342 else
343 uid = lookup_user(udev->owner);
c19a6b30
KS
344 }
345
783272f0
KS
346 if (strcmp(udev->group, "root") == 0)
347 gid = 0;
348 else {
c19a6b30 349 char *endptr;
783272f0 350 unsigned long id;
6d564166 351
783272f0 352 id = strtoul(udev->group, &endptr, 10);
765cbd97 353 if (endptr[0] == '\0')
c19a6b30 354 gid = (gid_t) id;
57e1a277 355 else
68c2c0b5 356 gid = lookup_group(udev->group);
c19a6b30
KS
357 }
358
e54f0b4a 359 info("creating device node '%s', major=%d, minor=%d, mode=%#o, uid=%d, gid=%d",
ad27f5b3
KS
360 filename, major(udev->devt), minor(udev->devt), udev->mode, uid, gid);
361
362 if (!udev->test_run)
a4d5ca64
KS
363 if (udev_node_mknod(udev, filename, udev->devt, udev->mode, uid, gid) != 0) {
364 retval = -1;
365 goto exit;
366 }
ad27f5b3
KS
367
368 setenv("DEVNAME", filename, 1);
50e5de03 369
bbbe503e 370 /* create all_partitions if requested */
fd9efc00 371 if (udev->partitions) {
fb6e4c28 372 char partitionname[PATH_SIZE];
1aa1e248 373 char *attr;
6d564166
KS
374 int range;
375
376 /* take the maximum registered minor range */
1aa1e248 377 attr = sysfs_attr_get_value(udev->dev->devpath, "range");
953249a3 378 if (attr != NULL) {
1aa1e248 379 range = atoi(attr);
6d564166
KS
380 if (range > 1)
381 udev->partitions = range-1;
382 }
7a947ce5
KS
383 info("creating device partition nodes '%s[1-%i]'", filename, udev->partitions);
384 if (!udev->test_run) {
385 for (i = 1; i <= udev->partitions; i++) {
7e720bd4
KS
386 dev_t part_devt;
387
63f61c5c
KS
388 snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i);
389 partitionname[sizeof(partitionname)-1] = '\0';
15139b8a 390 part_devt = makedev(major(udev->devt), minor(udev->devt) + i);
a4d5ca64 391 udev_node_mknod(udev, partitionname, part_devt, udev->mode, uid, gid);
ab2e5bd9 392 }
50e5de03 393 }
3d150dfb 394 }
a4d5ca64 395exit:
a4d5ca64 396 return retval;
ea733a2f
GKH
397}
398
ff9a488d
KS
399int udev_node_remove(struct udevice *udev)
400{
401 char filename[PATH_SIZE];
402 char partitionname[PATH_SIZE];
403 struct stat stats;
eff4a673 404 int retval = 0;
ff9a488d
KS
405 int num;
406
24f0605c
KS
407 strlcpy(filename, udev_root, sizeof(filename));
408 strlcat(filename, "/", sizeof(filename));
409 strlcat(filename, udev->name, sizeof(filename));
a4d5ca64 410 if (stat(filename, &stats) != 0) {
a0092d28
AA
411 info("device node '%s' not found", filename);
412 return 0;
a4d5ca64
KS
413 }
414 if (udev->devt && stats.st_rdev != udev->devt) {
415 info("device node '%s' points to a different device, skip removal", filename);
416 return -1;
417 }
418
419 info("removing device node '%s'", filename);
eff4a673
KS
420 if (!udev->test_run)
421 retval = unlink_secure(filename);
a4d5ca64
KS
422 if (retval)
423 return retval;
424
425 setenv("DEVNAME", filename, 1);
a4d5ca64
KS
426 num = udev->partitions;
427 if (num > 0) {
ff9a488d
KS
428 int i;
429
a4d5ca64 430 info("removing all_partitions '%s[1-%i]'", filename, num);
d7fdcd61 431 if (num > 255)
a4d5ca64 432 return -1;
a4d5ca64
KS
433 for (i = 1; i <= num; i++) {
434 snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i);
435 partitionname[sizeof(partitionname)-1] = '\0';
eff4a673
KS
436 if (!udev->test_run)
437 unlink_secure(partitionname);
5d24c6ca 438 }
f61d732a 439 }
27d4bf18 440 delete_path(filename);
ea733a2f
GKH
441 return retval;
442}