xref: /third_party/eudev/test/test-udev.c (revision 99ca880a)
1/***
2  This file is part of systemd.
3
4  Copyright 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
5  Copyright 2004-2012 Kay Sievers <kay@vrfy.org>
6
7  systemd is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Lesser General Public License as published by
9  the Free Software Foundation; either version 2.1 of the License, or
10  (at your option) any later version.
11
12  systemd is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License
18  along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <stdio.h>
22#include <stddef.h>
23#include <stdlib.h>
24#include <string.h>
25#include <fcntl.h>
26#include <ctype.h>
27#include <errno.h>
28#include <unistd.h>
29#include <grp.h>
30#include <sched.h>
31#include <sys/mount.h>
32#include <sys/signalfd.h>
33
34#include "missing.h"
35#include "udev.h"
36#include "udev-util.h"
37
38#ifndef HAVE_UNSHARE
39#include <sys/syscall.h>
40/* Provide our own replacement with local reach*/
41static inline int unshare (int x) { return syscall(SYS_unshare, x); }
42#endif
43
44#ifndef _USE_GNU
45/* Make sure CLONE_NEWNS macro is available */
46#include <linux/sched.h>
47#endif
48
49static int fake_filesystems(void) {
50        static const struct fakefs {
51                const char *src;
52                const char *target;
53                const char *error;
54        } fakefss[] = {
55                { "test/sys", "/sys",                   "failed to mount test /sys" },
56                { "test/dev", "/dev",                   "failed to mount test /dev" },
57                { "test/run", UDEV_ROOT_RUN,            "failed to mount test " UDEV_ROOT_RUN },
58                { "test/run", "/etc/udev/rules.d",      "failed to mount empty /etc/udev/rules.d" },
59                { "test/run", "/lib/udev/rules.d",      "failed to mount empty /lib/udev/rules.d" },
60        };
61        unsigned int i;
62        int err;
63
64        err = unshare(CLONE_NEWNS);
65        if (err < 0) {
66                err = -errno;
67                fprintf(stderr, "failed to call unshare(): %m\n");
68                goto out;
69        }
70
71        if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0) {
72                err = -errno;
73                fprintf(stderr, "failed to mount / as private: %m\n");
74                goto out;
75        }
76
77        for (i = 0; i < ELEMENTSOF(fakefss); i++) {
78                err = mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL);
79                if (err < 0) {
80                        err = -errno;
81                        fprintf(stderr, "%s %m", fakefss[i].error);
82                        return err;
83                }
84        }
85out:
86        return err;
87}
88
89int main(int argc, char *argv[]) {
90        _cleanup_udev_unref_ struct udev *udev = NULL;
91        _cleanup_udev_event_unref_ struct udev_event *event = NULL;
92        _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
93        _cleanup_udev_rules_unref_ struct udev_rules *rules = NULL;
94        char syspath[UTIL_PATH_SIZE];
95        const char *devpath;
96        const char *action;
97        sigset_t mask, sigmask_orig;
98        int err;
99
100        err = fake_filesystems();
101        if (err < 0)
102                return EXIT_FAILURE;
103
104        udev = udev_new();
105        if (udev == NULL)
106                return EXIT_FAILURE;
107
108        log_debug("version %s", VERSION);
109        mac_selinux_init("/dev");
110
111        sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
112
113        action = argv[1];
114        if (action == NULL) {
115                log_error("action missing");
116                goto out;
117        }
118
119        devpath = argv[2];
120        if (devpath == NULL) {
121                log_error("devpath missing");
122                goto out;
123        }
124
125        rules = udev_rules_new(udev, 1);
126
127        strscpyl(syspath, sizeof(syspath), "/sys", devpath, NULL);
128        dev = udev_device_new_from_synthetic_event(udev, syspath, action);
129        if (dev == NULL) {
130                log_debug("unknown device '%s'", devpath);
131                goto out;
132        }
133
134        event = udev_event_new(dev);
135
136        sigfillset(&mask);
137        sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
138        event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
139        if (event->fd_signal < 0) {
140                fprintf(stderr, "error creating signalfd\n");
141                goto out;
142        }
143
144        /* do what devtmpfs usually provides us */
145        if (udev_device_get_devnode(dev) != NULL) {
146                mode_t mode = 0600;
147
148                if (streq(udev_device_get_subsystem(dev), "block"))
149                        mode |= S_IFBLK;
150                else
151                        mode |= S_IFCHR;
152
153                if (!streq(action, "remove")) {
154                        mkdir_parents_label(udev_device_get_devnode(dev), 0755);
155                        mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev));
156                } else {
157                        unlink(udev_device_get_devnode(dev));
158                        rmdir_parents(udev_device_get_devnode(dev), "/");
159                }
160        }
161
162        udev_event_execute_rules(event,
163                                 3 * USEC_PER_SEC, USEC_PER_SEC,
164                                 NULL,
165                                 rules,
166                                 &sigmask_orig);
167        udev_event_execute_run(event,
168                               3 * USEC_PER_SEC, USEC_PER_SEC,
169                               NULL);
170out:
171        if (event != NULL && event->fd_signal >= 0)
172                close(event->fd_signal);
173        mac_selinux_finish();
174
175        return err ? EXIT_FAILURE : EXIT_SUCCESS;
176}
177