]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/udev-test.pl
tree-wide: use mode=0nnn for mount option
[thirdparty/systemd.git] / test / udev-test.pl
1 #!/usr/bin/env perl
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3
4 # udev test
5 #
6 # Provides automated testing of the udev binary.
7 # The whole test is self contained in this file, except the matching sysfs tree.
8 # Simply extend the @tests array, to add a new test variant.
9 #
10 # Every test is driven by its own temporary config file.
11 # This program prepares the environment, creates the config and calls udev.
12 #
13 # udev parses the rules, looks at the provided sysfs and
14 # first creates and then removes the device node.
15 # After creation and removal the result is checked against the
16 # expected value and the result is printed.
17 #
18 # Copyright © 2004 Leann Ogasawara <ogasawara@osdl.org>
19
20 use warnings;
21 use strict;
22
23 BEGIN {
24 my $EXIT_TEST_SKIP = 77;
25
26 unless (eval "use POSIX qw(WIFEXITED WEXITSTATUS);
27 use Cwd qw(getcwd abs_path);
28 use IPC::Semaphore;
29 use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT);
30 use Time::HiRes qw(usleep); 1") {
31 warn "Failed to import dependencies, skipping the test: $@";
32 exit($EXIT_TEST_SKIP);
33 }
34 }
35
36 # Relax sd-device's sysfs verification, since we want to provide a fake sysfs
37 # here that actually is a tmpfs.
38 $ENV{"SYSTEMD_DEVICE_VERIFY_SYSFS"}="0";
39
40 my $udev_bin = "./test-udev";
41 my $valgrind = 0;
42 my $gdb = 0;
43 my $strace = 0;
44 my $udev_bin_valgrind = "valgrind --tool=memcheck --leak-check=yes --track-origins=yes --quiet $udev_bin";
45 my $udev_bin_gdb = "gdb --args $udev_bin";
46 my $udev_bin_strace = "strace -efile $udev_bin";
47 my $udev_run = "test/run";
48 my $udev_tmpfs = "test/tmpfs";
49 my $udev_sys = "${udev_tmpfs}/sys";
50 my $udev_dev = "${udev_tmpfs}/dev";
51 my $udev_rules_dir = "$udev_run/udev/rules.d";
52 my $udev_rules = "$udev_rules_dir/udev-test.rules";
53 my $EXIT_TEST_SKIP = 77;
54
55 my $rules_10k_tags = "";
56 for (my $i = 1; $i <= 10000; ++$i) {
57 $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n";
58 }
59
60 my $rules_10k_tags_continuation = "KERNEL==\"sda\", \\\n";
61 for (my $i = 1; $i < 10000; ++$i) {
62 $rules_10k_tags_continuation .= 'TAG+="test' . $i . "\",\\\n";
63 }
64 $rules_10k_tags_continuation .= "TAG+=\"test10000\"\\n";
65
66 # Create a device list with all block devices under /sys
67 # (except virtual devices and cd-roms)
68 # the optional argument exp_func returns expected and non-expected
69 # symlinks for the device.
70 sub all_block_devs {
71 my ($exp_func) = @_;
72 my @devices;
73
74 foreach my $bd (glob "$udev_sys/dev/block/*") {
75 my $tgt = readlink($bd);
76 my ($exp, $notexp) = (undef, undef);
77
78 next if ($tgt =~ m!/virtual/! || $tgt =~ m!/sr[0-9]*$!);
79
80 $tgt =~ s!^\.\./\.\.!!;
81 ($exp, $notexp) = $exp_func->($tgt) if defined($exp_func);
82 my $device = {
83 devpath => $tgt,
84 exp_links => $exp,
85 not_exp_links => $notexp,
86 };
87 push(@devices, $device);
88 }
89 return \@devices;
90 }
91
92 # This generator returns a suitable exp_func for use with
93 # all_block_devs().
94 sub expect_for_some {
95 my ($pattern, $links, $donot) = @_;
96 my $_expect = sub {
97 my ($name) = @_;
98
99 if ($name =~ /$pattern/) {
100 return ($links, undef);
101 } elsif ($donot) {
102 return (undef, $links);
103 } else {
104 return (undef, undef);
105 }
106 };
107 return $_expect;
108 }
109
110 my @tests = (
111 {
112 desc => "no rules",
113 devices => [
114 {
115 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
116 exp_rem_error => "yes",
117 },
118 {
119 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
120 exp_rem_error => "yes",
121 }],
122 rules => <<EOF
123 #
124 EOF
125 },
126 {
127 desc => "label test of scsi disc",
128 devices => [
129 {
130 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
131 exp_links => ["boot_disk"],
132 }],
133 rules => <<EOF
134 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
135 KERNEL=="ttyACM0", SYMLINK+="modem"
136 EOF
137 },
138 {
139 desc => "label test of scsi disc",
140 devices => [
141 {
142 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
143 exp_links => ["boot_disk"],
144 }],
145 rules => <<EOF
146 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
147 KERNEL=="ttyACM0", SYMLINK+="modem"
148 EOF
149 },
150 {
151 desc => "label test of scsi disc",
152 devices => [
153 {
154 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
155 exp_links => ["boot_disk"],
156 }],
157 rules => <<EOF
158 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
159 KERNEL=="ttyACM0", SYMLINK+="modem"
160 EOF
161 },
162 {
163 desc => "label test of scsi partition",
164 devices => [
165 {
166 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
167 exp_links => ["boot_disk1"],
168 }],
169 rules => <<EOF
170 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
171 EOF
172 },
173 {
174 desc => "label test of pattern match",
175 devices => [
176 {
177 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
178 exp_links => ["boot_disk1", "boot_disk1-4", "boot_disk1-5"],
179 not_exp_links => ["boot_disk1-1", "boot_disk1-2", "boot_disk1-3", "boot_disk1-6", "boot_disk1-7"]
180 }],
181 rules => <<EOF
182 SUBSYSTEMS=="scsi", ATTRS{vendor}=="?ATA", SYMLINK+="boot_disk%n-1"
183 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA?", SYMLINK+="boot_disk%n-2"
184 SUBSYSTEMS=="scsi", ATTRS{vendor}=="A??", SYMLINK+="boot_disk%n"
185 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATAS", SYMLINK+="boot_disk%n-3"
186 SUBSYSTEMS=="scsi", ATTRS{vendor}=="AT?", SYMLINK+="boot_disk%n-4"
187 SUBSYSTEMS=="scsi", ATTRS{vendor}=="??A", SYMLINK+="boot_disk%n-5"
188 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", GOTO="skip-6"
189 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n-6"
190 LABEL="skip-6"
191 SUBSYSTEMS=="scsi", GOTO="skip-7"
192 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n-7"
193 LABEL="skip-7"
194 EOF
195 },
196 {
197 desc => "label test of multiple sysfs files",
198 devices => [
199 {
200 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
201 exp_links => ["boot_disk1"],
202 not_exp_links => ["boot_diskX1"],
203 }],
204 rules => <<EOF
205 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS X ", SYMLINK+="boot_diskX%n"
206 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK+="boot_disk%n"
207 EOF
208 },
209 {
210 desc => "label test of max sysfs files (skip invalid rule)",
211 devices => [
212 {
213 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
214 exp_links => ["boot_disk1", "boot_diskXY1"],
215 not_exp_links => ["boot_diskXX1"],
216 }],
217 rules => <<EOF
218 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", ATTRS{queue_depth}=="32", SYMLINK+="boot_diskXX%n"
219 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", ATTRS{queue_depth}=="1", SYMLINK+="boot_diskXY%n"
220 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", SYMLINK+="boot_disk%n"
221 EOF
222 },
223 {
224 desc => "catch device by *",
225 devices => [
226 {
227 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
228 exp_links => ["modem/0", "catch-all"],
229 }],
230 rules => <<EOF
231 KERNEL=="ttyACM*", SYMLINK+="modem/%n"
232 KERNEL=="*", SYMLINK+="catch-all"
233 EOF
234 },
235 # 10
236 {
237 desc => "catch device by * - take 2",
238 devices => [
239 {
240 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
241 exp_links => ["modem/0"],
242 not_exp_links => ["bad"],
243 }],
244 rules => <<EOF
245 KERNEL=="*ACM1", SYMLINK+="bad"
246 KERNEL=="*ACM0", SYMLINK+="modem/%n"
247 EOF
248 },
249 {
250 desc => "catch device by ?",
251 devices => [
252 {
253 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
254 exp_links => ["modem/0"],
255 not_exp_links => ["modem/0-1", "modem/0-2"],
256 }],
257 rules => <<EOF
258 KERNEL=="ttyACM??*", SYMLINK+="modem/%n-1"
259 KERNEL=="ttyACM??", SYMLINK+="modem/%n-2"
260 KERNEL=="ttyACM?", SYMLINK+="modem/%n"
261 EOF
262 },
263 {
264 desc => "catch device by character class",
265 devices => [
266 {
267 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
268 exp_links => ["modem/0"],
269 not_exp_links => ["modem/0-1", "modem/0-2"],
270 }],
271 rules => <<EOF
272 KERNEL=="ttyACM[A-Z]*", SYMLINK+="modem/%n-1"
273 KERNEL=="ttyACM?[0-9]", SYMLINK+="modem/%n-2"
274 KERNEL=="ttyACM[0-9]*", SYMLINK+="modem/%n"
275 EOF
276 },
277 {
278 desc => "don't replace kernel name",
279 devices => [
280 {
281 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
282 exp_links => ["modem"],
283 }],
284 rules => <<EOF
285 KERNEL=="ttyACM0", SYMLINK+="modem"
286 EOF
287 },
288 {
289 desc => "Handle comment lines in config file (and don't replace kernel name)",
290 devices => [
291 {
292 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
293 exp_links => ["modem"],
294 }],
295 rules => <<EOF
296 # this is a comment
297 KERNEL=="ttyACM0", SYMLINK+="modem"
298
299 EOF
300 },
301 {
302 desc => "Handle comment lines in config file with whitespace (and don't replace kernel name)",
303 devices => [
304 {
305 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
306 exp_links => ["modem"],
307 }],
308 rules => <<EOF
309 # this is a comment with whitespace before the comment
310 KERNEL=="ttyACM0", SYMLINK+="modem"
311
312 EOF
313 },
314 {
315 desc => "Handle whitespace only lines (and don't replace kernel name)",
316 devices => [
317 {
318 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
319 exp_links => ["whitespace"],
320 }],
321 rules => <<EOF
322
323
324
325 # this is a comment with whitespace before the comment
326 KERNEL=="ttyACM0", SYMLINK+="whitespace"
327
328
329
330 EOF
331 },
332 {
333 desc => "Handle empty lines in config file (and don't replace kernel name)",
334 devices => [
335 {
336 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
337 exp_links => ["modem"],
338 }],
339 rules => <<EOF
340
341 KERNEL=="ttyACM0", SYMLINK+="modem"
342
343 EOF
344 },
345 {
346 desc => "Handle backslashed multi lines in config file (and don't replace kernel name)",
347 devices => [
348 {
349 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
350 exp_links => ["modem"],
351 }],
352 rules => <<EOF
353 KERNEL=="ttyACM0", \\
354 SYMLINK+="modem"
355
356 EOF
357 },
358 {
359 desc => "preserve backslashes, if they are not for a newline",
360 devices => [
361 {
362 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
363 exp_links => ["aaa"],
364 }],
365 rules => <<EOF
366 KERNEL=="ttyACM0", PROGRAM=="/bin/echo -e \\101", RESULT=="A", SYMLINK+="aaa"
367 EOF
368 },
369 # 20
370 {
371 desc => "Handle stupid backslashed multi lines in config file (and don't replace kernel name)",
372 devices => [
373 {
374 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
375 exp_links => ["modem"],
376 }],
377 rules => <<EOF
378
379 #
380 \\
381
382 \\
383
384 #\\
385
386 KERNEL=="ttyACM0", \\
387 SYMLINK+="modem"
388
389 EOF
390 },
391 {
392 desc => "subdirectory handling",
393 devices => [
394 {
395 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
396 exp_links => ["sub/direct/ory/modem"],
397 }],
398 rules => <<EOF
399 KERNEL=="ttyACM0", SYMLINK+="sub/direct/ory/modem"
400 EOF
401 },
402 {
403 desc => "parent device name match of scsi partition",
404 devices => [
405 {
406 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
407 exp_links => ["first_disk5"],
408 }],
409 rules => <<EOF
410 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="first_disk%n"
411 EOF
412 },
413 {
414 desc => "test substitution chars",
415 devices => [
416 {
417 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
418 exp_links => ["Major:8:minor:5:kernelnumber:5:id:0:0:0:0"],
419 }],
420 rules => <<EOF
421 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="Major:%M:minor:%m:kernelnumber:%n:id:%b"
422 EOF
423 },
424 {
425 desc => "import of shell-value returned from program",
426 devices => [
427 {
428 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
429 exp_links => ["node12345678"],
430 }],
431 rules => <<EOF
432 SUBSYSTEMS=="scsi", IMPORT{program}="/bin/echo -e \' TEST_KEY=12345678\\n TEST_key2=98765\'", SYMLINK+="node\$env{TEST_KEY}"
433 KERNEL=="ttyACM0", SYMLINK+="modem"
434 EOF
435 },
436 {
437 desc => "substitution of sysfs value (%s{file})",
438 devices => [
439 {
440 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
441 exp_links => ["disk-ATA-sda"],
442 not_exp_links => ["modem"],
443 }],
444 rules => <<EOF
445 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="disk-%s{vendor}-%k"
446 KERNEL=="ttyACM0", SYMLINK+="modem"
447 EOF
448 },
449 {
450 desc => "program result substitution",
451 devices => [
452 {
453 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
454 exp_links => ["special-device-5"],
455 not_exp_links => ["not"],
456 }],
457 rules => <<EOF
458 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n special-device", RESULT=="-special-*", SYMLINK+="not"
459 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n special-device", RESULT=="special-*", SYMLINK+="%c-%n"
460 EOF
461 },
462 {
463 desc => "program result substitution (newline removal)",
464 devices => [
465 {
466 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
467 exp_links => ["newline_removed"],
468 }],
469 rules => <<EOF
470 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo test", RESULT=="test", SYMLINK+="newline_removed"
471 EOF
472 },
473 {
474 desc => "program result substitution",
475 devices => [
476 {
477 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
478 exp_links => ["test-0:0:0:0"],
479 }],
480 rules => <<EOF
481 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n test-%b", RESULT=="test-0:0*", SYMLINK+="%c"
482 EOF
483 },
484 {
485 desc => "program with lots of arguments",
486 devices => [
487 {
488 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
489 exp_links => ["foo9"],
490 not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"],
491 }],
492 rules => <<EOF
493 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="%c{7}"
494 EOF
495 },
496 {
497 desc => "program with subshell",
498 devices => [
499 {
500 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
501 exp_links => ["bar9"],
502 not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"],
503 }],
504 rules => <<EOF
505 SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'echo foo3 foo4 foo5 foo6 foo7 foo8 foo9 | sed s/foo9/bar9/'", KERNEL=="sda5", SYMLINK+="%c{7}"
506 EOF
507 },
508 {
509 desc => "program arguments combined with apostrophes",
510 devices => [
511 {
512 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
513 exp_links => ["foo7"],
514 not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo8"],
515 }],
516 rules => <<EOF
517 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n 'foo3 foo4' 'foo5 foo6 foo7 foo8'", KERNEL=="sda5", SYMLINK+="%c{5}"
518 EOF
519 },
520 {
521 desc => "program arguments combined with escaped double quotes, part 1",
522 devices => [
523 {
524 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
525 exp_links => ["foo2"],
526 not_exp_links => ["foo1"],
527 }],
528 rules => <<EOF
529 SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'printf %%s \\\"foo1 foo2\\\" | grep \\\"foo1 foo2\\\"'", KERNEL=="sda5", SYMLINK+="%c{2}"
530 EOF
531 },
532 {
533 desc => "program arguments combined with escaped double quotes, part 2",
534 devices => [
535 {
536 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
537 exp_links => ["foo2"],
538 not_exp_links => ["foo1"],
539 }],
540 rules => <<EOF
541 SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c \\\"printf %%s 'foo1 foo2' | grep 'foo1 foo2'\\\"", KERNEL=="sda5", SYMLINK+="%c{2}"
542 EOF
543 },
544 {
545 desc => "program arguments combined with escaped double quotes, part 3",
546 devices => [
547 {
548 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
549 exp_links => ["foo2"],
550 not_exp_links => ["foo1", "foo3"],
551 }],
552 rules => <<EOF
553 SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'printf \\\"%%s %%s\\\" \\\"foo1 foo2\\\" \\\"foo3\\\"| grep \\\"foo1 foo2\\\"'", KERNEL=="sda5", SYMLINK+="%c{2}"
554 EOF
555 },
556 {
557 desc => "characters before the %c{N} substitution",
558 devices => [
559 {
560 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
561 exp_links => ["my-foo9"],
562 }],
563 rules => <<EOF
564 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="my-%c{7}"
565 EOF
566 },
567 {
568 desc => "substitute the second to last argument",
569 devices => [
570 {
571 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
572 exp_links => ["my-foo8"],
573 not_exp_links => ["my-foo3", "my-foo4", "my-foo5", "my-foo6", "my-foo7", "my-foo9"],
574 }],
575 rules => <<EOF
576 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="my-%c{6}"
577 EOF
578 },
579 {
580 desc => "test substitution by variable name",
581 devices => [
582 {
583 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
584 exp_links => ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"],
585 }],
586 rules => <<EOF
587 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="Major:\$major-minor:\$minor-kernelnumber:\$number-id:\$id"
588 EOF
589 },
590 {
591 desc => "test substitution by variable name 2",
592 devices => [
593 {
594 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
595 exp_links => ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"],
596 }],
597 rules => <<EOF
598 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="Major:\$major-minor:%m-kernelnumber:\$number-id:\$id"
599 EOF
600 },
601 {
602 desc => "test substitution by variable name 3",
603 devices => [
604 {
605 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
606 exp_links => ["850:0:0:05"],
607 }],
608 rules => <<EOF
609 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="%M%m%b%n"
610 EOF
611 },
612 {
613 desc => "test substitution by variable name 4",
614 devices => [
615 {
616 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
617 exp_links => ["855"],
618 }],
619 rules => <<EOF
620 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="\$major\$minor\$number"
621 EOF
622 },
623 {
624 desc => "test substitution by variable name 5",
625 devices => [
626 {
627 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
628 exp_links => ["8550:0:0:0"],
629 }],
630 rules => <<EOF
631 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="\$major%m%n\$id"
632 EOF
633 },
634 {
635 desc => "non matching SUBSYSTEMS for device with no parent",
636 devices => [
637 {
638 devpath => "/devices/virtual/tty/console",
639 exp_links => ["TTY"],
640 not_exp_links => ["foo"],
641 }],
642 rules => <<EOF
643 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo", RESULT=="foo", SYMLINK+="foo"
644 KERNEL=="console", SYMLINK+="TTY"
645 EOF
646 },
647 {
648 desc => "non matching SUBSYSTEMS",
649 devices => [
650 {
651 devpath => "/devices/virtual/tty/console",
652 exp_links => ["TTY"],
653 not_exp_links => ["foo"],
654 }],
655 rules => <<EOF
656 SUBSYSTEMS=="foo", ATTRS{dev}=="5:1", SYMLINK+="foo"
657 KERNEL=="console", SYMLINK+="TTY"
658 EOF
659 },
660 {
661 desc => "ATTRS match",
662 devices => [
663 {
664 devpath => "/devices/virtual/tty/console",
665 exp_links => ["foo", "TTY"],
666 }],
667 rules => <<EOF
668 KERNEL=="console", SYMLINK+="TTY"
669 ATTRS{dev}=="5:1", SYMLINK+="foo"
670 EOF
671 },
672 {
673 desc => "ATTR (empty file)",
674 devices => [
675 {
676 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
677 exp_links => ["empty", "not-something"],
678 not_exp_links => ["something", "not-empty"],
679 }],
680 rules => <<EOF
681 KERNEL=="sda", ATTR{test_empty_file}=="?*", SYMLINK+="something"
682 KERNEL=="sda", ATTR{test_empty_file}!="", SYMLINK+="not-empty"
683 KERNEL=="sda", ATTR{test_empty_file}=="", SYMLINK+="empty"
684 KERNEL=="sda", ATTR{test_empty_file}!="?*", SYMLINK+="not-something"
685 EOF
686 },
687 {
688 desc => "ATTR (non-existent file)",
689 devices => [
690 {
691 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
692 exp_links => ["non-existent", "wrong"],
693 not_exp_links => ["something", "empty", "not-empty",
694 "not-something", "something"],
695 }],
696 rules => <<EOF
697 KERNEL=="sda", ATTR{nofile}=="?*", SYMLINK+="something"
698 KERNEL=="sda", ATTR{nofile}!="", SYMLINK+="not-empty"
699 KERNEL=="sda", ATTR{nofile}=="", SYMLINK+="empty"
700 KERNEL=="sda", ATTR{nofile}!="?*", SYMLINK+="not-something"
701 KERNEL=="sda", TEST!="nofile", SYMLINK+="non-existent"
702 KERNEL=="sda", SYMLINK+="wrong"
703 EOF
704 },
705 {
706 desc => "program and bus type match",
707 devices => [
708 {
709 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
710 exp_links => ["scsi-0:0:0:0"],
711 }],
712 rules => <<EOF
713 SUBSYSTEMS=="usb", PROGRAM=="/bin/echo -n usb-%b", SYMLINK+="%c"
714 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n scsi-%b", SYMLINK+="%c"
715 SUBSYSTEMS=="foo", PROGRAM=="/bin/echo -n foo-%b", SYMLINK+="%c"
716 EOF
717 },
718 {
719 desc => "sysfs parent hierarchy",
720 devices => [
721 {
722 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
723 exp_links => ["modem"],
724 }],
725 rules => <<EOF
726 ATTRS{idProduct}=="007b", SYMLINK+="modem"
727 EOF
728 },
729 {
730 desc => "name test with ! in the name",
731 devices => [
732 {
733 devpath => "/devices/virtual/block/fake!blockdev0",
734 devnode => "fake/blockdev0",
735 exp_links => ["is/a/fake/blockdev0"],
736 not_exp_links => ["is/not/a/fake/blockdev0", "modem"],
737 }],
738 rules => <<EOF
739 SUBSYSTEMS=="scsi", SYMLINK+="is/not/a/%k"
740 SUBSYSTEM=="block", SYMLINK+="is/a/%k"
741 KERNEL=="ttyACM0", SYMLINK+="modem"
742 EOF
743 },
744 {
745 desc => "name test with ! in the name, but no matching rule",
746 devices => [
747 {
748 devpath => "/devices/virtual/block/fake!blockdev0",
749 devnode => "fake/blockdev0",
750 not_exp_links => ["modem"],
751 }],
752 rules => <<EOF
753 KERNEL=="ttyACM0", SYMLINK+="modem"
754 EOF
755 },
756 {
757 desc => "KERNELS rule",
758 devices => [
759 {
760 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
761 exp_links => ["scsi-0:0:0:0"],
762 not_exp_links => ["no-match", "short-id", "not-scsi"],
763 }],
764 rules => <<EOF
765 SUBSYSTEMS=="usb", KERNELS=="0:0:0:0", SYMLINK+="not-scsi"
766 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:1", SYMLINK+="no-match"
767 SUBSYSTEMS=="scsi", KERNELS==":0", SYMLINK+="short-id"
768 SUBSYSTEMS=="scsi", KERNELS=="/0:0:0:0", SYMLINK+="no-match"
769 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="scsi-0:0:0:0"
770 EOF
771 },
772 {
773 desc => "KERNELS wildcard all",
774 devices => [
775 {
776 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
777 exp_links => ["scsi-0:0:0:0"],
778 not_exp_links => ["no-match", "before"],
779 }],
780 rules => <<EOF
781 SUBSYSTEMS=="scsi", KERNELS=="*:1", SYMLINK+="no-match"
782 SUBSYSTEMS=="scsi", KERNELS=="*:0:1", SYMLINK+="no-match"
783 SUBSYSTEMS=="scsi", KERNELS=="*:0:0:1", SYMLINK+="no-match"
784 SUBSYSTEMS=="scsi", KERNEL=="0:0:0:0", SYMLINK+="before"
785 SUBSYSTEMS=="scsi", KERNELS=="*", SYMLINK+="scsi-0:0:0:0"
786 EOF
787 },
788 {
789 desc => "KERNELS wildcard partial",
790 devices => [
791 {
792 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
793 exp_links => ["scsi-0:0:0:0", "before"],
794 }],
795 rules => <<EOF
796 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="before"
797 SUBSYSTEMS=="scsi", KERNELS=="*:0", SYMLINK+="scsi-0:0:0:0"
798 EOF
799 },
800 {
801 desc => "KERNELS wildcard partial 2",
802 devices => [
803 {
804 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
805 exp_links => ["scsi-0:0:0:0", "before"],
806 }],
807 rules => <<EOF
808 SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="before"
809 SUBSYSTEMS=="scsi", KERNELS=="*:0:0:0", SYMLINK+="scsi-0:0:0:0"
810 EOF
811 },
812 {
813 desc => "substitute attr with link target value (first match)",
814 devices => [
815 {
816 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
817 exp_links => ["driver-is-sd"],
818 }],
819 rules => <<EOF
820 SUBSYSTEMS=="scsi", SYMLINK+="driver-is-\$attr{driver}"
821 EOF
822 },
823 {
824 desc => "substitute attr with link target value (currently selected device)",
825 devices => [
826 {
827 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
828 exp_links => ["driver-is-ahci"],
829 }],
830 rules => <<EOF
831 SUBSYSTEMS=="pci", SYMLINK+="driver-is-\$attr{driver}"
832 EOF
833 },
834 {
835 desc => "ignore ATTRS attribute whitespace",
836 devices => [
837 {
838 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
839 exp_links => ["ignored"],
840 }],
841 rules => <<EOF
842 SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE SPACE", SYMLINK+="ignored"
843 EOF
844 },
845 {
846 desc => "do not ignore ATTRS attribute whitespace",
847 devices => [
848 {
849 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
850 exp_links => ["matched-with-space"],
851 not_exp_links => ["wrong-to-ignore"],
852 }],
853 rules => <<EOF
854 SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE SPACE ", SYMLINK+="wrong-to-ignore"
855 SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE SPACE ", SYMLINK+="matched-with-space"
856 EOF
857 },
858 {
859 desc => "permissions USER=bad GROUP=name",
860 devices => [
861 {
862 devpath => "/devices/virtual/tty/tty33",
863 exp_perms => "0:0:0600",
864 }],
865 rules => <<EOF
866 KERNEL=="tty33", OWNER="bad", GROUP="name"
867 EOF
868 },
869 {
870 desc => "permissions OWNER=1",
871 devices => [
872 {
873 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
874 exp_links => ["node"],
875 exp_perms => "1::0600",
876 }],
877 rules => <<EOF
878 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="1"
879 EOF
880 },
881 {
882 desc => "permissions GROUP=1",
883 devices => [
884 {
885 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
886 exp_links => ["node"],
887 exp_perms => ":1:0660",
888 }],
889 rules => <<EOF
890 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", GROUP="1"
891 EOF
892 },
893 {
894 desc => "textual user id",
895 devices => [
896 {
897 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
898 exp_links => ["node"],
899 exp_perms => "daemon::0600",
900 }],
901 rules => <<EOF
902 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="daemon"
903 EOF
904 },
905 {
906 desc => "textual group id",
907 devices => [
908 {
909 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
910 exp_links => ["node"],
911 exp_perms => ":daemon:0660",
912 }],
913 rules => <<EOF
914 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", GROUP="daemon"
915 EOF
916 },
917 {
918 desc => "textual user/group id",
919 devices => [
920 {
921 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
922 exp_links => ["node"],
923 exp_perms => "root:audio:0660",
924 }],
925 rules => <<EOF
926 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="root", GROUP="audio"
927 EOF
928 },
929 {
930 desc => "permissions MODE=0777",
931 devices => [
932 {
933 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
934 exp_links => ["node"],
935 exp_perms => "::0777",
936 }],
937 rules => <<EOF
938 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", MODE="0777"
939 EOF
940 },
941 {
942 desc => "permissions OWNER=1 GROUP=1 MODE=0777",
943 devices => [
944 {
945 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
946 exp_links => ["node"],
947 exp_perms => "1:1:0777",
948 }],
949 rules => <<EOF
950 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="1", GROUP="1", MODE="0777"
951 EOF
952 },
953 {
954 desc => "permissions OWNER to 1",
955 devices => [
956 {
957 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
958 exp_perms => "1::",
959 }],
960 rules => <<EOF
961 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", OWNER="1"
962 EOF
963 },
964 {
965 desc => "permissions GROUP to 1",
966 devices => [
967 {
968 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
969 exp_perms => ":1:0660",
970 }],
971 rules => <<EOF
972 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", GROUP="1"
973 EOF
974 },
975 {
976 desc => "permissions MODE to 0060",
977 devices => [
978 {
979 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
980 exp_perms => "::0060",
981 }],
982 rules => <<EOF
983 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", MODE="0060"
984 EOF
985 },
986 {
987 desc => "permissions OWNER, GROUP, MODE",
988 devices => [
989 {
990 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
991 exp_perms => "1:1:0777",
992 }],
993 rules => <<EOF
994 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", OWNER="1", GROUP="1", MODE="0777"
995 EOF
996 },
997 {
998 desc => "permissions only rule",
999 devices => [
1000 {
1001 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1002 exp_perms => "1:1:0777",
1003 }],
1004 rules => <<EOF
1005 KERNEL=="ttyACM[0-9]*", OWNER="1", GROUP="1", MODE="0777"
1006 KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444"
1007 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n"
1008 EOF
1009 },
1010 {
1011 desc => "multiple permissions only rule",
1012 devices => [
1013 {
1014 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1015 exp_perms => "1:1:0777",
1016 }],
1017 rules => <<EOF
1018 SUBSYSTEM=="tty", OWNER="1"
1019 SUBSYSTEM=="tty", GROUP="1"
1020 SUBSYSTEM=="tty", MODE="0777"
1021 KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444"
1022 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n"
1023 EOF
1024 },
1025 {
1026 desc => "permissions only rule with override at SYMLINK+ rule",
1027 devices => [
1028 {
1029 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1030 exp_perms => "1:2:0777",
1031 }],
1032 rules => <<EOF
1033 SUBSYSTEM=="tty", OWNER="1"
1034 SUBSYSTEM=="tty", GROUP="1"
1035 SUBSYSTEM=="tty", MODE="0777"
1036 KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444"
1037 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", GROUP="2"
1038 EOF
1039 },
1040 {
1041 desc => "major/minor number test",
1042 devices => [
1043 {
1044 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1045 exp_links => ["node"],
1046 exp_majorminor => "8:0",
1047 }],
1048 rules => <<EOF
1049 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node"
1050 EOF
1051 },
1052 {
1053 desc => "big major number test",
1054 devices => [
1055 {
1056 devpath => "/devices/virtual/misc/misc-fake1",
1057 exp_links => ["node"],
1058 exp_majorminor => "4095:1",
1059 }],
1060 rules => <<EOF
1061 KERNEL=="misc-fake1", SYMLINK+="node"
1062 EOF
1063 },
1064 {
1065 desc => "big major and big minor number test",
1066 devices => [
1067 {
1068 devpath => "/devices/virtual/misc/misc-fake89999",
1069 exp_links => ["node"],
1070 exp_majorminor => "4095:89999",
1071 }],
1072 rules => <<EOF
1073 KERNEL=="misc-fake89999", SYMLINK+="node"
1074 EOF
1075 },
1076 {
1077 desc => "multiple symlinks with format char",
1078 devices => [
1079 {
1080 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1081 exp_links => ["symlink1-0", "symlink2-ttyACM0", "symlink3-"],
1082 }],
1083 rules => <<EOF
1084 KERNEL=="ttyACM[0-9]*", SYMLINK="symlink1-%n symlink2-%k symlink3-%b"
1085 EOF
1086 },
1087 {
1088 desc => "multiple symlinks with a lot of s p a c e s",
1089 devices => [
1090 {
1091 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1092 exp_links => ["one", "two"],
1093 not_exp_links => [" "],
1094 }],
1095 rules => <<EOF
1096 KERNEL=="ttyACM[0-9]*", SYMLINK=" one two "
1097 EOF
1098 },
1099 {
1100 desc => "symlink with spaces in substituted variable",
1101 devices => [
1102 {
1103 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1104 exp_links => ["name-one_two_three-end"],
1105 not_exp_links => [" "],
1106 }],
1107 rules => <<EOF
1108 ENV{WITH_WS}="one two three"
1109 SYMLINK="name-\$env{WITH_WS}-end"
1110 EOF
1111 },
1112 {
1113 desc => "symlink with leading space in substituted variable",
1114 devices => [
1115 {
1116 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1117 exp_links => ["name-one_two_three-end"],
1118 not_exp_links => [" "],
1119 }],
1120 rules => <<EOF
1121 ENV{WITH_WS}=" one two three"
1122 SYMLINK="name-\$env{WITH_WS}-end"
1123 EOF
1124 },
1125 {
1126 desc => "symlink with trailing space in substituted variable",
1127 devices => [
1128 {
1129 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1130 exp_links => ["name-one_two_three-end"],
1131 not_exp_links => [" "],
1132 }],
1133 rules => <<EOF
1134 ENV{WITH_WS}="one two three "
1135 SYMLINK="name-\$env{WITH_WS}-end"
1136 EOF
1137 },
1138 {
1139 desc => "symlink with lots of space in substituted variable",
1140 devices => [
1141 {
1142 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1143 exp_links => ["name-one_two_three-end"],
1144 not_exp_links => [" "],
1145 }],
1146 rules => <<EOF
1147 ENV{WITH_WS}=" one two three "
1148 SYMLINK="name-\$env{WITH_WS}-end"
1149 EOF
1150 },
1151 {
1152 desc => "symlink with multiple spaces in substituted variable",
1153 devices => [
1154 {
1155 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1156 exp_links => ["name-one_two_three-end"],
1157 not_exp_links => [" "],
1158 }],
1159 rules => <<EOF
1160 ENV{WITH_WS}=" one two three "
1161 SYMLINK="name-\$env{WITH_WS}-end"
1162 EOF
1163 },
1164 {
1165 desc => "symlink with space and var with space",
1166 devices => [
1167 {
1168 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1169 exp_links => ["first", "name-one_two_three-end",
1170 "another_symlink", "a", "b", "c"],
1171 not_exp_links => [" "],
1172 }],
1173 rules => <<EOF
1174 ENV{WITH_WS}=" one two three "
1175 SYMLINK=" first name-\$env{WITH_WS}-end another_symlink a b c "
1176 EOF
1177 },
1178 {
1179 desc => "symlink with env which contain slash (see #19309)",
1180 devices => [
1181 {
1182 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1183 exp_links => ["first", "name-aaa_bbb_ccc-end",
1184 "another_symlink", "a", "b", "c"],
1185 not_exp_links => ["ame-aaa/bbb/ccc-end"],
1186 }],
1187 rules => <<EOF
1188 ENV{WITH_SLASH}="aaa/bbb/ccc"
1189 OPTIONS="string_escape=replace", ENV{REPLACED}="\$env{WITH_SLASH}"
1190 SYMLINK=" first name-\$env{REPLACED}-end another_symlink a b c "
1191 EOF
1192 },
1193 {
1194 desc => "symlink creation (same directory)",
1195 devices => [
1196 {
1197 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1198 exp_links => ["modem0"],
1199 }],
1200 rules => <<EOF
1201 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK="modem%n"
1202 EOF
1203 },
1204 {
1205 desc => "multiple symlinks",
1206 devices => [
1207 {
1208 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1209 exp_links => ["first-0", "second-0", "third-0"],
1210 }],
1211 rules => <<EOF
1212 KERNEL=="ttyACM0", SYMLINK="first-%n second-%n third-%n"
1213 EOF
1214 },
1215 {
1216 desc => "symlink name '.'",
1217 devices => [
1218 {
1219 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1220 exp_links => ["."],
1221 exp_add_error => "yes",
1222 exp_rem_error => "yes",
1223 }],
1224 rules => <<EOF
1225 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="."
1226 EOF
1227 },
1228 {
1229 desc => "symlink node to itself",
1230 devices => [
1231 {
1232 devpath => "/devices/virtual/tty/tty0",
1233 exp_links => ["link"],
1234 exp_add_error => "yes",
1235 exp_rem_error => "yes",
1236 }],
1237 option => "clean",
1238 rules => <<EOF
1239 KERNEL=="tty0", SYMLINK+="tty0"
1240 EOF
1241 },
1242 {
1243 desc => "symlink %n substitution",
1244 devices => [
1245 {
1246 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1247 exp_links => ["symlink0"],
1248 }],
1249 rules => <<EOF
1250 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="symlink%n"
1251 EOF
1252 },
1253 {
1254 desc => "symlink %k substitution",
1255 devices => [
1256 {
1257 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1258 exp_links => ["symlink-ttyACM0"],
1259 }],
1260 rules => <<EOF
1261 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="symlink-%k"
1262 EOF
1263 },
1264 {
1265 desc => "symlink %M:%m substitution",
1266 devices => [
1267 {
1268 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1269 exp_links => ["major-166:0"],
1270 }],
1271 rules => <<EOF
1272 KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="major-%M:%m"
1273 EOF
1274 },
1275 {
1276 desc => "symlink %b substitution",
1277 devices => [
1278 {
1279 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1280 exp_links => ["symlink-0:0:0:0"],
1281 }],
1282 rules => <<EOF
1283 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="symlink-%b"
1284 EOF
1285 },
1286 {
1287 desc => "symlink %c substitution",
1288 devices => [
1289 {
1290 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1291 exp_links => ["test"],
1292 }],
1293 rules => <<EOF
1294 KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo test", SYMLINK+="%c"
1295 EOF
1296 },
1297 {
1298 desc => "symlink %c{N} substitution",
1299 devices => [
1300 {
1301 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1302 exp_links => ["test"],
1303 not_exp_links => ["symlink", "this"],
1304 }],
1305 rules => <<EOF
1306 KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo symlink test this", SYMLINK+="%c{2}"
1307 EOF
1308 },
1309 {
1310 desc => "symlink %c{N+} substitution",
1311 devices => [
1312 {
1313 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1314 exp_links => ["test", "this"],
1315 not_exp_links => ["symlink"],
1316 }],
1317 rules => <<EOF
1318 KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo symlink test this", SYMLINK+="%c{2+}"
1319 EOF
1320 },
1321 {
1322 desc => "symlink only rule with %c{N+}",
1323 devices => [
1324 {
1325 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1326 exp_links => ["test", "this"],
1327 not_exp_links => ["symlink"],
1328 }],
1329 rules => <<EOF
1330 SUBSYSTEMS=="scsi", KERNEL=="sda", PROGRAM=="/bin/echo link test this" SYMLINK+="%c{2+}"
1331 EOF
1332 },
1333 {
1334 desc => "symlink %s{filename} substitution",
1335 devices => [
1336 {
1337 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1338 exp_links => ["166:0"],
1339 }],
1340 rules => <<EOF
1341 KERNEL=="ttyACM[0-9]*", SYMLINK+="%s{dev}"
1342 EOF
1343 },
1344 {
1345 desc => "program result substitution (numbered part of)",
1346 devices => [
1347 {
1348 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
1349 exp_links => ["link1", "link2"],
1350 not_exp_links => ["node"],
1351 }],
1352 rules => <<EOF
1353 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n node link1 link2", RESULT=="node *", SYMLINK+="%c{2} %c{3}"
1354 EOF
1355 },
1356 {
1357 desc => "program result substitution (numbered part of+)",
1358 devices => [
1359 {
1360 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
1361 exp_links => ["link1", "link2", "link3", "link4"],
1362 not_exp_links => ["node"],
1363 }],
1364 rules => <<EOF
1365 SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n node link1 link2 link3 link4", RESULT=="node *", SYMLINK+="%c{2+}"
1366 EOF
1367 },
1368 {
1369 desc => "SUBSYSTEM match test",
1370 devices => [
1371 {
1372 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1373 exp_links => ["node"],
1374 not_exp_links => ["should_not_match", "should_not_match2"],
1375 }],
1376 rules => <<EOF
1377 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match", SUBSYSTEM=="vc"
1378 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", SUBSYSTEM=="block"
1379 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match2", SUBSYSTEM=="vc"
1380 EOF
1381 },
1382 {
1383 desc => "DRIVERS match test",
1384 devices => [
1385 {
1386 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1387 exp_links => ["node"],
1388 not_exp_links => ["should_not_match"]
1389 }],
1390 rules => <<EOF
1391 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match", DRIVERS=="sd-wrong"
1392 SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", DRIVERS=="sd"
1393 EOF
1394 },
1395 {
1396 desc => "devnode substitution test",
1397 devices => [
1398 {
1399 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1400 exp_links => ["node"],
1401 }],
1402 rules => <<EOF
1403 SUBSYSTEMS=="scsi", KERNEL=="sda", PROGRAM=="/usr/bin/test -b %N" SYMLINK+="node"
1404 EOF
1405 },
1406 {
1407 desc => "parent node name substitution test",
1408 devices => [
1409 {
1410 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1411 exp_links => ["sda-part-1"],
1412 }],
1413 rules => <<EOF
1414 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="%P-part-%n"
1415 EOF
1416 },
1417 {
1418 desc => "udev_root substitution",
1419 devices => [
1420 {
1421 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1422 exp_links => ["start-/dev-end"],
1423 }],
1424 rules => <<EOF
1425 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="start-%r-end"
1426 EOF
1427 },
1428 {
1429 # This is not supported any more
1430 desc => "last_rule option",
1431 devices => [
1432 {
1433 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1434 exp_links => ["last"],
1435 not_exp_links => ["very-last"],
1436 exp_nodev_error => "yes",
1437 }],
1438 rules => <<EOF
1439 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="last", OPTIONS="last_rule"
1440 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="very-last"
1441 EOF
1442 },
1443 {
1444 desc => "negation KERNEL!=",
1445 devices => [
1446 {
1447 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1448 exp_links => ["match", "before"],
1449 not_exp_links => ["matches-but-is-negated"],
1450 }],
1451 rules => <<EOF
1452 SUBSYSTEMS=="scsi", KERNEL!="sda1", SYMLINK+="matches-but-is-negated"
1453 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1454 SUBSYSTEMS=="scsi", KERNEL!="xsda1", SYMLINK+="match"
1455 EOF
1456 },
1457 {
1458 desc => "negation SUBSYSTEM!=",
1459 devices => [
1460 {
1461 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1462 exp_links => ["before", "not-anything"],
1463 not_exp_links => ["matches-but-is-negated"],
1464 }],
1465 rules => <<EOF
1466 SUBSYSTEMS=="scsi", SUBSYSTEM=="block", KERNEL!="sda1", SYMLINK+="matches-but-is-negated"
1467 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1468 SUBSYSTEMS=="scsi", SUBSYSTEM!="anything", SYMLINK+="not-anything"
1469 EOF
1470 },
1471 {
1472 desc => "negation PROGRAM!= exit code",
1473 devices => [
1474 {
1475 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1476 exp_links => ["before", "nonzero-program"],
1477 }],
1478 rules => <<EOF
1479 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1480 KERNEL=="sda1", PROGRAM!="/bin/false", SYMLINK+="nonzero-program"
1481 EOF
1482 },
1483 {
1484 desc => "ENV{} test",
1485 devices => [
1486 {
1487 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1488 exp_links => ["true"],
1489 not_exp_links => ["bad", "wrong"],
1490 }],
1491 rules => <<EOF
1492 ENV{ENV_KEY_TEST}="test"
1493 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="go", SYMLINK+="wrong"
1494 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="test", SYMLINK+="true"
1495 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="bad", SYMLINK+="bad"
1496 EOF
1497 },
1498 {
1499 desc => "ENV{} test",
1500 devices => [
1501 {
1502 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1503 exp_links => ["true"],
1504 not_exp_links => ["bad", "wrong", "no"],
1505 }],
1506 rules => <<EOF
1507 ENV{ENV_KEY_TEST}="test"
1508 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="go", SYMLINK+="wrong"
1509 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="yes", ENV{ACTION}=="add", ENV{DEVPATH}=="*/block/sda/sdax1", SYMLINK+="no"
1510 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="test", ENV{ACTION}=="add", ENV{DEVPATH}=="*/block/sda/sda1", SYMLINK+="true"
1511 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="bad", SYMLINK+="bad"
1512 EOF
1513 },
1514 {
1515 desc => "ENV{} test (assign)",
1516 devices => [
1517 {
1518 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1519 exp_links => ["true", "before"],
1520 not_exp_links => ["no"],
1521 }],
1522 rules => <<EOF
1523 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="true"
1524 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="yes", SYMLINK+="no"
1525 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1526 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="true", SYMLINK+="true"
1527 EOF
1528 },
1529 {
1530 desc => "ENV{} test (assign 2 times)",
1531 devices => [
1532 {
1533 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1534 exp_links => ["true", "before"],
1535 not_exp_links => ["no", "bad"],
1536 }],
1537 rules => <<EOF
1538 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="true"
1539 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="absolutely-\$env{ASSIGN}"
1540 SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1541 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="yes", SYMLINK+="no"
1542 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="true", SYMLINK+="bad"
1543 SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="absolutely-true", SYMLINK+="true"
1544 EOF
1545 },
1546 {
1547 desc => "ENV{} test (assign2)",
1548 devices => [
1549 {
1550 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1551 exp_links => ["part"],
1552 not_exp_links => ["disk"],
1553 },
1554 {
1555 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1556 exp_links => ["disk"],
1557 not_exp_links => ["part"],
1558 },
1559 ],
1560 rules => <<EOF
1561 SUBSYSTEM=="block", KERNEL=="*[0-9]", ENV{PARTITION}="true", ENV{MAINDEVICE}="false"
1562 SUBSYSTEM=="block", KERNEL=="*[!0-9]", ENV{PARTITION}="false", ENV{MAINDEVICE}="true"
1563 ENV{MAINDEVICE}=="true", SYMLINK+="disk"
1564 SUBSYSTEM=="block", SYMLINK+="before"
1565 ENV{PARTITION}=="true", SYMLINK+="part"
1566 EOF
1567 },
1568 {
1569 desc => "untrusted string sanitize",
1570 devices => [
1571 {
1572 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1573 exp_links => ["sane"],
1574 }],
1575 rules => <<EOF
1576 SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e name; (/usr/bin/badprogram)", RESULT=="name_ _/usr/bin/badprogram_", SYMLINK+="sane"
1577 EOF
1578 },
1579 {
1580 desc => "untrusted string sanitize (don't replace utf8)",
1581 devices => [
1582 {
1583 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1584 exp_links => ["uber"],
1585 }],
1586 rules => <<EOF
1587 SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e \\xc3\\xbcber" RESULT=="\xc3\xbcber", SYMLINK+="uber"
1588 EOF
1589 },
1590 {
1591 desc => "untrusted string sanitize (replace invalid utf8)",
1592 devices => [
1593 {
1594 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1595 exp_links => ["replaced"],
1596 }],
1597 rules => <<EOF
1598 SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e \\xef\\xe8garbage", RESULT=="__garbage", SYMLINK+="replaced"
1599 EOF
1600 },
1601 {
1602 desc => "read sysfs value from parent device",
1603 devices => [
1604 {
1605 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1606 exp_links => ["serial-354172020305000"],
1607 }],
1608 rules => <<EOF
1609 KERNEL=="ttyACM*", ATTRS{serial}=="?*", SYMLINK+="serial-%s{serial}"
1610 EOF
1611 },
1612 {
1613 desc => "match against empty key string",
1614 devices => [
1615 {
1616 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1617 exp_links => ["ok"],
1618 not_exp_links => ["not-1-ok", "not-2-ok", "not-3-ok"],
1619 }],
1620 rules => <<EOF
1621 KERNEL=="sda", ATTRS{nothing}!="", SYMLINK+="not-1-ok"
1622 KERNEL=="sda", ATTRS{nothing}=="", SYMLINK+="not-2-ok"
1623 KERNEL=="sda", ATTRS{vendor}!="", SYMLINK+="ok"
1624 KERNEL=="sda", ATTRS{vendor}=="", SYMLINK+="not-3-ok"
1625 EOF
1626 },
1627 {
1628 desc => "check ACTION value",
1629 devices => [
1630 {
1631 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1632 exp_links => ["ok"],
1633 not_exp_links => ["unknown-not-ok"],
1634 }],
1635 rules => <<EOF
1636 ACTION=="unknown", KERNEL=="sda", SYMLINK+="unknown-not-ok"
1637 ACTION=="add", KERNEL=="sda", SYMLINK+="ok"
1638 EOF
1639 },
1640 {
1641 desc => "final assignment",
1642 devices => [
1643 {
1644 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1645 exp_links => ["ok"],
1646 exp_perms => "root:tty:0640",
1647 }],
1648 rules => <<EOF
1649 KERNEL=="sda", GROUP:="tty"
1650 KERNEL=="sda", GROUP="root", MODE="0640", SYMLINK+="ok"
1651 EOF
1652 },
1653 {
1654 desc => "final assignment 2",
1655 devices => [
1656 {
1657 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1658 exp_links => ["ok"],
1659 exp_perms => "root:tty:0640",
1660 }],
1661 rules => <<EOF
1662 KERNEL=="sda", GROUP:="tty"
1663 SUBSYSTEM=="block", MODE:="640"
1664 KERNEL=="sda", GROUP="root", MODE="0666", SYMLINK+="ok"
1665 EOF
1666 },
1667 {
1668 desc => "env substitution",
1669 devices => [
1670 {
1671 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1672 exp_links => ["node-add-me"],
1673 }],
1674 rules => <<EOF
1675 KERNEL=="sda", MODE="0666", SYMLINK+="node-\$env{ACTION}-me"
1676 EOF
1677 },
1678 {
1679 desc => "reset list to current value",
1680 devices => [
1681 {
1682 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1683 exp_links => ["three"],
1684 not_exp_links => ["two", "one"],
1685 }],
1686 rules => <<EOF
1687 KERNEL=="ttyACM[0-9]*", SYMLINK+="one"
1688 KERNEL=="ttyACM[0-9]*", SYMLINK+="two"
1689 KERNEL=="ttyACM[0-9]*", SYMLINK="three"
1690 EOF
1691 },
1692 {
1693 desc => "test empty SYMLINK+ (empty override)",
1694 devices => [
1695 {
1696 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1697 exp_links => ["right"],
1698 not_exp_links => ["wrong"],
1699 }],
1700 rules => <<EOF
1701 KERNEL=="ttyACM[0-9]*", SYMLINK+="wrong"
1702 KERNEL=="ttyACM[0-9]*", SYMLINK=""
1703 KERNEL=="ttyACM[0-9]*", SYMLINK+="right"
1704 EOF
1705 },
1706 {
1707 desc => "test multi matches",
1708 devices => [
1709 {
1710 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1711 exp_links => ["right", "before"],
1712 }],
1713 rules => <<EOF
1714 KERNEL=="ttyACM*", SYMLINK+="before"
1715 KERNEL=="ttyACM*|nothing", SYMLINK+="right"
1716 EOF
1717 },
1718 {
1719 desc => "test multi matches 2",
1720 devices => [
1721 {
1722 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1723 exp_links => ["right", "before"],
1724 not_exp_links => ["nomatch"],
1725 }],
1726 rules => <<EOF
1727 KERNEL=="dontknow*|*nothing", SYMLINK+="nomatch"
1728 KERNEL=="ttyACM*", SYMLINK+="before"
1729 KERNEL=="dontknow*|ttyACM*|nothing*", SYMLINK+="right"
1730 EOF
1731 },
1732 {
1733 desc => "test multi matches 3",
1734 devices => [
1735 {
1736 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1737 exp_links => ["right"],
1738 not_exp_links => ["nomatch", "wrong1", "wrong2"],
1739 }],
1740 rules => <<EOF
1741 KERNEL=="dontknow|nothing", SYMLINK+="nomatch"
1742 KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1"
1743 KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2"
1744 KERNEL=="dontknow|ttyACM0|nothing", SYMLINK+="right"
1745 EOF
1746 },
1747 {
1748 desc => "test multi matches 4",
1749 devices => [
1750 {
1751 devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
1752 exp_links => ["right"],
1753 not_exp_links => ["nomatch", "wrong1", "wrong2", "wrong3"],
1754 }],
1755 rules => <<EOF
1756 KERNEL=="dontknow|nothing", SYMLINK+="nomatch"
1757 KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1"
1758 KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2"
1759 KERNEL=="all|dontknow|ttyACM0", SYMLINK+="right"
1760 KERNEL=="ttyACM0a|nothing", SYMLINK+="wrong3"
1761 EOF
1762 },
1763 {
1764 desc => "test multi matches 5",
1765 devices => [
1766 {
1767 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1768 exp_links => ["found"],
1769 not_exp_name => "bad",
1770 }],
1771 rules => <<EOF
1772 KERNEL=="sda", TAG="foo"
1773 TAGS=="|foo", SYMLINK+="found"
1774 TAGS=="|aaa", SYMLINK+="bad"
1775 EOF
1776 },
1777 {
1778 desc => "test multi matches 6",
1779 devices => [
1780 {
1781 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1782 exp_links => ["found"],
1783 not_exp_name => "bad",
1784 }],
1785 rules => <<EOF
1786 KERNEL=="sda", TAG=""
1787 TAGS=="|foo", SYMLINK+="found"
1788 TAGS=="aaa|bbb", SYMLINK+="bad"
1789 EOF
1790 },
1791 {
1792 desc => "test multi matches 7",
1793 devices => [
1794 {
1795 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1796 exp_links => ["found"],
1797 not_exp_name => "bad",
1798 }],
1799 rules => <<EOF
1800 KERNEL=="sda", TAG="foo"
1801 TAGS=="foo||bar", SYMLINK+="found"
1802 TAGS=="aaa||bbb", SYMLINK+="bad"
1803 EOF
1804 },
1805 {
1806 desc => "test multi matches 8",
1807 devices => [
1808 {
1809 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1810 exp_links => ["found"],
1811 not_exp_name => "bad",
1812 }],
1813 rules => <<EOF
1814 KERNEL=="sda", TAG=""
1815 TAGS=="foo||bar", SYMLINK+="found"
1816 TAGS=="aaa|bbb", SYMLINK+="bad"
1817 EOF
1818 },
1819 {
1820 desc => "test multi matches 9",
1821 devices => [
1822 {
1823 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1824 exp_links => ["found"],
1825 not_exp_name => "bad",
1826 }],
1827 rules => <<EOF
1828 KERNEL=="sda", TAG="foo"
1829 TAGS=="foo|", SYMLINK+="found"
1830 TAGS=="aaa|", SYMLINK+="bad"
1831 EOF
1832 },
1833 {
1834 desc => "test multi matches 10",
1835 devices => [
1836 {
1837 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1838 exp_links => ["found"],
1839 not_exp_name => "bad",
1840 }],
1841 rules => <<EOF
1842 KERNEL=="sda", TAG=""
1843 TAGS=="foo|", SYMLINK+="found"
1844 TAGS=="aaa|bbb", SYMLINK+="bad"
1845 EOF
1846 },
1847 {
1848 desc => "test multi matches 11",
1849 devices => [
1850 {
1851 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1852 exp_links => ["found"],
1853 not_exp_name => "bad",
1854 }],
1855 rules => <<EOF
1856 KERNEL=="sda", TAG="c"
1857 TAGS=="foo||bar||c", SYMLINK+="found"
1858 TAGS=="aaa||bbb||ccc", SYMLINK+="bad"
1859 EOF
1860 },
1861 {
1862 desc => "IMPORT parent test",
1863 devices => [
1864 {
1865 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1866 exp_links => ["parent"],
1867 },
1868 {
1869 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1870 exp_links => ["parentenv-parent_right"],
1871 }],
1872 sleep_us => 500000, # Serialized! We need to sleep here after adding sda
1873 rules => <<EOF
1874 KERNEL=="sda1", IMPORT{parent}="PARENT*", SYMLINK+="parentenv-\$env{PARENT_KEY}\$env{WRONG_PARENT_KEY}"
1875 KERNEL=="sda", IMPORT{program}="/bin/echo -e \'PARENT_KEY=parent_right\\nWRONG_PARENT_KEY=parent_wrong'"
1876 KERNEL=="sda", SYMLINK+="parent"
1877 EOF
1878 },
1879 {
1880 desc => "GOTO test",
1881 devices => [
1882 {
1883 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1884 exp_links => ["right"],
1885 not_exp_test => ["wrong", "wrong2"],
1886 }],
1887 rules => <<EOF
1888 KERNEL=="sda1", GOTO="TEST"
1889 KERNEL=="sda1", SYMLINK+="wrong"
1890 KERNEL=="sda1", GOTO="BAD"
1891 KERNEL=="sda1", SYMLINK+="", LABEL="NO"
1892 KERNEL=="sda1", SYMLINK+="right", LABEL="TEST", GOTO="end"
1893 KERNEL=="sda1", SYMLINK+="wrong2", LABEL="BAD"
1894 LABEL="end"
1895 EOF
1896 },
1897 {
1898 desc => "GOTO label does not exist",
1899 devices => [
1900 {
1901 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1902 exp_links => ["right"],
1903 }],
1904 rules => <<EOF
1905 KERNEL=="sda1", GOTO="does-not-exist"
1906 KERNEL=="sda1", SYMLINK+="right",
1907 LABEL="exists"
1908 EOF
1909 },
1910 {
1911 desc => "SYMLINK+ compare test",
1912 devices => [
1913 {
1914 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1915 exp_links => ["right", "link"],
1916 not_exp_links => ["wrong"],
1917 }],
1918 rules => <<EOF
1919 KERNEL=="sda1", SYMLINK+="link"
1920 KERNEL=="sda1", SYMLINK=="link*", SYMLINK+="right"
1921 KERNEL=="sda1", SYMLINK=="nolink*", SYMLINK+="wrong"
1922 EOF
1923 },
1924 {
1925 desc => "invalid key operation",
1926 devices => [
1927 {
1928 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1929 exp_links => ["yes"],
1930 not_exp_links => ["no"],
1931 }],
1932 rules => <<EOF
1933 KERNEL="sda1", SYMLINK+="no"
1934 KERNEL=="sda1", SYMLINK+="yes"
1935 EOF
1936 },
1937 {
1938 desc => "operator chars in attribute",
1939 devices => [
1940 {
1941 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1942 exp_links => ["yes"],
1943 }],
1944 rules => <<EOF
1945 KERNEL=="sda", ATTR{test:colon+plus}=="?*", SYMLINK+="yes"
1946 EOF
1947 },
1948 {
1949 desc => "overlong comment line",
1950 devices => [
1951 {
1952 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
1953 exp_links => ["yes"],
1954 not_exp_links => ["no"],
1955 }],
1956 rules => <<EOF
1957 # 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
1958 # 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
1959 KERNEL=="sda1", SYMLINK+=="no"
1960 KERNEL=="sda1", SYMLINK+="yes"
1961 EOF
1962 },
1963 {
1964 desc => "magic subsys/kernel lookup",
1965 devices => [
1966 {
1967 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1968 exp_links => ["00:16:41:e2:8d:ff"],
1969 }],
1970 rules => <<EOF
1971 KERNEL=="sda", SYMLINK+="\$attr{[net/eth0]address}"
1972 EOF
1973 },
1974 {
1975 desc => "TEST absolute path",
1976 devices => [
1977 {
1978 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1979 exp_links => ["there"],
1980 not_exp_links => ["notthere"],
1981 }],
1982 rules => <<EOF
1983 TEST=="/etc/passwd", SYMLINK+="there"
1984 TEST!="/etc/passwd", SYMLINK+="notthere"
1985 EOF
1986 },
1987 {
1988 desc => "TEST subsys/kernel lookup",
1989 devices => [
1990 {
1991 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
1992 exp_links => ["yes"],
1993 }],
1994 rules => <<EOF
1995 KERNEL=="sda", TEST=="[net/eth0]", SYMLINK+="yes"
1996 EOF
1997 },
1998 {
1999 desc => "TEST relative path",
2000 devices => [
2001 {
2002 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2003 exp_links => ["relative"],
2004 }],
2005 rules => <<EOF
2006 KERNEL=="sda", TEST=="size", SYMLINK+="relative"
2007 EOF
2008 },
2009 {
2010 desc => "TEST wildcard substitution (find queue/nr_requests)",
2011 devices => [
2012 {
2013 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2014 exp_links => ["found-subdir"],
2015 }],
2016 rules => <<EOF
2017 KERNEL=="sda", TEST=="*/nr_requests", SYMLINK+="found-subdir"
2018 EOF
2019 },
2020 {
2021 desc => "TEST MODE=0000",
2022 devices => [
2023 {
2024 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2025 exp_perms => "0:0:0000",
2026 exp_rem_error => "yes",
2027 }],
2028 rules => <<EOF
2029 KERNEL=="sda", MODE="0000"
2030 EOF
2031 },
2032 {
2033 desc => "TEST PROGRAM feeds OWNER, GROUP, MODE",
2034 devices => [
2035 {
2036 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2037 exp_perms => "1:1:0400",
2038 }],
2039 rules => <<EOF
2040 KERNEL=="sda", MODE="666"
2041 KERNEL=="sda", PROGRAM=="/bin/echo 1 1 0400", OWNER="%c{1}", GROUP="%c{2}", MODE="%c{3}"
2042 EOF
2043 },
2044 {
2045 desc => "TEST PROGRAM feeds MODE with overflow",
2046 devices => [
2047 {
2048 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2049 exp_perms => "0:0:0440",
2050 exp_rem_error => "yes",
2051 }],
2052 rules => <<EOF
2053 KERNEL=="sda", MODE="440"
2054 KERNEL=="sda", PROGRAM=="/bin/echo 0 0 0400letsdoabuffferoverflow0123456789012345789012345678901234567890", OWNER="%c{1}", GROUP="%c{2}", MODE="%c{3}"
2055 EOF
2056 },
2057 {
2058 desc => "magic [subsys/sysname] attribute substitution",
2059 devices => [
2060 {
2061 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2062 exp_links => ["sda-8741C4G-end"],
2063 exp_perms => "0:0:0600",
2064 }],
2065 rules => <<EOF
2066 KERNEL=="sda", SYMLINK+="%k-%s{[dmi/id]product_name}-end"
2067 EOF
2068 },
2069 {
2070 desc => "builtin path_id",
2071 devices => [
2072 {
2073 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2074 exp_links => ["disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0"],
2075 }],
2076 rules => <<EOF
2077 KERNEL=="sda", IMPORT{builtin}="path_id"
2078 KERNEL=="sda", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/\$env{ID_PATH}"
2079 EOF
2080 },
2081 {
2082 desc => "add and match tag",
2083 devices => [
2084 {
2085 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2086 exp_links => ["found"],
2087 not_exp_links => ["bad"],
2088 }],
2089 rules => <<EOF
2090 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", TAG+="green"
2091 TAGS=="green", SYMLINK+="found"
2092 TAGS=="blue", SYMLINK+="bad"
2093 EOF
2094 },
2095 {
2096 desc => "don't crash with lots of tags",
2097 devices => [
2098 {
2099 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2100 exp_links => ["found"],
2101 }],
2102 rules => $rules_10k_tags . <<EOF
2103 TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="found"
2104 EOF
2105 },
2106 {
2107 desc => "continuations",
2108 devices => [
2109 {
2110 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2111 exp_links => ["found"],
2112 not_exp_name => "bad",
2113 }],
2114 rules => $rules_10k_tags_continuation . <<EOF
2115 TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="bad"
2116 KERNEL=="sda",\\
2117 # comment in continuation
2118 TAG+="hoge1",\\
2119 # space before comment
2120 TAG+="hoge2",\\
2121 # spaces before and after token are dropped
2122 TAG+="hoge3", \\
2123 \\
2124 \\
2125 TAG+="hoge4"
2126 TAGS=="hoge1", TAGS=="hoge2", TAGS=="hoge3", TAGS=="hoge4", SYMLINK+="found"
2127 EOF
2128 },
2129 {
2130 desc => "continuations with empty line",
2131 devices => [
2132 {
2133 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2134 exp_links => ["found"],
2135 not_exp_name => "bad",
2136
2137 }],
2138 rules => <<EOF
2139 # empty line finishes continuation
2140 KERNEL=="sda", TAG+="foo" \\
2141
2142 KERNEL=="sdb", TAG+="hoge"
2143 KERNEL=="sda", TAG+="aaa" \\
2144 KERNEL=="sdb", TAG+="bbb"
2145 TAGS=="foo", SYMLINK+="found"
2146 TAGS=="aaa", SYMLINK+="bad"
2147 EOF
2148 },
2149 {
2150 desc => "continuations with white only line",
2151 devices => [
2152 {
2153 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
2154 exp_links => ["found"],
2155 not_exp_name => "bad",
2156 }],
2157 rules => <<EOF
2158 # space only line finishes continuation
2159 KERNEL=="sda", TAG+="foo" \\
2160 \t
2161 KERNEL=="sdb", TAG+="hoge"
2162 KERNEL=="sda", TAG+="aaa" \\
2163 KERNEL=="sdb", TAG+="bbb"
2164 TAGS=="foo", SYMLINK+="found"
2165 TAGS=="aaa", SYMLINK+="bad"
2166 EOF
2167 },
2168 {
2169 desc => "multiple devices",
2170 devices => [
2171 {
2172 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
2173 exp_links => ["part-1"],
2174 },
2175 {
2176 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
2177 exp_links => ["part-5"],
2178 },
2179 {
2180 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6",
2181 exp_links => ["part-6"],
2182 },
2183 {
2184 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7",
2185 exp_links => ["part-7"],
2186 },
2187 {
2188 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8",
2189 exp_links => ["part-8"],
2190 },
2191 {
2192 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9",
2193 exp_links => ["part-9"],
2194 },
2195 {
2196 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10",
2197 exp_links => ["part-10"],
2198 },
2199 ],
2200 rules => <<EOF
2201 SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n"
2202 EOF
2203 },
2204 {
2205 desc => "multiple devices, same link name, positive prio",
2206 repeat => 100,
2207 devices => [
2208 {
2209 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
2210 exp_links => ["part-1"],
2211 not_exp_links => ["partition"],
2212 },
2213 {
2214 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
2215 exp_links => ["part-5"],
2216 not_exp_links => ["partition"],
2217 },
2218 {
2219 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6",
2220 not_exp_links => ["partition"],
2221 exp_links => ["part-6"],
2222 },
2223 {
2224 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7",
2225 exp_links => ["part-7", "partition"],
2226 },
2227 {
2228 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8",
2229 not_exp_links => ["partition"],
2230 exp_links => ["part-8"],
2231 },
2232 {
2233 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9",
2234 not_exp_links => ["partition"],
2235 exp_links => ["part-9"],
2236 },
2237 {
2238 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10",
2239 not_exp_links => ["partition"],
2240 exp_links => ["part-10"],
2241 },
2242 ],
2243 rules => <<EOF
2244 SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n"
2245 SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition"
2246 KERNEL=="*7", OPTIONS+="link_priority=10"
2247 EOF
2248 },
2249 {
2250 desc => "multiple devices, same link name, negative prio",
2251 devices => [
2252 {
2253 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
2254 exp_links => ["part-1"],
2255 not_exp_links => ["partition"],
2256 },
2257 {
2258 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
2259 exp_links => ["part-5"],
2260 not_exp_links => ["partition"],
2261 },
2262 {
2263 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6",
2264 not_exp_links => ["partition"],
2265 exp_links => ["part-6"],
2266 },
2267 {
2268 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7",
2269 exp_links => ["part-7", "partition"],
2270 },
2271 {
2272 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8",
2273 not_exp_links => ["partition"],
2274 exp_links => ["part-8"],
2275 },
2276 {
2277 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9",
2278 not_exp_links => ["partition"],
2279 exp_links => ["part-9"],
2280 },
2281 {
2282 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10",
2283 not_exp_links => ["partition"],
2284 exp_links => ["part-10"],
2285 },
2286 ],
2287 rules => <<EOF
2288 SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n"
2289 SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition"
2290 KERNEL!="*7", OPTIONS+="link_priority=-10"
2291 EOF
2292 },
2293 {
2294 desc => "multiple devices, same link name, positive prio, sleep",
2295 devices => [
2296 {
2297 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
2298 exp_links => ["part-1"],
2299 not_exp_links => ["partition"],
2300 },
2301 {
2302 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
2303 exp_links => ["part-5"],
2304 not_exp_links => ["partition"],
2305 },
2306 {
2307 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6",
2308 not_exp_links => ["partition"],
2309 exp_links => ["part-6"],
2310 },
2311 {
2312 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7",
2313 exp_links => ["part-7", "partition"],
2314 },
2315 {
2316 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8",
2317 not_exp_links => ["partition"],
2318 exp_links => ["part-8"],
2319 },
2320 {
2321 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9",
2322 not_exp_links => ["partition"],
2323 exp_links => ["part-9"],
2324 },
2325 {
2326 devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10",
2327 not_exp_links => ["partition"],
2328 exp_links => ["part-10"],
2329 },
2330 ],
2331 sleep_us => 10000,
2332 rules => <<EOF
2333 SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n"
2334 SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition"
2335 KERNEL=="*7", OPTIONS+="link_priority=10"
2336 EOF
2337 },
2338 {
2339 desc => 'all_block_devs',
2340 generator => expect_for_some("\\/sda6\$", ["blockdev"]),
2341 repeat => 10,
2342 rules => <<EOF
2343 SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sd*", SYMLINK+="blockdev"
2344 KERNEL=="sda6", OPTIONS+="link_priority=10"
2345 EOF
2346 }
2347 );
2348
2349 sub create_rules {
2350 my ($rules) = @_;
2351
2352 # create temporary rules
2353 system("mkdir", "-p", "$udev_rules_dir");
2354 open CONF, ">$udev_rules" || die "unable to create rules file: $udev_rules";
2355 print CONF $$rules;
2356 close CONF;
2357 }
2358
2359 sub udev {
2360 my ($action, $devpath) = @_;
2361
2362 if ($valgrind > 0) {
2363 return system("$udev_bin_valgrind $action $devpath");
2364 } elsif ($gdb > 0) {
2365 return system("$udev_bin_gdb $action $devpath");
2366 } elsif ($strace > 0) {
2367 return system("$udev_bin_strace $action $devpath");
2368 } else {
2369 return system("$udev_bin", "$action", "$devpath");
2370 }
2371 }
2372
2373 my $error = 0;
2374 my $good = 0;
2375 my $exp_good = 0;
2376
2377 sub permissions_test {
2378 my($rules, $uid, $gid, $mode) = @_;
2379
2380 my $wrong = 0;
2381 my $userid;
2382 my $groupid;
2383
2384 $rules->{exp_perms} =~ m/^(.*):(.*):(.*)$/;
2385 if ($1 ne "") {
2386 if (defined(getpwnam($1))) {
2387 $userid = int(getpwnam($1));
2388 } else {
2389 $userid = $1;
2390 }
2391 if ($uid != $userid) { $wrong = 1; }
2392 }
2393 if ($2 ne "") {
2394 if (defined(getgrnam($2))) {
2395 $groupid = int(getgrnam($2));
2396 } else {
2397 $groupid = $2;
2398 }
2399 if ($gid != $groupid) { $wrong = 1; }
2400 }
2401 if ($3 ne "") {
2402 if (($mode & 07777) != oct($3)) { $wrong = 1; };
2403 }
2404 if ($wrong == 0) {
2405 print "permissions: ok\n";
2406 $good++;
2407 } else {
2408 printf " expected permissions are: %s:%s:%#o\n", $1, $2, oct($3);
2409 printf " created permissions are : %i:%i:%#o\n", $uid, $gid, $mode & 07777;
2410 print "permissions: error\n";
2411 $error++;
2412 sleep(1);
2413 }
2414 }
2415
2416 sub major_minor_test {
2417 my($rules, $rdev) = @_;
2418
2419 my $major = ($rdev >> 8) & 0xfff;
2420 my $minor = ($rdev & 0xff) | (($rdev >> 12) & 0xfff00);
2421 my $wrong = 0;
2422
2423 $rules->{exp_majorminor} =~ m/^(.*):(.*)$/;
2424 if ($1 ne "") {
2425 if ($major != $1) { $wrong = 1; };
2426 }
2427 if ($2 ne "") {
2428 if ($minor != $2) { $wrong = 1; };
2429 }
2430 if ($wrong == 0) {
2431 print "major:minor: ok\n";
2432 $good++;
2433 } else {
2434 printf " expected major:minor is: %i:%i\n", $1, $2;
2435 printf " created major:minor is : %i:%i\n", $major, $minor;
2436 print "major:minor: error\n";
2437 $error++;
2438 sleep(1);
2439 }
2440 }
2441
2442 sub udev_setup {
2443 system("umount \"$udev_tmpfs\" 2>/dev/null");
2444 rmdir($udev_tmpfs);
2445 mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n";
2446
2447 if (system("mount", "-o", "rw,mode=0755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) {
2448 warn "unable to mount tmpfs";
2449 return 0;
2450 }
2451
2452 mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n";
2453 # setting group and mode of udev_dev ensures the tests work
2454 # even if the parent directory has setgid bit enabled.
2455 chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n";
2456 chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n";
2457
2458 if (system("mknod", $udev_dev . "/null", "c", "1", "3")) {
2459 warn "unable to create $udev_dev/null";
2460 return 0;
2461 }
2462
2463 # check if we are permitted to create block device nodes
2464 my $block_device_filename = $udev_dev . "/sda";
2465 if (system("mknod", $block_device_filename, "b", "8", "0")) {
2466 warn "unable to create $block_device_filename";
2467 return 0;
2468 }
2469 unlink $block_device_filename;
2470
2471 system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys";
2472
2473 system("rm", "-rf", "$udev_run");
2474
2475 if (!mkdir($udev_run)) {
2476 warn "unable to create directory $udev_run";
2477 return 0;
2478 }
2479
2480 return 1;
2481 }
2482
2483 sub get_devnode {
2484 my ($device) = @_;
2485 my $devnode;
2486
2487 if (defined($device->{devnode})) {
2488 $devnode = "$udev_dev/$device->{devnode}";
2489 } else {
2490 $devnode = "$device->{devpath}";
2491 $devnode =~ s!.*/!$udev_dev/!;
2492 }
2493 return $devnode;
2494 }
2495
2496 sub check_devnode {
2497 my ($device) = @_;
2498 my $devnode = get_devnode($device);
2499
2500 my @st = lstat("$devnode");
2501 if (! (-b _ || -c _)) {
2502 print "add $devnode: error\n";
2503 system("tree", "$udev_dev");
2504 $error++;
2505 return undef;
2506 }
2507
2508 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
2509 $atime, $mtime, $ctime, $blksize, $blocks) = @st;
2510
2511 if (defined($device->{exp_perms})) {
2512 permissions_test($device, $uid, $gid, $mode);
2513 }
2514 if (defined($device->{exp_majorminor})) {
2515 major_minor_test($device, $rdev);
2516 }
2517 print "add $devnode: ok\n";
2518 $good++;
2519 return $devnode;
2520 }
2521
2522 sub get_link_target {
2523 my ($link) = @_;
2524
2525 my $cwd = getcwd();
2526 my $dir = "$udev_dev/$link";
2527 my $tgt = readlink("$udev_dev/$link");
2528 $dir =~ s!/[^/]*$!!;
2529 $tgt = abs_path("$dir/$tgt");
2530 $tgt =~ s!^$cwd/!!;
2531 return $tgt;
2532 }
2533
2534 sub check_link_add {
2535 my ($link, $devnode, $err_expected) = @_;
2536
2537 my @st = lstat("$udev_dev/$link");
2538 if (-l _) {
2539 my $tgt = get_link_target($link);
2540
2541 if ($tgt ne $devnode) {
2542 print "symlink $link: error, found -> $tgt\n";
2543 $error++;
2544 system("tree", "$udev_dev");
2545 } else {
2546 print "symlink $link: ok\n";
2547 $good++;
2548 }
2549 } else {
2550 print "symlink $link: error";
2551 if ($err_expected) {
2552 print " as expected\n";
2553 $good++;
2554 } else {
2555 print "\n";
2556 system("tree", "$udev_dev");
2557 print "\n";
2558 $error++;
2559 sleep(1);
2560 }
2561 }
2562 }
2563
2564 sub check_link_nonexistent {
2565 my ($link, $devnode, $err_expected) = @_;
2566
2567 if ((-e "$udev_dev/$link") || (-l "$udev_dev/$link")) {
2568 my $tgt = get_link_target($link);
2569
2570 if ($tgt ne $devnode) {
2571 print "nonexistent: '$link' points to other device (ok)\n";
2572 $good++;
2573 } else {
2574 print "nonexistent: error \'$link\' should not be there";
2575 if ($err_expected) {
2576 print " (as expected)\n";
2577 $good++;
2578 } else {
2579 print "\n";
2580 system("tree", "$udev_dev");
2581 print "\n";
2582 $error++;
2583 sleep(1);
2584 }
2585 }
2586 } else {
2587 print "nonexistent $link: ok\n";
2588 $good++;
2589 }
2590 }
2591
2592 sub check_add {
2593 my ($device) = @_;
2594 my $devnode = check_devnode($device);
2595
2596 if (defined($device->{exp_links})) {
2597 foreach my $link (@{$device->{exp_links}}) {
2598 check_link_add($link, $devnode,
2599 $device->{exp_add_error});
2600 }
2601 }
2602 if (defined $device->{not_exp_links}) {
2603 foreach my $link (@{$device->{not_exp_links}}) {
2604 check_link_nonexistent($link, $devnode,
2605 $device->{exp_nodev_error});
2606 }
2607 }
2608 }
2609
2610 sub check_remove_devnode {
2611 my ($device) = @_;
2612 my $devnode = get_devnode($device);
2613
2614 if (-e "$devnode") {
2615 print "remove $devnode: error";
2616 print "\n";
2617 system("tree", "$udev_dev");
2618 print "\n";
2619 $error++;
2620 sleep(1);
2621 } else {
2622 print "remove $devnode: ok\n";
2623 $good++;
2624 }
2625 }
2626
2627 sub check_link_remove {
2628 my ($link, $err_expected) = @_;
2629
2630 if ((-e "$udev_dev/$link") ||
2631 (-l "$udev_dev/$link")) {
2632 print "remove $link: error";
2633 if ($err_expected) {
2634 print " as expected\n";
2635 $good++;
2636 } else {
2637 print "\n";
2638 system("tree", "$udev_dev");
2639 print "\n";
2640 $error++;
2641 sleep(1);
2642 }
2643 } else {
2644 print "remove $link: ok\n";
2645 $good++;
2646 }
2647 }
2648
2649 sub check_remove {
2650 my ($device) = @_;
2651
2652 check_remove_devnode($device);
2653
2654 return if (!defined($device->{exp_links}));
2655
2656 foreach my $link (@{$device->{exp_links}}) {
2657 check_link_remove($link, $device->{exp_rem_error});
2658 }
2659 }
2660
2661 sub run_udev {
2662 my ($action, $dev, $sleep_us, $sema) = @_;
2663
2664 # Notify main process that this worker has started
2665 $sema->op(0, 1, 0);
2666
2667 # Wait for start
2668 $sema->op(0, 0, 0);
2669 usleep($sleep_us) if defined ($sleep_us);
2670 my $rc = udev($action, $dev->{devpath});
2671 exit $rc;
2672 }
2673
2674 sub fork_and_run_udev {
2675 my ($action, $rules, $sema) = @_;
2676 my @devices = @{$rules->{devices}};
2677 my $dev;
2678 my $k = 0;
2679
2680 $sema->setval(0, 1);
2681 foreach $dev (@devices) {
2682 my $pid = fork();
2683
2684 if (!$pid) {
2685 run_udev($action, $dev,
2686 defined($rules->{sleep_us}) ? $k * $rules->{sleep_us} : undef,
2687 $sema);
2688 } else {
2689 $dev->{pid} = $pid;
2690 }
2691 $k++;
2692 }
2693
2694 # This operation waits for all workers to become ready, and
2695 # starts them off when that's the case.
2696 $sema->op(0, -($#devices + 2), 0);
2697
2698 foreach $dev (@devices) {
2699 my $rc;
2700 my $pid;
2701
2702 $pid = waitpid($dev->{pid}, 0);
2703 if ($pid == -1) {
2704 print "error waiting for pid dev->{pid}\n";
2705 }
2706 if (WIFEXITED($?)) {
2707 $rc = WEXITSTATUS($?);
2708
2709 if ($rc) {
2710 print "$udev_bin $action for $dev->{devpath} failed with code $rc\n";
2711 $error += 1;
2712 } else {
2713 $good++;
2714 }
2715 }
2716 }
2717 }
2718
2719 sub run_test {
2720 my ($rules, $number, $sema) = @_;
2721 my $rc;
2722 my @devices;
2723 my $ntests;
2724 my $cur_good = $good;
2725 my $cur_error = $error;
2726
2727 if (!defined $rules->{devices}) {
2728 $rules->{devices} = all_block_devs($rules->{generator});
2729 }
2730 @devices = @{$rules->{devices}};
2731 # For each device: exit status and devnode test for add & remove
2732 $ntests += 4 * ($#devices + 1);
2733
2734 foreach my $dev (@devices) {
2735 $ntests += 2 * ($#{$dev->{exp_links}} + 1)
2736 + ($#{$dev->{not_exp_links}} + 1)
2737 + (defined $dev->{exp_perms} ? 1 : 0)
2738 + (defined $dev->{exp_majorminor} ? 1 : 0);
2739 }
2740 if (defined $rules->{repeat}) {
2741 $ntests *= $rules->{repeat};
2742 }
2743 $exp_good += $ntests;
2744 print "TEST $number: $rules->{desc}\n";
2745 create_rules(\$rules->{rules});
2746
2747 REPEAT:
2748 fork_and_run_udev("add", $rules, $sema);
2749
2750 foreach my $dev (@devices) {
2751 check_add($dev);
2752 }
2753
2754 if (defined($rules->{option}) && $rules->{option} eq "keep") {
2755 print "\n\n";
2756 return;
2757 }
2758
2759 fork_and_run_udev("remove", $rules, $sema);
2760
2761 foreach my $dev (@devices) {
2762 check_remove($dev);
2763 }
2764
2765 if (defined($rules->{repeat}) && --($rules->{repeat}) > 0) {
2766 goto REPEAT;
2767 }
2768 printf "TEST $number: errors: %d good: %d/%d\n\n", $error-$cur_error,
2769 $good-$cur_good, $ntests;
2770
2771 if (defined($rules->{option}) && $rules->{option} eq "clean") {
2772 udev_setup();
2773 }
2774
2775 }
2776
2777 sub cleanup {
2778 system("rm", "-rf", "$udev_run");
2779 system("umount", "$udev_tmpfs");
2780 rmdir($udev_tmpfs);
2781 }
2782
2783 # only run if we have root permissions
2784 # due to mknod restrictions
2785 if (!($<==0)) {
2786 print "Must have root permissions to run properly.\n";
2787 exit($EXIT_TEST_SKIP);
2788 }
2789
2790 # skip the test when running in a chroot
2791 system("systemd-detect-virt", "-r", "-q");
2792 if ($? >> 8 == 0) {
2793 print "Running in a chroot, skipping the test.\n";
2794 exit($EXIT_TEST_SKIP);
2795 }
2796
2797 if (!udev_setup()) {
2798 warn "Failed to set up the environment, skipping the test";
2799 cleanup();
2800 exit($EXIT_TEST_SKIP);
2801 }
2802
2803 if (system($udev_bin, "check")) {
2804 warn "$udev_bin failed to set up the environment, skipping the test";
2805 cleanup();
2806 exit($EXIT_TEST_SKIP);
2807 }
2808
2809 my $test_num = 1;
2810 my @list;
2811
2812 foreach my $arg (@ARGV) {
2813 if ($arg =~ m/--valgrind/) {
2814 $valgrind = 1;
2815 printf("using valgrind\n");
2816 } elsif ($arg =~ m/--gdb/) {
2817 $gdb = 1;
2818 printf("using gdb\n");
2819 } elsif ($arg =~ m/--strace/) {
2820 $strace = 1;
2821 printf("using strace\n");
2822 } else {
2823 push(@list, $arg);
2824 }
2825 }
2826 my $sema = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR | IPC_CREAT);
2827
2828 if ($list[0]) {
2829 foreach my $arg (@list) {
2830 if (defined($tests[$arg-1]->{desc})) {
2831 print "udev-test will run test number $arg:\n\n";
2832 run_test($tests[$arg-1], $arg, $sema);
2833 } else {
2834 print "test does not exist.\n";
2835 }
2836 }
2837 } else {
2838 # test all
2839 print "\nudev-test will run ".($#tests + 1)." tests:\n\n";
2840
2841 foreach my $rules (@tests) {
2842 run_test($rules, $test_num, $sema);
2843 $test_num++;
2844 }
2845 }
2846
2847 $sema->remove;
2848 print "$error errors occurred. $good/$exp_good good results.\n\n";
2849
2850 cleanup();
2851
2852 if ($error > 0) {
2853 exit(1);
2854 }
2855 exit(0);