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