smdev

suckless mdev
git clone git://git.2f30.org/smdev
Log | Files | Refs | README | LICENSE

smdev.c (8663B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/ioctl.h>
      3 #include <sys/stat.h>
      4 #include <sys/types.h>
      5 
      6 #include <linux/sockios.h>
      7 #include <linux/if_packet.h>
      8 #include <net/if.h>
      9 #include <netinet/in.h>
     10 #include <ifaddrs.h>
     11 
     12 #include <errno.h>
     13 #include <fcntl.h>
     14 #include <grp.h>
     15 #include <libgen.h>
     16 #include <limits.h>
     17 #include <pwd.h>
     18 #include <regex.h>
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <unistd.h>
     23 
     24 #include "config.h"
     25 #include "mkpath.h"
     26 #include "util.h"
     27 
     28 enum action {
     29 	ADD_ACTION,
     30 	REMOVE_ACTION,
     31 	UNKNOWN_ACTION
     32 };
     33 
     34 struct event {
     35 	int minor;
     36 	int major;
     37 	enum action action;
     38 	char *devpath;
     39 	char *devname;
     40 	struct rule *rule;
     41 };
     42 
     43 /* Simple cache for regcomp() results */
     44 static struct pregentry {
     45 	regex_t preg;
     46 	int cached;
     47 } pregcache[LEN(rules)];
     48 
     49 /* The expanded/parsed path components of a rule */
     50 struct rulepath {
     51 	char path[PATH_MAX];
     52 	char name[PATH_MAX];
     53 };
     54 
     55 static int dohotplug(void);
     56 static int matchrule(int, char *);
     57 static void runrulecmd(struct rule *);
     58 static void parsepath(struct rule *, struct rulepath *, const char *);
     59 static int removedev(struct event *);
     60 static int createdev(struct event *);
     61 static int doevent(struct event *);
     62 static int craftev(char *);
     63 static void populatedev(const char *);
     64 static int ifrename(void);
     65 
     66 static void
     67 usage(void)
     68 {
     69 	eprintf("usage: %s [-s]\n", argv0);
     70 }
     71 
     72 int
     73 main(int argc, char *argv[])
     74 {
     75 	int sflag = 0;
     76 	int i;
     77 
     78 	ARGBEGIN {
     79 	case 's':
     80 		sflag = 1;
     81 		break;
     82 	default:
     83 		usage();
     84 	} ARGEND;
     85 
     86 	umask(0);
     87 	if (sflag) {
     88 		recurse("/sys/devices", populatedev);
     89 	} else {
     90 		if (dohotplug() < 0)
     91 			eprintf("Environment not set up correctly for hotplugging\n");
     92 	}
     93 
     94 	for (i = 0; i < LEN(pregcache); i++)
     95 		if (pregcache[i].cached)
     96 			regfree(&pregcache[i].preg);
     97 
     98 	if (ifrename() < 0)
     99 		return EXIT_FAILURE;
    100 
    101 	return EXIT_SUCCESS;
    102 }
    103 
    104 static enum action
    105 mapaction(const char *action)
    106 {
    107 	if (strcmp(action, "add") == 0)
    108 		return ADD_ACTION;
    109 	if (strcmp(action, "remove") == 0)
    110 		return REMOVE_ACTION;
    111 	return UNKNOWN_ACTION;
    112 }
    113 
    114 /* Handle hotplugging events */
    115 static int
    116 dohotplug(void)
    117 {
    118 	char *minor, *major;
    119 	char *action;
    120 	char *devpath;
    121 	char *devname;
    122 	struct event ev;
    123 
    124 	minor = getenv("MINOR");
    125 	major = getenv("MAJOR");
    126 	action = getenv("ACTION");
    127 	devpath = getenv("DEVPATH");
    128 	devname = getenv("DEVNAME");
    129 	if (!minor || !major || !action || !devpath || !devname)
    130 		return -1;
    131 
    132 	ev.minor = estrtol(minor, 10);
    133 	ev.major = estrtol(major, 10);
    134 	ev.action = mapaction(action);
    135 	ev.devpath = devpath;
    136 	ev.devname = devname;
    137 	return doevent(&ev);
    138 }
    139 
    140 /*
    141  * `ruleidx' indexes into the rules[] table.  We assume
    142  * pregcache[] is mapped 1-1 with the rules[] table.
    143  */
    144 static int
    145 matchrule(int ruleidx, char *devname)
    146 {
    147 	struct rule *rule = &rules[ruleidx];
    148 	regex_t *preg;
    149 	regmatch_t pmatch;
    150 	int ret;
    151 
    152 	if (!pregcache[ruleidx].cached) {
    153 		ret = regcomp(&pregcache[ruleidx].preg,
    154 			      rule->devregex, REG_EXTENDED);
    155 		if (ret < 0)
    156 			eprintf("regcomp:");
    157 		pregcache[ruleidx].cached = 1;
    158 	}
    159 	preg = &pregcache[ruleidx].preg;
    160 
    161 	ret = regexec(preg, devname, 1, &pmatch, 0);
    162 	if (ret == REG_NOMATCH || pmatch.rm_so ||
    163 	    pmatch.rm_eo != strlen(devname))
    164 		return -1;
    165 	return 0;
    166 }
    167 
    168 static void
    169 runrulecmd(struct rule *rule)
    170 {
    171 	if (rule->cmd)
    172 		system(&rule->cmd[1]);
    173 }
    174 
    175 static void
    176 parsepath(struct rule *rule, struct rulepath *rpath,
    177 	  const char *devname)
    178 {
    179 	char buf[PATH_MAX], *path, *dirc;
    180 	const char *basedevname;
    181 
    182 	if(!(basedevname = strrchr(devname, '/')))
    183 		basedevname = devname;
    184 
    185 	if (!rule->path) {
    186 		strlcpy(rpath->name, basedevname, sizeof(rpath->name));
    187 		snprintf(rpath->path, sizeof(rpath->path), "/dev/%s",
    188 			 rpath->name);
    189 		return;
    190 	}
    191 
    192 	if (rule->path[0] != '=' && rule->path[0] != '>')
    193 		eprintf("Invalid path '%s'\n", rule->path);
    194 
    195 	path = strdup(&rule->path[1]);
    196 	if (!path)
    197 		eprintf("strdup:");
    198 
    199 	/* No need to rename the device node */
    200 	if (rule->path[strlen(rule->path) - 1] == '/') {
    201 		snprintf(rpath->path, sizeof(rpath->path), "/dev/%s%s",
    202 			 path, basedevname);
    203 		strlcpy(rpath->name, basedevname, sizeof(rpath->name));
    204 		free(path);
    205 		return;
    206 	}
    207 
    208 	if (strchr(path, '/')) {
    209 		if (!(dirc = strdup(path)))
    210 			eprintf("strdup:");
    211 		snprintf(buf, sizeof(buf), "/dev/%s", dirname(dirc));
    212 		strlcpy(rpath->name, basename(path), sizeof(rpath->name));
    213 		snprintf(rpath->path, sizeof(rpath->path), "%s/%s", buf,
    214 			 rpath->name);
    215 		free(dirc);
    216 	} else {
    217 		strlcpy(rpath->name, path, sizeof(rpath->name));
    218 		snprintf(rpath->path, sizeof(rpath->path), "/dev/%s",
    219 			 rpath->name);
    220 	}
    221 
    222 	free(path);
    223 }
    224 
    225 static int
    226 removedev(struct event *ev)
    227 {
    228 	struct rule *rule;
    229 	struct rulepath rpath;
    230 	char *ocwd;
    231 	char buf[PATH_MAX];
    232 
    233 	rule = ev->rule;
    234 	ocwd = agetcwd();
    235 
    236 	parsepath(rule, &rpath, ev->devname);
    237 
    238 	if(rule->cmd) {
    239 		if (chdir("/dev") < 0)
    240 			eprintf("chdir /dev:");
    241 		runrulecmd(rule);
    242 	}
    243 
    244 	if (chdir(ocwd) < 0)
    245 		eprintf("chdir %s:", ocwd);
    246 
    247 	free(ocwd);
    248 
    249 	if (rule->path && rule->path[0] == '!')
    250 		return 0;
    251 
    252 	/* Delete device node */
    253 	unlink(rpath.path);
    254 	/* Delete symlink */
    255 	if (rule->path && rule->path[0] == '>') {
    256 		snprintf(buf, sizeof(buf), "/dev/%s", ev->devname);
    257 		unlink(buf);
    258 	}
    259 	return 0;
    260 }
    261 
    262 static int
    263 createdev(struct event *ev)
    264 {
    265 	struct rule *rule;
    266 	struct rulepath rpath;
    267 	struct passwd *pw;
    268 	struct group *gr;
    269 	char *dirc, *ocwd;
    270 	char buf[BUFSIZ];
    271 	int type;
    272 
    273 	rule = ev->rule;
    274 	ocwd = agetcwd();
    275 
    276 	if (rule->path && rule->path[0] == '!')
    277 		goto runrule;
    278 
    279 	snprintf(buf, sizeof(buf), "%d:%d", ev->major, ev->minor);
    280 	if ((type = devtype(buf)) < 0)
    281 		return -1;
    282 
    283 	/* Parse path and create the directory tree */
    284 	parsepath(rule, &rpath, ev->devname);
    285 	if (!(dirc = strdup(rpath.path)))
    286 		eprintf("strdup:");
    287 	strlcpy(buf, dirname(dirc), sizeof(buf));
    288 	free(dirc);
    289 	umask(022);
    290 	if (mkpath(buf, 0755) < 0)
    291 		eprintf("mkdir %s:", buf);
    292 	umask(0);
    293 
    294 	if (mknod(rpath.path, rule->mode | type,
    295 		  makedev(ev->major, ev->minor)) < 0 &&
    296 	    errno != EEXIST)
    297 		eprintf("mknod %s:", rpath.path);
    298 
    299 	errno = 0;
    300 	pw = getpwnam(rule->user);
    301 	if (!pw) {
    302 		if (errno)
    303 			eprintf("getpwnam %s:", rule->user);
    304 		else
    305 			eprintf("getpwnam %s: no such user\n",
    306 				 rule->user);
    307 	}
    308 
    309 	errno = 0;
    310 	gr = getgrnam(rule->group);
    311 	if (!gr) {
    312 		if (errno)
    313 			eprintf("getgrnam %s:", rule->group);
    314 		else
    315 			eprintf("getgrnam %s: no such group\n",
    316 				 rule->group);
    317 	}
    318 
    319 	if (chown(rpath.path, pw->pw_uid, gr->gr_gid) < 0)
    320 		eprintf("chown %s:", rpath.path);
    321 
    322 	if (chmod(rpath.path, rule->mode) < 0)
    323 		eprintf("chmod %s:", rpath.path);
    324 
    325 	if (rule->path && rule->path[0] == '>') {
    326 		/* ev->devname is the original device name */
    327 		snprintf(buf, sizeof(buf), "/dev/%s", ev->devname);
    328 		symlink(rpath.path, buf);
    329 	}
    330 
    331 runrule:
    332 	if(rule->cmd) {
    333 		if (chdir("/dev") < 0)
    334 			eprintf("chdir /dev:");
    335 		runrulecmd(rule);
    336 	}
    337 
    338 	if (chdir(ocwd) < 0)
    339 		eprintf("chdir %s:", ocwd);
    340 
    341 	free(ocwd);
    342 
    343 	return 0;
    344 }
    345 
    346 /* Event dispatcher */
    347 static int
    348 doevent(struct event *ev)
    349 {
    350 	int i;
    351 
    352 	for (i = 0; i < LEN(rules); i++) {
    353 		if (matchrule(i, ev->devname) < 0)
    354 			continue;
    355 		ev->rule = &rules[i];
    356 		switch (ev->action) {
    357 		case ADD_ACTION:
    358 			return createdev(ev);
    359 		case REMOVE_ACTION:
    360 			return removedev(ev);
    361 		default:
    362 			return 0;
    363 		}
    364 	}
    365 	return 0;
    366 }
    367 
    368 /* Craft a fake event so the rest of the code can cope */
    369 static int
    370 craftev(char *sysfspath)
    371 {
    372 	char path[PATH_MAX];
    373 	char *devpath;
    374 
    375 	devpath = sysfspath + strlen("/sys");
    376 	snprintf(path, sizeof(path), "/sys%s/uevent", devpath);
    377 
    378 	clearenv();
    379 	setenv("ACTION", "add", 1);
    380 	setenv("DEVPATH", devpath, 1);
    381 	if(readuevent(path) < 0)
    382 		return -1;
    383 	return 0;
    384 }
    385 
    386 static void
    387 populatedev(const char *path)
    388 {
    389 	char *cwd;
    390 
    391 	recurse(path, populatedev);
    392 	if (strcmp(path, "dev") == 0) {
    393 		cwd = agetcwd();
    394 		if (!craftev(cwd))
    395 			dohotplug();
    396 		free(cwd);
    397 	}
    398 }
    399 
    400 static int
    401 ifrename(void)
    402 {
    403 	struct sockaddr_ll *sa;
    404 	struct ifaddrs *ifas, *ifa;
    405 	struct ifreq ifr;
    406 	int sd;
    407 	int i;
    408 	int r;
    409 	int ok = 0;
    410 
    411 	sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    412 	if (sd < 0)
    413 		eprintf("socket:");
    414 	r = getifaddrs(&ifas);
    415 	if (r < 0)
    416 		eprintf("getifaddrs:");
    417 	for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
    418 		if (ifa->ifa_flags & IFF_LOOPBACK)
    419 			continue;
    420 		if (ifa->ifa_addr->sa_family != AF_PACKET)
    421 			continue;
    422 		sa = (struct sockaddr_ll *)ifa->ifa_addr;
    423 		for (i = 0; mac2names[i].name; i++) {
    424 			if (memcmp(mac2names[i].mac, sa->sll_addr, 6) != 0)
    425 				continue;
    426 			memset(&ifr, 0, sizeof(ifr));
    427 			strlcpy(ifr.ifr_name,
    428 				ifa->ifa_name, sizeof(ifr.ifr_name));
    429 			strlcpy(ifr.ifr_newname,
    430 				mac2names[i].name, sizeof(ifr.ifr_newname));
    431 			r = ioctl(sd, SIOCSIFNAME, &ifr);
    432 			if (r < 0) {
    433 				weprintf("SIOCSIFNAME:");
    434 				ok = -1;
    435 			}
    436 		}
    437 	}
    438 	freeifaddrs(ifas);
    439 	close(sd);
    440 	return ok;
    441 }