smdev.c - smdev - suckless mdev
 (HTM) git clone git://git.suckless.org/smdev
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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 }