Initial commit - smdev - suckless mdev | |
git clone git://git.suckless.org/smdev | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 733f60dba2b8c878945b856c23a6bc44de52fc11 | |
Author: sin <[email protected]> | |
Date: Tue, 20 Aug 2013 13:26:10 +0100 | |
Initial commit | |
Diffstat: | |
A LICENSE | 21 +++++++++++++++++++++ | |
A Makefile | 59 +++++++++++++++++++++++++++++… | |
A arg.h | 55 +++++++++++++++++++++++++++++… | |
A config.h | 26 ++++++++++++++++++++++++++ | |
A config.mk | 12 ++++++++++++ | |
A smdev.c | 209 +++++++++++++++++++++++++++++… | |
A util.h | 13 +++++++++++++ | |
A util/agetcwd.c | 18 ++++++++++++++++++ | |
A util/apathmax.c | 25 +++++++++++++++++++++++++ | |
A util/eprintf.c | 46 +++++++++++++++++++++++++++++… | |
A util/estrtol.c | 27 +++++++++++++++++++++++++++ | |
A util/recurse.c | 40 +++++++++++++++++++++++++++++… | |
12 files changed, 551 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/LICENSE b/LICENSE | |
@@ -0,0 +1,21 @@ | |
+MIT/X Consortium License | |
+ | |
+© 2013 sin <[email protected]> | |
+ | |
+Permission is hereby granted, free of charge, to any person obtaining a | |
+copy of this software and associated documentation files (the "Software"), | |
+to deal in the Software without restriction, including without limitation | |
+the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
+and/or sell copies of the Software, and to permit persons to whom the | |
+Software is furnished to do so, subject to the following conditions: | |
+ | |
+The above copyright notice and this permission notice shall be included in | |
+all copies or substantial portions of the Software. | |
+ | |
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
+DEALINGS IN THE SOFTWARE. | |
diff --git a/Makefile b/Makefile | |
@@ -0,0 +1,59 @@ | |
+include config.mk | |
+ | |
+.POSIX: | |
+.SUFFIXES: .c .o | |
+ | |
+LIB = \ | |
+ util/agetcwd.o \ | |
+ util/apathmax.o \ | |
+ util/eprintf.o \ | |
+ util/estrtol.o \ | |
+ util/recurse.o | |
+ | |
+SRC = smdev.c | |
+ | |
+OBJ = $(SRC:.c=.o) $(LIB) | |
+BIN = $(SRC:.c=) | |
+MAN = $(SRC:.c=.1) | |
+ | |
+all: options binlib | |
+ | |
+options: | |
+ @echo mdev build options: | |
+ @echo "CFLAGS = $(CFLAGS)" | |
+ @echo "LDFLAGS = $(LDFLAGS)" | |
+ @echo "CC = $(CC)" | |
+ | |
+binlib: util.a | |
+ $(MAKE) bin | |
+ | |
+bin: $(BIN) | |
+ | |
+$(OBJ): util.h config.mk | |
+ | |
+.o: | |
+ @echo LD $@ | |
+ @$(LD) -o $@ $< util.a $(LDFLAGS) | |
+ | |
+.c.o: | |
+ @echo CC $< | |
+ @$(CC) -c -o $@ $< $(CFLAGS) | |
+ | |
+util.a: $(LIB) | |
+ @echo AR $@ | |
+ @$(AR) -r -c $@ $(LIB) | |
+ @ranlib $@ | |
+ | |
+install: all | |
+ @echo installing executable to $(DESTDIR)$(PREFIX)/sbin | |
+ @mkdir -p $(DESTDIR)$(PREFIX)/sbin | |
+ @cp -f $(BIN) $(DESTDIR)$(PREFIX)/sbin | |
+ @cd $(DESTDIR)$(PREFIX)/sbin && chmod 755 $(BIN) | |
+ | |
+uninstall: | |
+ @echo removing executable from $(DESTDIR)$(PREFIX)/sbin | |
+ @cd $(DESTDIR)$(PREFIX)/sbin && rm -f $(BIN) | |
+ | |
+clean: | |
+ @echo cleaning | |
+ @rm -f $(BIN) $(OBJ) $(LIB) util.a | |
diff --git a/arg.h b/arg.h | |
@@ -0,0 +1,55 @@ | |
+/* | |
+ * Copy me if you can. | |
+ * by 20h | |
+ */ | |
+ | |
+#ifndef __ARG_H__ | |
+#define __ARG_H__ | |
+ | |
+extern char *argv0; | |
+ | |
+#define USED(x) ((void)(x)) | |
+ | |
+/* use main(int argc, char *argv[]) */ | |
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ | |
+ argv[0] && argv[0][1]\ | |
+ && argv[0][0] == '-';\ | |
+ argc--, argv++) {\ | |
+ char _argc;\ | |
+ char **_argv;\ | |
+ int brk;\ | |
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\ | |
+ argv++;\ | |
+ argc--;\ | |
+ break;\ | |
+ }\ | |
+ for (brk = 0, argv[0]++, _argv = argv;\ | |
+ argv[0][0] && !brk;\ | |
+ argv[0]++) {\ | |
+ if (_argv != argv)\ | |
+ break;\ | |
+ _argc = argv[0][0];\ | |
+ switch (_argc) | |
+ | |
+#define ARGEND }\ | |
+ USED(_argc);\ | |
+ }\ | |
+ USED(argv);\ | |
+ USED(argc); | |
+ | |
+#define ARGC() _argc | |
+ | |
+#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ | |
+ ((x), abort(), (char *)0) :\ | |
+ (brk = 1, (argv[0][1] != '\0')?\ | |
+ (&argv[0][1]) :\ | |
+ (argc--, argv++, argv[0]))) | |
+ | |
+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ | |
+ (char *)0 :\ | |
+ (brk = 1, (argv[0][1] != '\0')?\ | |
+ (&argv[0][1]) :\ | |
+ (argc--, argv++, argv[0]))) | |
+ | |
+#endif | |
+ | |
diff --git a/config.h b/config.h | |
@@ -0,0 +1,26 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+struct { | |
+ const char *devregex; | |
+ const char *user; | |
+ const char *group; | |
+ int mode; | |
+ const char *path; | |
+ const char *cmd; | |
+} Rules[] = { | |
+ { "null", "root", "root", 0666, NULL, "@chmod 666 $SMDEV" }, | |
+ { "zero", "root", "root", 0666, NULL, NULL }, | |
+ { "full", "root", "root", 0666, NULL, NULL }, | |
+ { "random", "root", "root", 0666, NULL, NULL }, | |
+ { "urandom", "root", "root", 0666, NULL, NULL }, | |
+ { "mem", "root", "root", 0640, NULL, NULL }, | |
+ { "kmem", "root", "root", 0640, NULL, NULL }, | |
+ { "console", "root", "tty", 0600, NULL, "@chmod 600 $SMDEV" }, | |
+ { "ptmx", "root", "tty", 0666, NULL, NULL }, | |
+ { "pty.*", "root", "tty", 0660, NULL, NULL }, | |
+ { "tty", "root", "tty", 0666, NULL, NULL }, | |
+ { "tty[0-9]*", "root", "tty", 0660, NULL, NULL }, | |
+ { "vcsa*[0-9]*","root", "tty", 0660, NULL, NULL }, | |
+ { "sd[a-z].*", "root", "disk", 0660, NULL, NULL }, | |
+ { "sr[0-9]*", "root", "cdrom", 0660, NULL, "@ln -sf $SMDEV cdrom" }, | |
+ { ".*", "root", "root", 0777, NULL, NULL }, | |
+}; | |
diff --git a/config.mk b/config.mk | |
@@ -0,0 +1,12 @@ | |
+# smdev version | |
+VERSION = 0.0 | |
+ | |
+# paths | |
+PREFIX = /usr/local | |
+ | |
+#CC = gcc | |
+#CC = musl-gcc | |
+LD = $(CC) | |
+CPPFLAGS = -D_BSD_SOURCE -D_GNU_SOURCE | |
+CFLAGS = -g -ansi -Wall $(CPPFLAGS) | |
+LDFLAGS = -g | |
diff --git a/smdev.c b/smdev.c | |
@@ -0,0 +1,209 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <sys/types.h> | |
+#include <sys/stat.h> | |
+#include <fcntl.h> | |
+#include <unistd.h> | |
+#include <errno.h> | |
+#include <pwd.h> | |
+#include <grp.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <limits.h> | |
+#include <regex.h> | |
+#include "config.h" | |
+#include "util.h" | |
+ | |
+static int devtomajmin(const char *path, int *maj, int *min); | |
+static int devtype(const char *majmin); | |
+static int create_dev(const char *path); | |
+static void sysrecurse(const char *path); | |
+ | |
+static void | |
+usage(void) | |
+{ | |
+ eprintf("usage: %s [-s]\n", argv0); | |
+} | |
+ | |
+int | |
+main(int argc, char *argv[]) | |
+{ | |
+ int sflag = 0; | |
+ | |
+ ARGBEGIN { | |
+ case 's': | |
+ sflag = 1; | |
+ break; | |
+ default: | |
+ usage(); | |
+ } ARGEND; | |
+ | |
+ if (!sflag) | |
+ usage(); | |
+ | |
+ recurse("/sys/devices", sysrecurse); | |
+ | |
+ return 0; | |
+} | |
+ | |
+/* Example `path' is /sys/devices/virtual/tty/tty0/dev */ | |
+static int | |
+devtomajmin(const char *path, int *maj, int *min) | |
+{ | |
+ char buf[BUFSIZ]; | |
+ int fd; | |
+ ssize_t n; | |
+ | |
+ fd = open(path, O_RDONLY); | |
+ if (fd < 0) | |
+ eprintf("open %s:", path); | |
+ n = read(fd, buf, sizeof(buf) - 1); | |
+ close(fd); | |
+ if (n < 0) | |
+ eprintf("%s: read error:", path); | |
+ if (!n) | |
+ return -1; | |
+ if (buf[n - 1] == '\n') | |
+ buf[n - 1] = '\0'; | |
+ buf[n] = '\0'; | |
+ sscanf(buf, "%d:%d", maj, min); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+devtype(const char *majmin) | |
+{ | |
+ char path[PATH_MAX]; | |
+ | |
+ snprintf(path, sizeof(path), "/sys/dev/block/%s", majmin); | |
+ if (!access(path, F_OK)) | |
+ return S_IFBLK; | |
+ snprintf(path, sizeof(path), "/sys/dev/char/%s", majmin); | |
+ if (!access(path, F_OK)) | |
+ return S_IFCHR; | |
+ return -1; | |
+} | |
+ | |
+static int | |
+create_dev(const char *path) | |
+{ | |
+ struct passwd *pw; | |
+ struct group *gr; | |
+ regex_t match; | |
+ regmatch_t off; | |
+ char *regex; | |
+ char buf[64], *p; | |
+ char tmppath[PATH_MAX], devpath[PATH_MAX], *dev; | |
+ char *devname; | |
+ int maj, min, type; | |
+ int i, ret; | |
+ | |
+ p = strrchr(path, '/'); | |
+ if (!p) | |
+ return -1; | |
+ p++; | |
+ devname = strdup(p); | |
+ if (!devname) | |
+ eprintf("strdup:"); | |
+ snprintf(devpath, sizeof(devpath), "/dev/%s", devname); | |
+ | |
+ snprintf(tmppath, sizeof(tmppath), "%s/dev", path); | |
+ ret = devtomajmin(tmppath, &maj, &min); | |
+ if (ret < 0) { | |
+ free(devname); | |
+ return -1; | |
+ } | |
+ | |
+ snprintf(buf, sizeof(buf), "%d:%d", maj, min); | |
+ type = devtype(buf); | |
+ if (type < 0) { | |
+ free(devname); | |
+ return -1; | |
+ } | |
+ | |
+ for (i = 0; i < LEN(Rules); i++) { | |
+ regex = strdup(Rules[i].devregex); | |
+ if (!regex) | |
+ eprintf("strdup:"); | |
+ | |
+ ret = regcomp(&match, regex, REG_EXTENDED); | |
+ if (ret < 0) | |
+ eprintf("regcomp:"); | |
+ | |
+ ret = regexec(&match, devname, 1, &off, 0); | |
+ regfree(&match); | |
+ free(regex); | |
+ | |
+ if (ret || off.rm_so || off.rm_eo != strlen(devname)) | |
+ continue; | |
+ | |
+ if (Rules[i].cmd) { | |
+ switch (Rules[i].cmd[0]) { | |
+ case '@': | |
+ case '$': | |
+ case '*': | |
+ fprintf(stderr, "Unsupported command '%s' for … | |
+ Rules[i].cmd, Rules[i].devregex); | |
+ break; | |
+ default: | |
+ eprintf("Invalid command '%s'\n", Rules[i].cmd… | |
+ } | |
+ } | |
+ | |
+ dev = devpath; | |
+ if (Rules[i].path) { | |
+ switch (Rules[i].path[0]) { | |
+ case '=': | |
+ if (Rules[i].path[strlen(Rules[i].path) - 1] =… | |
+ snprintf(devpath, sizeof(devpath), "/d… | |
+ if (mkdir(devpath, 0755) < 0) | |
+ eprintf("mkdir %s:", devpath); | |
+ strcat(devpath, devname); | |
+ } else { | |
+ snprintf(devpath, sizeof(devpath), | |
+ "/dev/%s", &Rules[i].path[1]); | |
+ } | |
+ break; | |
+ case '>': | |
+ fprintf(stderr, "Unsupported path '%s' for tar… | |
+ Rules[i].path, Rules[i].devregex); | |
+ break; | |
+ default: | |
+ eprintf("Invalid path '%s'\n", Rules[i].path); | |
+ } | |
+ } | |
+ | |
+ ret = mknod(dev, Rules[i].mode | type, makedev(maj, min)); | |
+ if (ret < 0 && errno != EEXIST) | |
+ eprintf("mknod %s:", dev); | |
+ | |
+ pw = getpwnam(Rules[i].user); | |
+ if (!pw) | |
+ eprintf("getpwnam %s:", Rules[i].user); | |
+ | |
+ gr = getgrnam(Rules[i].group); | |
+ if (!gr) | |
+ eprintf("getgrnam %s:", Rules[i].group); | |
+ | |
+ ret = chown(dev, pw->pw_uid, gr->gr_gid); | |
+ if (ret < 0) | |
+ eprintf("chown %s:", dev); | |
+ break; | |
+ } | |
+ | |
+ free(devname); | |
+ return 0; | |
+} | |
+ | |
+static void | |
+sysrecurse(const char *path) | |
+{ | |
+ char *cwd; | |
+ | |
+ recurse(path, sysrecurse); | |
+ if (!strcmp(path, "dev")) { | |
+ cwd = agetcwd(); | |
+ create_dev(cwd); | |
+ free(cwd); | |
+ } | |
+} | |
diff --git a/util.h b/util.h | |
@@ -0,0 +1,13 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include "arg.h" | |
+ | |
+#define LEN(x) (sizeof (x) / sizeof *(x)) | |
+ | |
+extern char *argv0; | |
+ | |
+char *agetcwd(void); | |
+void apathmax(char **, long *); | |
+void enprintf(int, const char *, ...); | |
+void eprintf(const char *, ...); | |
+long estrtol(const char *, int); | |
+void recurse(const char *, void (*)(const char *)); | |
diff --git a/util/agetcwd.c b/util/agetcwd.c | |
@@ -0,0 +1,18 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <unistd.h> | |
+ | |
+#include "../util.h" | |
+ | |
+char * | |
+agetcwd(void) | |
+{ | |
+ char *buf; | |
+ long size; | |
+ | |
+ apathmax(&buf, &size); | |
+ if(!getcwd(buf, size)) | |
+ eprintf("getcwd:"); | |
+ | |
+ return buf; | |
+} | |
+ | |
diff --git a/util/apathmax.c b/util/apathmax.c | |
@@ -0,0 +1,25 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <errno.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <unistd.h> | |
+ | |
+#include "../util.h" | |
+ | |
+void | |
+apathmax(char **p, long *size) | |
+{ | |
+ errno = 0; | |
+ | |
+ if((*size = pathconf("/", _PC_PATH_MAX)) == -1) { | |
+ if(errno == 0) { | |
+ *size = BUFSIZ; | |
+ } else { | |
+ eprintf("pathconf:"); | |
+ } | |
+ } | |
+ | |
+ if(!(*p = malloc(*size))) | |
+ eprintf("malloc:"); | |
+} | |
+ | |
diff --git a/util/eprintf.c b/util/eprintf.c | |
@@ -0,0 +1,46 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+ | |
+#include "../util.h" | |
+ | |
+char *argv0; | |
+ | |
+static void venprintf(int, const char *, va_list); | |
+ | |
+void | |
+eprintf(const char *fmt, ...) | |
+{ | |
+ va_list ap; | |
+ | |
+ va_start(ap, fmt); | |
+ venprintf(EXIT_FAILURE, fmt, ap); | |
+ va_end(ap); | |
+} | |
+ | |
+void | |
+enprintf(int status, const char *fmt, ...) | |
+{ | |
+ va_list ap; | |
+ | |
+ va_start(ap, fmt); | |
+ venprintf(status, fmt, ap); | |
+ va_end(ap); | |
+} | |
+ | |
+void | |
+venprintf(int status, const char *fmt, va_list ap) | |
+{ | |
+ /*fprintf(stderr, "%s: ", argv0);*/ | |
+ | |
+ vfprintf(stderr, fmt, ap); | |
+ | |
+ if(fmt[0] && fmt[strlen(fmt)-1] == ':') { | |
+ fputc(' ', stderr); | |
+ perror(NULL); | |
+ } | |
+ | |
+ exit(status); | |
+} | |
diff --git a/util/estrtol.c b/util/estrtol.c | |
@@ -0,0 +1,27 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <errno.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+ | |
+#include "../util.h" | |
+ | |
+long | |
+estrtol(const char *s, int base) | |
+{ | |
+ char *end; | |
+ long n; | |
+ | |
+ errno = 0; | |
+ n = strtol(s, &end, base); | |
+ if(*end != '\0') { | |
+ if(base == 0) | |
+ eprintf("%s: not an integer\n", s); | |
+ else | |
+ eprintf("%s: not a base %d integer\n", s, base); | |
+ } | |
+ if(errno != 0) | |
+ eprintf("%s:", s); | |
+ | |
+ return n; | |
+} | |
+ | |
diff --git a/util/recurse.c b/util/recurse.c | |
@@ -0,0 +1,40 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <dirent.h> | |
+#include <errno.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+#include <sys/stat.h> | |
+ | |
+#include "../util.h" | |
+ | |
+void | |
+recurse(const char *path, void (*fn)(const char *)) | |
+{ | |
+ char *cwd; | |
+ struct dirent *d; | |
+ struct stat st; | |
+ DIR *dp; | |
+ | |
+ if(lstat(path, &st) == -1 || !S_ISDIR(st.st_mode)) { | |
+ return; | |
+ } else if(!(dp = opendir(path))) { | |
+ eprintf("opendir %s:", path); | |
+ } | |
+ | |
+ cwd = agetcwd(); | |
+ if(chdir(path) == -1) | |
+ eprintf("chdir %s:", path); | |
+ | |
+ while((d = readdir(dp))) { | |
+ if(strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) | |
+ fn(d->d_name); | |
+ } | |
+ | |
+ closedir(dp); | |
+ if(chdir(cwd) == -1) | |
+ eprintf("chdir %s:", cwd); | |
+ | |
+ free(cwd); | |
+} | |
+ |