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 }