--- /dev/null
+
+/*
+ * (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);
+}