makedev stuff
I've been real busy w/ other stuff, so I'll release this in the state
it's in, and continue work if people like what I've done. My MAKEDEV
scans all files in /etc/makedev.d, reading in only files w/ no
extension, or an extension that matches the OS. The format of the files
in /etc/makedev.d is "<name> <command>". MAKEDEV will take one of more
names on the command line (`./MAKEDEV std ide`), searching all appropriate
files for lines starting w/ "std " or "ide ". Any lines that it finds,
it will pass off to /bin/sh -c.
Here are a few example config file entries:
std mknod --mode=640 mem c 1 1 && chown root:kmem mem
std mknod --mode=640 kmem c 1 2 && chown root:kmem kmem
lvm2 mkdir device-mapper; minor=$(grep "[0-9] device-mapper$" /proc/misc | sed 's/[ ]\+device-mapper//'); mknod --mod=600 device-mapper/control c 10 $minor && chown root:root device-mapper/control
I decided to go w/ running commands, instead of going w/ Redhat and
Ian Zimmerman's methods of calling mknod()/link() directly so that
packages could extend makedev without having to touch the source code
itself. For example, current debian makedev needs to know about
/proc/devices; my lvm2 packages will need makedev to know about
/proc/misc, as the kernel driver registers a misc device w/ a
dynamically allocated minor number. Other architectures we support make
need to create devices using input from another source; as long as that
source can be expressed in some sort of command, it can be done w/out
having to hack makedev.
The drawbacks, of course, is the fact that shell is ugly (to some ;),
and there will be a lot of fork'ing/exec'ing involved. Of course, it
will still be less than the old makedev, and it shouldn't be run all
that often (or w/ a massive number of devices).
I kept it simple; it could use features such as being able to put
commands on multiple lines, some sort of macro support. If folks like
the direction this is headed, I'll do those things; otherwise I'll wait
for/help w/ an alternate implementation.
--
Broad surveillance is a mark of bad security.
-- Bruce Schneier
/*
* Copyright (C) 2002 Andres Salomon <dilinger@mp3revolution.net>
*
* 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; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#ifndef CONFDIR
#define CONFDIR "/etc/makedev.d"
#endif
#ifndef DEVDIR
#define DEVDIR "/dev"
#endif
#ifndef DEFAULT_OS
#define DEFAULT_OS "linux"
#endif
#ifndef PACKAGE
#define PACKAGE "MAKEDEV"
#endif
#ifndef VERSION
#define VERSION "4.0"
#endif
static const char *makedev_cfgdir = CONFDIR;
static const char *makedev_os = DEFAULT_OS;
/*
* Same as snprintf(), except ensure NUL termination.
*/
static int t_snprintf (char *str, size_t size, const char *fmt, ...)
{
va_list ap;
int ret;
va_start (ap, fmt);
ret = vsnprintf (str, size, fmt, ap);
va_end (ap);
str[size - 1] = '\0';
return ret;
}
/*
* Remove all whitespace from the beginning of a string.
*/
static char *str_chug (char *s)
{
while (isspace (*s))
s++;
return s;
}
/*
* Remove all whitespace from the end of a string.
*/
static char *str_chomp (char *s)
{
char *ret = s;
s = &s[strlen (s) - 1];
while (isspace (*s)) {
*s = '\0';
if (s == ret)
break;
s--;
}
return ret;
}
/*
* Return the next word from a string, updating and removing the word from
* the passed string.
*/
static char *str_get_next_word (char **s)
{
char *ret;
ret = *s = str_chug (*s);
while (!isspace (**s) && **s)
(*s)++;
if (**s) {
**s = '\0';
(*s)++;
}
return ret;
}
/*
* Check a file for information about creating devname. Syntax is:
* <name> <command> ...
* If <name> matches 'devname', then run the command. <name> may also be
* processed if it's a special value;
*/
static void scan_file (const char *f, const char *devname)
{
FILE *fp;
char buf[512], *s, *next;
size_t line_nr = 0;
fp = fopen (f, "r");
if (fp == NULL) {
fprintf (stderr, "Warning: cannot read %s: %s\n", f,
strerror (errno));
return;
}
while ((s = fgets (buf, sizeof (buf), fp)) != NULL) {
line_nr++;
next = str_get_next_word (&s);
switch (*next) {
case '#':
case '\0':
/* skip comments/blank lines */
break;
default:
/* name; only process if creating */
if (strcmp (devname, next) == 0) {
switch (fork ()) {
case -1:
fprintf (stderr, "Error: unable to fork: %s\n", strerror (errno));
break;
case 0:
execl ("/bin/sh", "/bin/sh", "-c", s, NULL);
fprintf (stderr, "Error: exec failed: %s\n", strerror (errno));
break;
default:
waitpid (0, NULL, 0);
}
}
break;
}
}
fclose (fp);
}
/*
* Given a device name, search for it; this scans the cfgdir for files
* containing relevant device info, searching for instructions on how to
* create. Only files w/out extensions, or files w/ extensions of the
* same OS are scanned.
*/
static void create_device (const char *devname)
{
struct dirent *file;
char s[256], *ext;
struct stat buf;
DIR *d;
d = opendir (makedev_cfgdir);
if (d == NULL) {
fprintf (stderr, "Error: cannot read %s: %s\n", makedev_cfgdir,
strerror (errno));
exit (1);
}
while ((file = readdir (d))) {
if (strcmp (file->d_name, ".") == 0 ||
strcmp (file->d_name, "..") == 0)
continue;
ext = strrchr (file->d_name, '.');
if (ext != NULL && strcmp (&ext[1], makedev_os) == 0) {
/* extension matching OS */
t_snprintf (s, sizeof (s), "%s/%s", makedev_cfgdir,
file->d_name);
scan_file (s, devname);
}
else if (ext == NULL) {
/* no extension */
t_snprintf (s, sizeof (s), "%s/%s.%s", makedev_cfgdir,
file->d_name, makedev_os);
if (stat (s, &buf) == -1) {
t_snprintf (s, sizeof (s), "%s/%s",
makedev_cfgdir, file->d_name);
scan_file (s, devname);
}
}
}
closedir (d);
}
static void usage (const char *argv0, int to_stderr)
{
FILE *out = to_stderr ? stderr : stdout;
fprintf (out, "Usage: %s [options] [device list ...]\n\n"
"Options:\n"
" -c <configdir> config file directory [" CONFDIR "]\n"
" -d <devicedir> device directory [" DEVDIR "]\n"
" -h display this screen and exit\n"
" -o <os name> target OS [" DEFAULT_OS "]\n"
" -v display version info and exit\n",
argv0);
exit (to_stderr);
}
int main (int argc, char **argv)
{
char cwd[PATH_MAX+1];
char *devdir = NULL;
int c;
while ((c = getopt (argc, argv, "c:d:ho:v")) != -1) {
switch (c) {
case 'c':
makedev_cfgdir = strdup (optarg);
break;
case 'd':
if (devdir)
free (devdir);
devdir = strdup (optarg);
break;
case 'h':
usage (argv[0], 0);
break;
case 'o':
makedev_os = strdup (optarg);
break;
case 'v':
printf (PACKAGE " " VERSION "\n");
return 0;
default:
usage (argv[0], 1);
break;
}
}
if (optind >= argc)
usage (argv[0], 1);
/* TODO: check for $devdir/.devfsd */
getcwd (cwd, sizeof (cwd));
if (chdir (devdir ? devdir : DEVDIR) == -1) {
fprintf (stderr, "Error: cannot change directory to %s: %s\n",
devdir ? devdir : DEVDIR, strerror (errno));
return 1;
}
while (optind < argc) {
create_device (argv[optind]);
optind++;
}
chdir (cwd);
return 0;
}
Reply to: