aboutsummaryrefslogtreecommitdiffstats
path: root/nldev-handler.c
blob: 4fa6e2abd1143c698545f9b62b184b2f219d83a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "log.h"

#define EVENT_TIMEOUT 2000

void
child(char *runpath)
{
	pid_t pid;

	if (!(pid = fork())) {
		dbg("running %s", runpath);
		if (execlp(runpath, basename(runpath), NULL) < 0)
			edie("execvp");
		exit(0);
	}
	if (pid < 0)
		edie("fork");

	waitpid(pid, NULL, 0);
}


void
usage(void)
{
	die("usage: %s [-d] [-r run]\n", argv0);
}

int main(int argc, char *argv[])
{
	struct pollfd fds;
	int r;
	static char buf[16384];
	char *runpath = "/bin/mdev";
	char *subsystem = NULL;

	ARGBEGIN {
	case 'd':
		dodebug = 1;
		break;
	case 'r':
		runpath = EARGF(usage());
		break;
	case 'f':
		subsystem = EARGF(usage());
		break;
	default:
		usage();
	} ARGEND;

	dbg("runpath=%s\n", runpath);

	fds.fd = 0; /* stdin */
	fds.events = POLLIN;
	while ((r = poll(&fds, 1, EVENT_TIMEOUT)) > 0) {
		size_t len;
		int i, slen;
		char *key, *value;

		clearenv();
		setenv("PATH", "/sbin:/bin", 1);

		if (!(fds.revents & POLLIN))
			continue;

		len = read(fds.fd, buf, sizeof(buf)-1);
		buf[len] = 0;
		dbg("Got %u bytes", len);

		slen = 0;
		for (i = 0; i < len; i += slen + 1) {
			key = buf + i;
			value = strchr(key, '=');
			slen = strlen(buf+i);

			if (!slen || value == NULL)
				continue;
			if (subsystem && !strncmp(key, "SUBSYSTEM=", 10)
					&& !strstr(key+10, subsystem)) {
				dbg("subsystem filter '%s' applied.",
						subsystem);
				break;
			}

			value[0] = '\0';
			value++;

			/*
			 * We generally trust the kernel. But there
			 * might be some udev flaw. (It's >20k sloc!)
			 */
			if (strcmp(key, "PATH")) {
				setenv(key, value, 1);
				dbg("%s = \"%s\"", key, value);
			}
		}
		if (getenv("ACTION") != NULL &&
				getenv("DEVPATH") != NULL &&
				getenv("SUBSYSTEM") != NULL &&
				getenv("SEQNUM") != NULL) {
			child(runpath);
		}

		if (fds.revents & POLLHUP) {
			dbg("parent hung up\n");
			return 0;
		}
	}
	if (r == -1)
		edie("poll");

	if (r == 0)
		dbg("exit due to timeout");

	return 0;
}