2 * udevruler.c - simple udev-rule composer
4 * Reads the udev-db to get all currently known devices and
5 * scans the sysfs device chain for the choosen device to select attributes
6 * to compose a rule for the udev.rules file to uniquely name this device.
8 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation version 2 of the License.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 675 Mass Ave, Cambridge, MA 02139, USA.
35 #include "udev_version.h"
38 #include "libsysfs/sysfs.h"
42 unsigned char logname[LOGNAME_SIZE];
43 void log_message(int level, const char *format, ...)
50 va_start(args, format);
51 vsyslog(level, format, args);
56 static char *dev_blacklist[] = {
75 struct list_head list;
77 char devpath[DEVPATH_SIZE];
79 char config_file[NAME_SIZE];
84 static LIST_HEAD(device_list);
85 static int device_count;
87 /* callback for database dump */
88 static int add_record(char *path, struct udevice *udev)
91 struct device *loop_dev;
94 while (dev_blacklist[i][0] != '\0') {
95 if (strncmp(udev->name, dev_blacklist[i], strlen(dev_blacklist[i])) == 0)
100 dev = malloc(sizeof(struct device));
102 printf("error malloc\n");
105 strfieldcpy(dev->name, udev->name);
106 strfieldcpy(dev->devpath, path);
107 dev->config_line = udev->config_line;
108 strfieldcpy(dev->config_file, udev->config_file);
109 dev->config_uptime = udev->config_uptime;
112 /* sort in lexical order */
113 list_for_each_entry(loop_dev, &device_list, list) {
114 if (strcmp(loop_dev->name, dev->name) > 0) {
119 list_add_tail(&dev->list, &loop_dev->list);
126 /* get all devices from udev database */
127 static int get_all_devices(void)
132 INIT_LIST_HEAD(&device_list);
134 retval = udevdb_open_ro();
136 printf("unable to open udev database\n");
140 udevdb_call_foreach(add_record);
147 struct list_head list;
152 static LIST_HEAD(attribute_list);
153 static int attribute_count;
155 static int add_attribute(const char *key, int level)
157 struct attribute *attr;
159 dbg("add attribute '%s'", key);
160 attr = malloc(sizeof(struct attribute));
162 printf("error malloc\n");
166 strfieldcpy(attr->key, key);
168 list_add_tail(&attr->list, &attribute_list);
173 static int add_all_attributes(const char *path, int level)
175 struct dlist *attributes;
176 struct sysfs_attribute *attr;
177 struct sysfs_directory *sysfs_dir;
178 char value[NAME_SIZE];
183 dbg("look at '%s', level %i", path, level);
185 sysfs_dir = sysfs_open_directory(path);
186 if (sysfs_dir == NULL)
189 attributes = sysfs_get_dir_attributes(sysfs_dir);
190 if (attributes == NULL) {
195 dlist_for_each_data(attributes, attr, struct sysfs_attribute)
196 if (attr->value != NULL) {
197 dbg("found attribute '%s'", attr->name);
198 strfieldcpy(value, attr->value);
203 /* skip very long attributes */
207 /* remove trailing newline */
208 if (value[len-1] == '\n') {
213 /* skip nonprintable values */
215 if (!isprint(value[len-1]))
220 sprintf(key, "SYSFS{%s}=\"%s\"", attr->name, value);
221 add_attribute(key, level);
228 static int get_all_attributes(char *path)
230 struct sysfs_class_device *class_dev;
231 struct sysfs_class_device *class_dev_parent;
232 struct sysfs_attribute *attr;
233 struct sysfs_device *sysfs_dev;
234 struct sysfs_device *sysfs_dev_parent;
240 INIT_LIST_HEAD(&attribute_list);
242 /* get the class dev */
243 class_dev = sysfs_open_class_device_path(path);
244 if (class_dev == NULL) {
245 dbg("couldn't get the class device");
249 /* read the 'dev' file for major/minor*/
250 attr = sysfs_get_classdev_attr(class_dev, "dev");
252 dbg("couldn't get the \"dev\" file");
257 sysfs_close_attribute(attr);
259 /* open sysfs class device directory and get all attributes */
260 if (add_all_attributes(class_dev->path, level) != 0) {
261 dbg("couldn't open class device directory");
267 /* get the device link (if parent exists look here) */
268 class_dev_parent = sysfs_get_classdev_parent(class_dev);
269 if (class_dev_parent != NULL) {
270 //sysfs_close_class_device(class_dev);
271 class_dev = class_dev_parent;
273 sysfs_dev = sysfs_get_classdev_device(class_dev);
275 /* look the device chain upwards */
276 while (sysfs_dev != NULL) {
277 if (sysfs_dev->bus[0] != '\0') {
278 add_attribute("", level);
279 sprintf(key, "BUS=\"%s\"", sysfs_dev->bus);
280 add_attribute(key, level);
281 sprintf(key, "ID=\"%s\"", sysfs_dev->bus_id);
282 add_attribute(key, level);
284 /* open sysfs device directory and print all attributes */
285 add_all_attributes(sysfs_dev->path, level);
289 sysfs_dev_parent = sysfs_get_device_parent(sysfs_dev);
290 if (sysfs_dev_parent == NULL)
293 //sysfs_close_device(sysfs_dev);
294 sysfs_dev = sysfs_dev_parent;
296 sysfs_close_device(sysfs_dev);
299 //sysfs_close_class_device(class_dev);
304 int main(int argc, char *argv[]) {
305 newtComponent lbox, run, lattr;
306 newtComponent quit, form, answer;
307 newtGrid grid, grid2;
309 char path[NAME_SIZE];
317 newtWinMessage("udevruler", "Ok",
318 "This program lets you select a device currently present "
319 "on the system and you may choose the attributes to uniquely "
320 "name the device with a udev rule.\n"
321 "No configuration will be changed, it just prints the rule "
322 "to place in a udev.rules configuration file. The \"%k\" in the "
323 "NAME key of the printed rule may be replaced by the name the "
324 "node should have.");
326 init_logging("udevruler");
330 lbox = newtListbox(2, 1, 10, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
332 /* look for last discovered device */
334 list_for_each_entry(dev, &device_list, list)
335 if (dev->config_uptime > time_last)
336 time_last = dev->config_uptime;
338 /* skip if more than 16 recent devices */
340 list_for_each_entry(dev, &device_list, list) {
341 if (dev->config_uptime < time_last - 10)
346 /* add devices up to 10 seconds older than the last one */
347 if (count_last < 16) {
348 newtListboxAppendEntry(lbox, "--- last dicovered ---", NULL);
349 list_for_each_entry(dev, &device_list, list) {
350 if (dev->config_uptime < time_last - 10)
353 dbg("%s %i", dev->name, dev->config_line);
354 newtListboxAppendEntry(lbox, dev->name, (void*) dev);
357 newtListboxAppendEntry(lbox, "", NULL);
360 /* add devices not catched by a rule */
361 newtListboxAppendEntry(lbox, "--- not configured by a rule ---", NULL);
362 list_for_each_entry(dev, &device_list, list) {
366 if (dev->config_line != 0)
369 dbg("%s %i", dev->name, dev->config_line);
370 newtListboxAppendEntry(lbox, dev->name, (void*) dev);
373 newtListboxAppendEntry(lbox, "", NULL);
375 /* add remaining devices */
376 newtListboxAppendEntry(lbox, "--- configured by a rule ---", NULL);
377 list_for_each_entry(dev, &device_list, list) {
381 dbg("%s %i", dev->name, dev->config_line);
382 newtListboxAppendEntry(lbox, dev->name, (void*) dev);
385 newtPushHelpLine(" <Tab>/<Alt-Tab> between elements | Use <Enter> to select a device");
386 snprintf(roottext, sizeof(roottext), "simple udev rule composer, version %s, (c) 2004 can't sleep team", UDEV_VERSION);
387 roottext[sizeof(roottext)-1] = '\0';
388 newtDrawRootText(0, 0, roottext);
390 form = newtForm(NULL, NULL, 0);
391 grid = newtCreateGrid(1, 2);
392 grid2 = newtButtonBar("Select device", &run, "Quit", &quit, NULL);
393 newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, lbox, 1, 0, 1, 0, NEWT_ANCHOR_TOP, 0);
394 newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, grid2, 0, 1, 0, 0, NEWT_ANCHOR_TOP, 0);
395 newtFormAddComponents(form, lbox, run, quit, NULL);
396 newtGridWrappedWindow(grid,"Choose the device for which to compose a rule");
397 newtGridFree(grid, 1);
400 struct attribute *attr;
401 newtComponent ok, back, form2, answer2, text;
402 newtGrid grid3, grid4;
405 struct attribute **selattr;
408 answer = newtRunForm(form);
412 dev = (struct device *) newtListboxGetCurrent(lbox);
416 if (dev->config_line > 0)
417 snprintf(text_rule, sizeof(text_rule),
418 "The device is handled by a rule in the file '%s', line %i.",
419 dev->config_file, dev->config_line);
421 strcpy(text_rule, "The device was not handled by a rule.");
423 strfieldcpy(path, sysfs_path);
424 strfieldcat(path, dev->devpath);
425 dbg("look at sysfs device '%s'", path);
426 get_all_attributes(path);
428 grid3 = newtCreateGrid(1, 3);
429 form2 = newtForm(NULL, NULL, 0);
430 grid4 = newtButtonBar("Ok", &ok, "Back", &back, NULL);
432 lattr = newtListbox(-1, -1, 10, NEWT_FLAG_MULTIPLE | NEWT_FLAG_BORDER | NEWT_FLAG_RETURNEXIT);
433 list_for_each_entry(attr, &attribute_list, list)
434 newtListboxAddEntry(lattr, attr->key, (void *) attr);
436 text = newtTextbox(-1, -1, 50, 2, NEWT_FLAG_WRAP);
437 newtTextboxSetText(text, text_rule);
439 newtGridSetField(grid3, 0, 0, NEWT_GRID_COMPONENT, lattr, 0, 0, 0, 1, 0, 0);
440 newtGridSetField(grid3, 0, 1, NEWT_GRID_COMPONENT, text, 0, 0, 0, 1, 0, 0);
441 newtGridSetField(grid3, 0, 2, NEWT_GRID_SUBGRID, grid4, 0, 1, 0, 0, NEWT_ANCHOR_TOP, 0);
443 newtFormAddComponents(form2, text, lattr, ok, back, NULL);
444 newtGridWrappedWindow(grid3, "Select one ore more attributes within one section with the space bar");
445 newtGridFree(grid3, 1);
452 answer2 = newtRunForm(form2);
456 selattr = (struct attribute **) newtListboxGetSelection(lattr, &numitems);
463 for (i = 0; i < numitems; i++) {
464 if (selattr[i]->key[0] == '\0')
467 if (onelevel != -1) {
468 if (onelevel != selattr[i]->level) {
473 onelevel = selattr[i]->level;
476 dbg("'%s'\n", selattr[i]->key);
477 strfieldcat(rule, selattr[i]->key);
478 strfieldcat(rule, ", ");
481 newtWinMessage("error", "Ok", "Please select only attributes within one section");
485 if (strlen(rule) > 200) {
486 newtWinMessage("error", "Ok", "The line is too long, please select fewer attributes.");
491 strfieldcat(rule, "NAME=\"%k\"");
492 newtWinMessage("the rule to place in config file", "Ok", rule);
500 newtFormDestroy(form);