diff options
Diffstat (limited to 'mkdevnodes.c')
-rw-r--r-- | mkdevnodes.c | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/mkdevnodes.c b/mkdevnodes.c new file mode 100644 index 0000000..ccf2d5f --- /dev/null +++ b/mkdevnodes.c @@ -0,0 +1,260 @@ + +/* + * (C) Copyright 2010 Intel Corporation + * + * Author: Auke Kok <auke-jan.h.kok@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> + + +/* + * dev_info - filter table + * + * devices found in sysfs often require different base permissions or + * group/owner ids. To set things up as correctly as possible, we + * traverse a table with overrides. The table is parsed in order and + * multiple fields can hit (e.g. one with the ALL wildcard, and one + * specific for the minor node number). The last field to hit will + * effectively be used. + * + * ALL in the minor node field marks all minor nodes to use that field. + * ALL in the major node field marks the end of the table. + * + * a non-NULL string in the 'dir' field allows you to prepend a + * path between the device node and the /dev/ folder name. This is + * mostly used for alsa and v4l device nodes. + */ + +#define DEV "/dev" + +#define ALL 256 + + +struct dev_info { + unsigned int major; + unsigned int minor; /* 256 = all */ + const char *dir; + const char *owner; + const char *group; + unsigned int perms; + int uid; /* -1 in these fields cause owner/group to be */ + int gid; /* looked up in /etc/group and /etc/passwd */ +}; + +static struct dev_info di_chr[] = { + { 1, 1, NULL, "root", "kmem", 0640, 0, -1 }, /* mem */ + { 1, 2, NULL, "root", "kmem", 0640, 0, -1 }, /* kmem */ + { 1, 3, NULL, "root", "root", 0666, 0, 0 }, /* null */ + { 1, 4, NULL, "root", "kmem", 0640, 0, -1 }, /* port */ + { 1, 5, NULL, "root", "root", 0666, 0, 0 }, /* zero */ + { 1, 7, NULL, "root", "root", 0666, 0, 0 }, /* full */ + { 1, 8, NULL, "root", "root", 0666, 0, 0 }, /* random */ + { 1, 9, NULL, "root", "root", 0666, 0, 0 }, /* urandom */ + { 2, ALL, NULL, "root", "tty", 0660, 0, -1 }, /* pty */ + { 3, ALL, NULL, "root", "tty", 0660, 0, -1 }, /* tty* */ + { 4, ALL, NULL, "root", "tty", 0660, 0, -1 }, /* ttyS* */ + { 5, 0, NULL, "root", "tty", 0666, 0, -1 }, /* tty */ + { 5, 1, NULL, "root", "tty", 0600, 0, -1 }, /* console */ + { 5, 2, NULL, "root", "tty", 0666, 0, -1 }, /* ptmx */ + { 7, ALL, NULL, "root", "tty", 0660, 0, -1 }, /* vcs* */ + { 13, ALL, "input", "root", "video", 0660, 0, -1 }, /* input */ + { 10, 232, NULL, "root", "kvm", 0660, 0, -1 }, /* kvm */ + { 14, ALL, NULL, "root", "audio", 0660, 0, -1 }, /* oss */ + { 21, ALL, NULL, "root", "disk", 0640, 0, -1 }, /* sg* */ + { 29, ALL, NULL, "root", "video", 0600, 0, -1 }, /* fb* */ + { 81, ALL, "v4l", "root", "video", 0660, 0, -1 }, /* v4l */ + { 116, ALL, "snd", "root", "audio", 0660, 0, -1 }, /* alsa */ + { 189, ALL, "usb", "root", "root", 0600, 0, 0 }, /* usb */ + { 226, ALL, "dri", "root", "video", 0660, 0, 0 }, /* dri */ + + /* terminate */ + { ALL, 0, NULL, NULL, NULL, 0, 0, 0 }, +}; + +static struct dev_info di_blk[] = { + { 1, ALL, NULL, "root", "disk", 0640, 0, -1 }, /* ram* */ + { 8, ALL, NULL, "root", "disk", 0640, 0, -1 }, /* sd* */ + { 11, ALL, NULL, "root", "cdrom", 0640, 0, -1 }, /* sr* */ + /* terminate */ + { ALL, 0, NULL, NULL, NULL, 0, 0, 0 }, +}; + +static void node_add(int type, unsigned int maj, unsigned int min, + char* name, struct dev_info *di) +{ + struct passwd *p; + struct group *g; + unsigned int perms = 0660; + int owner = 0; + int group = 0; + char path[PATH_MAX]; + int n = 0; + + /* defaults */ + snprintf(path, PATH_MAX, "%s/%s", DEV, name); + + while (di[n].major != ALL) { + if (maj != di[n].major) + goto out; + if ((min != di[n].minor) && (di[n].minor != ALL)) + goto out; + + if (di[n].dir != NULL) { + snprintf(path, PATH_MAX, "%s/%s", DEV, di[n].dir); + mkdir(path, 0755); + snprintf(path, PATH_MAX, "%s/%s/%s", DEV, di[n].dir, name); + } + + perms = di[n].perms; + + if (di[n].uid == -1) { + p = getpwnam(di[n].owner); + if (p) { + owner = p->pw_uid; + di[n].uid = owner; + } + } else { + owner = di[n].uid; + } + + if (di[n].gid == -1) { + g = getgrnam(di[n].group); + if (g) { + group = g->gr_gid; + di[n].gid = group; + } + } else { + group = di[n].gid; + } + /* don't break, allow more filters to hit */ +out: + n++; + } + + if ((owner == 0) && (group == 0)) { + mknod(path, type | perms, makedev(maj, min)); + } else { + if (mknod(path, type, makedev(maj, min))) + return; + if (chown(path, owner, group)) + return; + chmod(path, perms); + } +} + +static void walk(const char *dir, int type, struct dev_info *di) +{ + DIR *d; + struct dirent *entry; + char path[PATH_MAX]; + ssize_t len; + unsigned int maj; + unsigned int min; + + d = opendir(dir); + if (!d) + return; + + while ((entry = readdir(d))) { + char p[PATH_MAX]; + + if ((strcmp(".", entry->d_name) == 0) || + (strcmp("..", entry->d_name) == 0)) + continue; + + sscanf(entry->d_name, "%d:%d", &maj, &min); + + snprintf(p, PATH_MAX, "%s/%s", dir, entry->d_name); + len = readlink(p, path, PATH_MAX - 1); + if (len != -1) + path[len] = '\0'; + + node_add(type, maj, min, basename(path), di); + + } + + closedir(d); +} + +void populate_dev_blk(void) +{ + int u = umask(0000); + walk("/sys/dev/block", S_IFBLK, di_blk); + umask(u); +} + +void populate_dev_chr(void) +{ + int u = umask(0000); + walk("/sys/dev/char", S_IFCHR, di_chr); + umask(u); +} + +static struct option opts[] = { + { "block", 0, NULL, 'b' }, + { "char", 0, NULL, 'c' }, + { "help", 0, NULL, 'h' }, + { 0, 0, NULL, 0 } +}; + +void usage(const char *name) +{ + printf("Usage: %s [OPTION...]\n", name); + printf(" -b, --block Only create block device nodes\n"); + printf(" -c, --char Only create char device nodes \n"); + printf(" -h, --help Show this help message\n"); +} + +int main(int argc, char **argv) +{ + int c; + int i; + int blk = 1; + int chr = 1; + + while (1) { + c = getopt_long(argc, argv, "bch", opts, &i); + if (c == -1) + break; + + switch (c) { + case 'b': + chr = 0; + break; + case 'c': + blk = 0; + break; + case 'h': + usage(argv[0]); + exit (EXIT_SUCCESS); + break; + default: + break; + } + } + + if (blk) + populate_dev_blk(); + if (chr) + populate_dev_chr(); + exit (EXIT_SUCCESS); +} |