]> git.ipfire.org Git - thirdparty/systemd.git/blame - udevruler.c
[PATCH] be paranoid in dev_d.c
[thirdparty/systemd.git] / udevruler.c
CommitLineData
b608ade8
KS
1/*
2 * udevruler.c - simple udev-rule composer
3 *
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.
7 *
8 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
9 *
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.
13 *
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.
18 *
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.
22 *
23 */
24
25#include <newt.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <stddef.h>
29#include <ctype.h>
30#include <time.h>
31#include <unistd.h>
32
33#include "udev.h"
34#include "udev_lib.h"
35#include "udev_version.h"
36#include "udevdb.h"
37#include "logging.h"
38#include "libsysfs/sysfs.h"
39#include "list.h"
40
41#ifdef LOG
42unsigned char logname[LOGNAME_SIZE];
43void log_message(int level, const char *format, ...)
44{
45 va_list args;
46
47 if (!udev_log)
48 return;
49
50 va_start(args, format);
51 vsyslog(level, format, args);
52 va_end(args);
53}
54#endif
55
56static char *dev_blacklist[] = {
57 "tty",
58 "pty",
59 "zero",
60 "null",
61 "kmsg",
62 "rtc",
63 "timer",
64 "full",
65 "kmem",
66 "mem",
67 "random",
68 "urandom",
69 "console",
70 "port",
71 ""
72};
73
74struct device {
75 struct list_head list;
76 char name[NAME_SIZE];
77 char devpath[DEVPATH_SIZE];
78 int config_line;
79 char config_file[NAME_SIZE];
bbbe503e 80 long config_uptime;
b608ade8
KS
81 int added;
82};
83
4a539daf
KS
84static LIST_HEAD(device_list);
85static int device_count;
b608ade8
KS
86
87/* callback for database dump */
88static int add_record(char *path, struct udevice *udev)
89{
90 struct device *dev;
91 struct device *loop_dev;
92 int i = 0;
93
94 while (dev_blacklist[i][0] != '\0') {
95 if (strncmp(udev->name, dev_blacklist[i], strlen(dev_blacklist[i])) == 0)
96 goto exit;
97 i++;
98 }
99
100 dev = malloc(sizeof(struct device));
101 if (dev == NULL) {
102 printf("error malloc\n");
103 exit(2);
104 }
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);
bbbe503e 109 dev->config_uptime = udev->config_uptime;
b608ade8
KS
110 dev->added = 0;
111
112 /* sort in lexical order */
113 list_for_each_entry(loop_dev, &device_list, list) {
114 if (strcmp(loop_dev->name, dev->name) > 0) {
115 break;
116 }
117 }
118
119 list_add_tail(&dev->list, &loop_dev->list);
120 device_count++;
121
122exit:
123 return 0;
124}
125
126/* get all devices from udev database */
127static int get_all_devices(void)
128{
129 int retval;
130
131 device_count = 0;
132 INIT_LIST_HEAD(&device_list);
133
134 retval = udevdb_open_ro();
135 if (retval != 0) {
136 printf("unable to open udev database\n");
137 exit(1);
138 }
139
140 udevdb_call_foreach(add_record);
141 udevdb_exit();
142
143 return 0;
144}
145
146struct attribute {
147 struct list_head list;
148 int level;
149 char key[NAME_SIZE];
150};
151
4a539daf
KS
152static LIST_HEAD(attribute_list);
153static int attribute_count;
b608ade8
KS
154
155static int add_attribute(const char *key, int level)
156{
157 struct attribute *attr;
158
159 dbg("add attribute '%s'", key);
160 attr = malloc(sizeof(struct attribute));
161 if (attr == NULL) {
162 printf("error malloc\n");
163 exit(2);
164 }
165
166 strfieldcpy(attr->key, key);
167 attr->level = level;
168 list_add_tail(&attr->list, &attribute_list);
169 attribute_count++;
170 return 0;
171}
172
173static int add_all_attributes(const char *path, int level)
174{
175 struct dlist *attributes;
176 struct sysfs_attribute *attr;
177 struct sysfs_directory *sysfs_dir;
178 char value[NAME_SIZE];
179 char key[NAME_SIZE];
180 int len;
181 int retval = 0;
182
183 dbg("look at '%s', level %i", path, level);
184
185 sysfs_dir = sysfs_open_directory(path);
186 if (sysfs_dir == NULL)
187 return -1;
188
189 attributes = sysfs_get_dir_attributes(sysfs_dir);
190 if (attributes == NULL) {
191 retval = -1;
192 return 0;
193 }
194
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);
199 len = strlen(value);
200 if (len == 0)
201 continue;
202
203 /* skip very long attributes */
204 if (len > 40)
205 continue;
206
207 /* remove trailing newline */
208 if (value[len-1] == '\n') {
209 value[len-1] = '\0';
210 len--;
211 }
212
213 /* skip nonprintable values */
214 while (len) {
215 if (!isprint(value[len-1]))
216 break;
217 len--;
218 }
219 if (len == 0) {
220 sprintf(key, "SYSFS{%s}=\"%s\"", attr->name, value);
221 add_attribute(key, level);
222 }
223 }
224
dd785ba6 225 sysfs_close_directory(sysfs_dir);
b608ade8
KS
226 return 0;
227}
228
229static int get_all_attributes(char *path)
230{
231 struct sysfs_class_device *class_dev;
232 struct sysfs_class_device *class_dev_parent;
b608ade8
KS
233 struct sysfs_device *sysfs_dev;
234 struct sysfs_device *sysfs_dev_parent;
235 char key[NAME_SIZE];
236 int retval = 0;
237 int level = 0;
238
239 attribute_count = 0;
240 INIT_LIST_HEAD(&attribute_list);
241
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");
246 return -1;
247 }
248
b608ade8
KS
249 /* open sysfs class device directory and get all attributes */
250 if (add_all_attributes(class_dev->path, level) != 0) {
251 dbg("couldn't open class device directory");
252 retval = -1;
253 goto exit;
254 }
255 level++;
256
257 /* get the device link (if parent exists look here) */
258 class_dev_parent = sysfs_get_classdev_parent(class_dev);
dd785ba6
KS
259 if (class_dev_parent != NULL)
260 sysfs_dev = sysfs_get_classdev_device(class_dev_parent);
261 else
262 sysfs_dev = sysfs_get_classdev_device(class_dev);
b608ade8
KS
263
264 /* look the device chain upwards */
265 while (sysfs_dev != NULL) {
266 if (sysfs_dev->bus[0] != '\0') {
267 add_attribute("", level);
268 sprintf(key, "BUS=\"%s\"", sysfs_dev->bus);
269 add_attribute(key, level);
270 sprintf(key, "ID=\"%s\"", sysfs_dev->bus_id);
271 add_attribute(key, level);
272
273 /* open sysfs device directory and print all attributes */
274 add_all_attributes(sysfs_dev->path, level);
275 }
276 level++;
277
278 sysfs_dev_parent = sysfs_get_device_parent(sysfs_dev);
279 if (sysfs_dev_parent == NULL)
280 break;
281
b608ade8
KS
282 sysfs_dev = sysfs_dev_parent;
283 }
b608ade8
KS
284
285exit:
dd785ba6 286 sysfs_close_class_device(class_dev);
b608ade8
KS
287 return retval;
288}
289
290
291int main(int argc, char *argv[]) {
292 newtComponent lbox, run, lattr;
293 newtComponent quit, form, answer;
294 newtGrid grid, grid2;
295 char roottext[81];
296 char path[NAME_SIZE];
297 struct device *dev;
bbbe503e 298 long time_last;
b608ade8
KS
299 int count_last;
300
301 newtInit();
302 newtCls();
303
304 newtWinMessage("udevruler", "Ok",
305 "This program lets you select a device currently present "
306 "on the system and you may choose the attributes to uniquely "
307 "name the device with a udev rule.\n"
308 "No configuration will be changed, it just prints the rule "
309 "to place in a udev.rules configuration file. The \"%k\" in the "
310 "NAME key of the printed rule may be replaced by the name the "
311 "node should have.");
312
313 init_logging("udevruler");
314 udev_init_config();
315 get_all_devices();
316
317 lbox = newtListbox(2, 1, 10, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
318
319 /* look for last discovered device */
320 time_last = 0;
321 list_for_each_entry(dev, &device_list, list)
bbbe503e
KS
322 if (dev->config_uptime > time_last)
323 time_last = dev->config_uptime;
b608ade8
KS
324
325 /* skip if more than 16 recent devices */
326 count_last = 0;
327 list_for_each_entry(dev, &device_list, list) {
bbbe503e 328 if (dev->config_uptime < time_last - 10)
b608ade8
KS
329 continue;
330 count_last++;
331 }
332
333 /* add devices up to 10 seconds older than the last one */
334 if (count_last < 16) {
335 newtListboxAppendEntry(lbox, "--- last dicovered ---", NULL);
336 list_for_each_entry(dev, &device_list, list) {
bbbe503e 337 if (dev->config_uptime < time_last - 10)
b608ade8
KS
338 continue;
339
340 dbg("%s %i", dev->name, dev->config_line);
341 newtListboxAppendEntry(lbox, dev->name, (void*) dev);
342 dev->added = 1;
343 }
344 newtListboxAppendEntry(lbox, "", NULL);
345 }
346
347 /* add devices not catched by a rule */
348 newtListboxAppendEntry(lbox, "--- not configured by a rule ---", NULL);
349 list_for_each_entry(dev, &device_list, list) {
350 if (dev->added)
351 continue;
352
353 if (dev->config_line != 0)
354 continue;
355
356 dbg("%s %i", dev->name, dev->config_line);
357 newtListboxAppendEntry(lbox, dev->name, (void*) dev);
358 dev->added = 1;
359 }
360 newtListboxAppendEntry(lbox, "", NULL);
361
362 /* add remaining devices */
363 newtListboxAppendEntry(lbox, "--- configured by a rule ---", NULL);
364 list_for_each_entry(dev, &device_list, list) {
365 if (dev->added)
366 continue;
367
368 dbg("%s %i", dev->name, dev->config_line);
369 newtListboxAppendEntry(lbox, dev->name, (void*) dev);
370 }
371
372 newtPushHelpLine(" <Tab>/<Alt-Tab> between elements | Use <Enter> to select a device");
373 snprintf(roottext, sizeof(roottext), "simple udev rule composer, version %s, (c) 2004 can't sleep team", UDEV_VERSION);
374 roottext[sizeof(roottext)-1] = '\0';
375 newtDrawRootText(0, 0, roottext);
376
377 form = newtForm(NULL, NULL, 0);
378 grid = newtCreateGrid(1, 2);
379 grid2 = newtButtonBar("Select device", &run, "Quit", &quit, NULL);
380 newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, lbox, 1, 0, 1, 0, NEWT_ANCHOR_TOP, 0);
381 newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, grid2, 0, 1, 0, 0, NEWT_ANCHOR_TOP, 0);
382 newtFormAddComponents(form, lbox, run, quit, NULL);
383 newtGridWrappedWindow(grid,"Choose the device for which to compose a rule");
384 newtGridFree(grid, 1);
385
386 while (1) {
387 struct attribute *attr;
388 newtComponent ok, back, form2, answer2, text;
389 newtGrid grid3, grid4;
390 int i;
391 int numitems;
392 struct attribute **selattr;
dd785ba6 393 char text_rule[255];
b608ade8
KS
394
395 answer = newtRunForm(form);
396 if (answer == quit)
397 break;
398
399 dev = (struct device *) newtListboxGetCurrent(lbox);
400 if (dev == NULL)
401 continue;
402
403 if (dev->config_line > 0)
404 snprintf(text_rule, sizeof(text_rule),
405 "The device is handled by a rule in the file '%s', line %i.",
406 dev->config_file, dev->config_line);
407 else
408 strcpy(text_rule, "The device was not handled by a rule.");
409
410 strfieldcpy(path, sysfs_path);
411 strfieldcat(path, dev->devpath);
412 dbg("look at sysfs device '%s'", path);
413 get_all_attributes(path);
414
415 grid3 = newtCreateGrid(1, 3);
416 form2 = newtForm(NULL, NULL, 0);
417 grid4 = newtButtonBar("Ok", &ok, "Back", &back, NULL);
418
419 lattr = newtListbox(-1, -1, 10, NEWT_FLAG_MULTIPLE | NEWT_FLAG_BORDER | NEWT_FLAG_RETURNEXIT);
420 list_for_each_entry(attr, &attribute_list, list)
421 newtListboxAddEntry(lattr, attr->key, (void *) attr);
422
423 text = newtTextbox(-1, -1, 50, 2, NEWT_FLAG_WRAP);
424 newtTextboxSetText(text, text_rule);
425
426 newtGridSetField(grid3, 0, 0, NEWT_GRID_COMPONENT, lattr, 0, 0, 0, 1, 0, 0);
427 newtGridSetField(grid3, 0, 1, NEWT_GRID_COMPONENT, text, 0, 0, 0, 1, 0, 0);
428 newtGridSetField(grid3, 0, 2, NEWT_GRID_SUBGRID, grid4, 0, 1, 0, 0, NEWT_ANCHOR_TOP, 0);
429
430 newtFormAddComponents(form2, text, lattr, ok, back, NULL);
431 newtGridWrappedWindow(grid3, "Select one ore more attributes within one section with the space bar");
432 newtGridFree(grid3, 1);
433
434 while (1) {
435 char rule[255];
436 int onelevel;
437 int skipped;
438
439 answer2 = newtRunForm(form2);
440 if (answer2 == back)
441 break;
442
443 selattr = (struct attribute **) newtListboxGetSelection(lattr, &numitems);
444 if (selattr == NULL)
445 continue;
446
447 rule[0] = '\0';
448 onelevel = -1;
449 skipped = 0;
450 for (i = 0; i < numitems; i++) {
451 if (selattr[i]->key[0] == '\0')
452 continue;
453
454 if (onelevel != -1) {
455 if (onelevel != selattr[i]->level) {
456 skipped = 1;
457 continue;
458 }
459 } else {
460 onelevel = selattr[i]->level;
461 }
462
463 dbg("'%s'\n", selattr[i]->key);
464 strfieldcat(rule, selattr[i]->key);
465 strfieldcat(rule, ", ");
466 }
467 if (skipped) {
468 newtWinMessage("error", "Ok", "Please select only attributes within one section");
469 continue;
470 }
471
472 if (strlen(rule) > 200) {
473 newtWinMessage("error", "Ok", "The line is too long, please select fewer attributes.");
474 } else {
475 if (rule[0] == '\0')
476 continue;
477
478 strfieldcat(rule, "NAME=\"%k\"");
479 newtWinMessage("the rule to place in config file", "Ok", rule);
480 }
481 }
482
483 newtPopWindow();
484 }
485
486 newtPopWindow();
487 newtFormDestroy(form);
488 newtFinished();
489 return 0;
490}