diff options
Diffstat (limited to 'udevruler.c')
-rw-r--r-- | udevruler.c | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/udevruler.c b/udevruler.c new file mode 100644 index 0000000000..b66940e944 --- /dev/null +++ b/udevruler.c @@ -0,0 +1,503 @@ +/* + * udevruler.c - simple udev-rule composer + * + * Reads the udev-db to get all currently known devices and + * scans the sysfs device chain for the choosen device to select attributes + * to compose a rule for the udev.rules file to uniquely name this device. + * + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * + * 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. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <newt.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <ctype.h> +#include <time.h> +#include <unistd.h> + +#include "udev.h" +#include "udev_lib.h" +#include "udev_version.h" +#include "udevdb.h" +#include "logging.h" +#include "libsysfs/sysfs.h" +#include "list.h" + +#ifdef LOG +unsigned char logname[LOGNAME_SIZE]; +void log_message(int level, const char *format, ...) +{ + va_list args; + + if (!udev_log) + return; + + va_start(args, format); + vsyslog(level, format, args); + va_end(args); +} +#endif + +static char *dev_blacklist[] = { + "tty", + "pty", + "zero", + "null", + "kmsg", + "rtc", + "timer", + "full", + "kmem", + "mem", + "random", + "urandom", + "console", + "port", + "" +}; + +struct device { + struct list_head list; + char name[NAME_SIZE]; + char devpath[DEVPATH_SIZE]; + int config_line; + char config_file[NAME_SIZE]; + time_t config_time; + int added; +}; + +LIST_HEAD(device_list); +int device_count; + +/* callback for database dump */ +static int add_record(char *path, struct udevice *udev) +{ + struct device *dev; + struct device *loop_dev; + int i = 0; + + while (dev_blacklist[i][0] != '\0') { + if (strncmp(udev->name, dev_blacklist[i], strlen(dev_blacklist[i])) == 0) + goto exit; + i++; + } + + dev = malloc(sizeof(struct device)); + if (dev == NULL) { + printf("error malloc\n"); + exit(2); + } + strfieldcpy(dev->name, udev->name); + strfieldcpy(dev->devpath, path); + dev->config_line = udev->config_line; + strfieldcpy(dev->config_file, udev->config_file); + dev->config_time = udev->config_time; + dev->added = 0; + + /* sort in lexical order */ + list_for_each_entry(loop_dev, &device_list, list) { + if (strcmp(loop_dev->name, dev->name) > 0) { + break; + } + } + + list_add_tail(&dev->list, &loop_dev->list); + device_count++; + +exit: + return 0; +} + +/* get all devices from udev database */ +static int get_all_devices(void) +{ + int retval; + + device_count = 0; + INIT_LIST_HEAD(&device_list); + + retval = udevdb_open_ro(); + if (retval != 0) { + printf("unable to open udev database\n"); + exit(1); + } + + udevdb_call_foreach(add_record); + udevdb_exit(); + + return 0; +} + +struct attribute { + struct list_head list; + int level; + char key[NAME_SIZE]; +}; + +LIST_HEAD(attribute_list); +int attribute_count; + +static int add_attribute(const char *key, int level) +{ + struct attribute *attr; + + dbg("add attribute '%s'", key); + attr = malloc(sizeof(struct attribute)); + if (attr == NULL) { + printf("error malloc\n"); + exit(2); + } + + strfieldcpy(attr->key, key); + attr->level = level; + list_add_tail(&attr->list, &attribute_list); + attribute_count++; + return 0; +} + +static int add_all_attributes(const char *path, int level) +{ + struct dlist *attributes; + struct sysfs_attribute *attr; + struct sysfs_directory *sysfs_dir; + char value[NAME_SIZE]; + char key[NAME_SIZE]; + int len; + int retval = 0; + + dbg("look at '%s', level %i", path, level); + + sysfs_dir = sysfs_open_directory(path); + if (sysfs_dir == NULL) + return -1; + + attributes = sysfs_get_dir_attributes(sysfs_dir); + if (attributes == NULL) { + retval = -1; + return 0; + } + + dlist_for_each_data(attributes, attr, struct sysfs_attribute) + if (attr->value != NULL) { + dbg("found attribute '%s'", attr->name); + strfieldcpy(value, attr->value); + len = strlen(value); + if (len == 0) + continue; + + /* skip very long attributes */ + if (len > 40) + continue; + + /* remove trailing newline */ + if (value[len-1] == '\n') { + value[len-1] = '\0'; + len--; + } + + /* skip nonprintable values */ + while (len) { + if (!isprint(value[len-1])) + break; + len--; + } + if (len == 0) { + sprintf(key, "SYSFS{%s}=\"%s\"", attr->name, value); + add_attribute(key, level); + } + } + + return 0; +} + +static int get_all_attributes(char *path) +{ + struct sysfs_class_device *class_dev; + struct sysfs_class_device *class_dev_parent; + struct sysfs_attribute *attr; + struct sysfs_device *sysfs_dev; + struct sysfs_device *sysfs_dev_parent; + char key[NAME_SIZE]; + int retval = 0; + int level = 0; + + attribute_count = 0; + INIT_LIST_HEAD(&attribute_list); + + /* get the class dev */ + class_dev = sysfs_open_class_device_path(path); + if (class_dev == NULL) { + dbg("couldn't get the class device"); + return -1; + } + + /* read the 'dev' file for major/minor*/ + attr = sysfs_get_classdev_attr(class_dev, "dev"); + if (attr == NULL) { + dbg("couldn't get the \"dev\" file"); + retval = -1; + goto exit; + } + + sysfs_close_attribute(attr); + + /* open sysfs class device directory and get all attributes */ + if (add_all_attributes(class_dev->path, level) != 0) { + dbg("couldn't open class device directory"); + retval = -1; + goto exit; + } + level++; + + /* get the device link (if parent exists look here) */ + class_dev_parent = sysfs_get_classdev_parent(class_dev); + if (class_dev_parent != NULL) { + //sysfs_close_class_device(class_dev); + class_dev = class_dev_parent; + } + sysfs_dev = sysfs_get_classdev_device(class_dev); + + /* look the device chain upwards */ + while (sysfs_dev != NULL) { + if (sysfs_dev->bus[0] != '\0') { + add_attribute("", level); + sprintf(key, "BUS=\"%s\"", sysfs_dev->bus); + add_attribute(key, level); + sprintf(key, "ID=\"%s\"", sysfs_dev->bus_id); + add_attribute(key, level); + + /* open sysfs device directory and print all attributes */ + add_all_attributes(sysfs_dev->path, level); + } + level++; + + sysfs_dev_parent = sysfs_get_device_parent(sysfs_dev); + if (sysfs_dev_parent == NULL) + break; + + //sysfs_close_device(sysfs_dev); + sysfs_dev = sysfs_dev_parent; + } + sysfs_close_device(sysfs_dev); + +exit: + //sysfs_close_class_device(class_dev); + return retval; +} + + +int main(int argc, char *argv[]) { + newtComponent lbox, run, lattr; + newtComponent quit, form, answer; + newtGrid grid, grid2; + char roottext[81]; + char path[NAME_SIZE]; + struct device *dev; + time_t time_last; + int count_last; + + newtInit(); + newtCls(); + + newtWinMessage("udevruler", "Ok", + "This program lets you select a device currently present " + "on the system and you may choose the attributes to uniquely " + "name the device with a udev rule.\n" + "No configuration will be changed, it just prints the rule " + "to place in a udev.rules configuration file. The \"%k\" in the " + "NAME key of the printed rule may be replaced by the name the " + "node should have."); + + init_logging("udevruler"); + udev_init_config(); + get_all_devices(); + + lbox = newtListbox(2, 1, 10, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT); + + /* look for last discovered device */ + time_last = 0; + list_for_each_entry(dev, &device_list, list) + if (dev->config_time > time_last) + time_last = dev->config_time; + + /* skip if more than 16 recent devices */ + count_last = 0; + list_for_each_entry(dev, &device_list, list) { + if (dev->config_time < time_last - 10) + continue; + count_last++; + } + + /* add devices up to 10 seconds older than the last one */ + if (count_last < 16) { + newtListboxAppendEntry(lbox, "--- last dicovered ---", NULL); + list_for_each_entry(dev, &device_list, list) { + if (dev->config_time < time_last - 10) + continue; + + dbg("%s %i", dev->name, dev->config_line); + newtListboxAppendEntry(lbox, dev->name, (void*) dev); + dev->added = 1; + } + newtListboxAppendEntry(lbox, "", NULL); + } + + /* add devices not catched by a rule */ + newtListboxAppendEntry(lbox, "--- not configured by a rule ---", NULL); + list_for_each_entry(dev, &device_list, list) { + if (dev->added) + continue; + + if (dev->config_line != 0) + continue; + + dbg("%s %i", dev->name, dev->config_line); + newtListboxAppendEntry(lbox, dev->name, (void*) dev); + dev->added = 1; + } + newtListboxAppendEntry(lbox, "", NULL); + + /* add remaining devices */ + newtListboxAppendEntry(lbox, "--- configured by a rule ---", NULL); + list_for_each_entry(dev, &device_list, list) { + if (dev->added) + continue; + + dbg("%s %i", dev->name, dev->config_line); + newtListboxAppendEntry(lbox, dev->name, (void*) dev); + } + + newtPushHelpLine(" <Tab>/<Alt-Tab> between elements | Use <Enter> to select a device"); + snprintf(roottext, sizeof(roottext), "simple udev rule composer, version %s, (c) 2004 can't sleep team", UDEV_VERSION); + roottext[sizeof(roottext)-1] = '\0'; + newtDrawRootText(0, 0, roottext); + + form = newtForm(NULL, NULL, 0); + grid = newtCreateGrid(1, 2); + grid2 = newtButtonBar("Select device", &run, "Quit", &quit, NULL); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, lbox, 1, 0, 1, 0, NEWT_ANCHOR_TOP, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, grid2, 0, 1, 0, 0, NEWT_ANCHOR_TOP, 0); + newtFormAddComponents(form, lbox, run, quit, NULL); + newtGridWrappedWindow(grid,"Choose the device for which to compose a rule"); + newtGridFree(grid, 1); + + while (1) { + struct attribute *attr; + newtComponent ok, back, form2, answer2, text; + newtGrid grid3, grid4; + int i; + int numitems; + struct attribute **selattr; + char text_rule[80]; + + answer = newtRunForm(form); + if (answer == quit) + break; + + dev = (struct device *) newtListboxGetCurrent(lbox); + if (dev == NULL) + continue; + + if (dev->config_line > 0) + snprintf(text_rule, sizeof(text_rule), + "The device is handled by a rule in the file '%s', line %i.", + dev->config_file, dev->config_line); + else + strcpy(text_rule, "The device was not handled by a rule."); + + strfieldcpy(path, sysfs_path); + strfieldcat(path, dev->devpath); + dbg("look at sysfs device '%s'", path); + get_all_attributes(path); + + grid3 = newtCreateGrid(1, 3); + form2 = newtForm(NULL, NULL, 0); + grid4 = newtButtonBar("Ok", &ok, "Back", &back, NULL); + + lattr = newtListbox(-1, -1, 10, NEWT_FLAG_MULTIPLE | NEWT_FLAG_BORDER | NEWT_FLAG_RETURNEXIT); + list_for_each_entry(attr, &attribute_list, list) + newtListboxAddEntry(lattr, attr->key, (void *) attr); + + text = newtTextbox(-1, -1, 50, 2, NEWT_FLAG_WRAP); + newtTextboxSetText(text, text_rule); + + newtGridSetField(grid3, 0, 0, NEWT_GRID_COMPONENT, lattr, 0, 0, 0, 1, 0, 0); + newtGridSetField(grid3, 0, 1, NEWT_GRID_COMPONENT, text, 0, 0, 0, 1, 0, 0); + newtGridSetField(grid3, 0, 2, NEWT_GRID_SUBGRID, grid4, 0, 1, 0, 0, NEWT_ANCHOR_TOP, 0); + + newtFormAddComponents(form2, text, lattr, ok, back, NULL); + newtGridWrappedWindow(grid3, "Select one ore more attributes within one section with the space bar"); + newtGridFree(grid3, 1); + + while (1) { + char rule[255]; + int onelevel; + int skipped; + + answer2 = newtRunForm(form2); + if (answer2 == back) + break; + + selattr = (struct attribute **) newtListboxGetSelection(lattr, &numitems); + if (selattr == NULL) + continue; + + rule[0] = '\0'; + onelevel = -1; + skipped = 0; + for (i = 0; i < numitems; i++) { + if (selattr[i]->key[0] == '\0') + continue; + + if (onelevel != -1) { + if (onelevel != selattr[i]->level) { + skipped = 1; + continue; + } + } else { + onelevel = selattr[i]->level; + } + + dbg("'%s'\n", selattr[i]->key); + strfieldcat(rule, selattr[i]->key); + strfieldcat(rule, ", "); + } + if (skipped) { + newtWinMessage("error", "Ok", "Please select only attributes within one section"); + continue; + } + + if (strlen(rule) > 200) { + newtWinMessage("error", "Ok", "The line is too long, please select fewer attributes."); + } else { + if (rule[0] == '\0') + continue; + + strfieldcat(rule, "NAME=\"%k\""); + newtWinMessage("the rule to place in config file", "Ok", rule); + } + } + + newtPopWindow(); + } + + newtPopWindow(); + newtFormDestroy(form); + newtFinished(); + return 0; +} |