smdev

suckless mdev
git clone git://git.suckless.org/smdev
Log | Files | Refs | README | LICENSE

commit 733f60dba2b8c878945b856c23a6bc44de52fc11
Author: sin <sin@2f30.org>
Date:   Tue, 20 Aug 2013 13:26:10 +0100

Initial commit

Diffstat:
ALICENSE | 21+++++++++++++++++++++
AMakefile | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarg.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.h | 26++++++++++++++++++++++++++
Aconfig.mk | 12++++++++++++
Asmdev.c | 209+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.h | 13+++++++++++++
Autil/agetcwd.c | 18++++++++++++++++++
Autil/apathmax.c | 25+++++++++++++++++++++++++
Autil/eprintf.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Autil/estrtol.c | 27+++++++++++++++++++++++++++
Autil/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 <sin@2f30.org> + +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 target '%s'\n", + 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), "/dev/%s", &Rules[i].path[1]); + 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 target '%s'\n", + 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); +} +