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
20use warnings;
21use strict;
22
23BEGIN {
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
40my $udev_bin            = "./test-udev";
41my $valgrind            = 0;
42my $gdb                 = 0;
43my $strace              = 0;
44my $udev_bin_valgrind   = "valgrind --tool=memcheck --leak-check=yes --track-origins=yes --quiet $udev_bin";
45my $udev_bin_gdb        = "gdb --args $udev_bin";
46my $udev_bin_strace     = "strace -efile $udev_bin";
47my $udev_run            = "test/run";
48my $udev_tmpfs          = "test/tmpfs";
49my $udev_sys            = "${udev_tmpfs}/sys";
50my $udev_dev            = "${udev_tmpfs}/dev";
51my $udev_rules_dir      = "$udev_run/udev/rules.d";
52my $udev_rules          = "$udev_rules_dir/udev-test.rules";
53my $EXIT_TEST_SKIP      = 77;
54
55my $rules_10k_tags      = "";
56for (my $i = 1; $i <= 10000; ++$i) {
57        $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n";
58}
59
60my $rules_10k_tags_continuation = "KERNEL==\"sda\", \\\n";
61for (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.
70sub 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().
94sub 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
110my @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#
124EOF
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
134SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
135KERNEL=="ttyACM0", SYMLINK+="modem"
136EOF
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
146SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
147KERNEL=="ttyACM0", SYMLINK+="modem"
148EOF
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
158SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
159KERNEL=="ttyACM0", SYMLINK+="modem"
160EOF
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
170SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
171EOF
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
182SUBSYSTEMS=="scsi", ATTRS{vendor}=="?ATA", SYMLINK+="boot_disk%n-1"
183SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA?", SYMLINK+="boot_disk%n-2"
184SUBSYSTEMS=="scsi", ATTRS{vendor}=="A??", SYMLINK+="boot_disk%n"
185SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATAS", SYMLINK+="boot_disk%n-3"
186SUBSYSTEMS=="scsi", ATTRS{vendor}=="AT?", SYMLINK+="boot_disk%n-4"
187SUBSYSTEMS=="scsi", ATTRS{vendor}=="??A", SYMLINK+="boot_disk%n-5"
188SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", GOTO="skip-6"
189SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n-6"
190LABEL="skip-6"
191SUBSYSTEMS=="scsi", GOTO="skip-7"
192SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n-7"
193LABEL="skip-7"
194EOF
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
205SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS X ", SYMLINK+="boot_diskX%n"
206SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK+="boot_disk%n"
207EOF
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
218SUBSYSTEMS=="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"
219SUBSYSTEMS=="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"
220SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", SYMLINK+="boot_disk%n"
221EOF
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
231KERNEL=="ttyACM*", SYMLINK+="modem/%n"
232KERNEL=="*", SYMLINK+="catch-all"
233EOF
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
245KERNEL=="*ACM1", SYMLINK+="bad"
246KERNEL=="*ACM0", SYMLINK+="modem/%n"
247EOF
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
258KERNEL=="ttyACM??*", SYMLINK+="modem/%n-1"
259KERNEL=="ttyACM??", SYMLINK+="modem/%n-2"
260KERNEL=="ttyACM?", SYMLINK+="modem/%n"
261EOF
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
272KERNEL=="ttyACM[A-Z]*", SYMLINK+="modem/%n-1"
273KERNEL=="ttyACM?[0-9]", SYMLINK+="modem/%n-2"
274KERNEL=="ttyACM[0-9]*", SYMLINK+="modem/%n"
275EOF
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
285KERNEL=="ttyACM0", SYMLINK+="modem"
286EOF
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
297KERNEL=="ttyACM0", SYMLINK+="modem"
298
299EOF
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
310KERNEL=="ttyACM0", SYMLINK+="modem"
311
312EOF
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
326KERNEL=="ttyACM0", SYMLINK+="whitespace"
327
328
329
330EOF
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
341KERNEL=="ttyACM0", SYMLINK+="modem"
342
343EOF
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
353KERNEL=="ttyACM0", \\
354SYMLINK+="modem"
355
356EOF
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
366KERNEL=="ttyACM0", PROGRAM=="/bin/echo -e \\101", RESULT=="A", SYMLINK+="aaa"
367EOF
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
386KERNEL=="ttyACM0", \\
387        SYMLINK+="modem"
388
389EOF
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
399KERNEL=="ttyACM0", SYMLINK+="sub/direct/ory/modem"
400EOF
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
410SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="first_disk%n"
411EOF
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
421SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="Major:%M:minor:%m:kernelnumber:%n:id:%b"
422EOF
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
432SUBSYSTEMS=="scsi", IMPORT{program}="/bin/echo -e \' TEST_KEY=12345678\\n  TEST_key2=98765\'", SYMLINK+="node\$env{TEST_KEY}"
433KERNEL=="ttyACM0", SYMLINK+="modem"
434EOF
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
445SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="disk-%s{vendor}-%k"
446KERNEL=="ttyACM0", SYMLINK+="modem"
447EOF
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
458SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n special-device", RESULT=="-special-*", SYMLINK+="not"
459SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n special-device", RESULT=="special-*", SYMLINK+="%c-%n"
460EOF
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
470SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo test", RESULT=="test", SYMLINK+="newline_removed"
471EOF
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
481SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n test-%b", RESULT=="test-0:0*", SYMLINK+="%c"
482EOF
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
493SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="%c{7}"
494EOF
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
505SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'echo foo3 foo4 foo5 foo6 foo7 foo8 foo9 | sed  s/foo9/bar9/'", KERNEL=="sda5", SYMLINK+="%c{7}"
506EOF
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
517SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n 'foo3 foo4'   'foo5   foo6   foo7 foo8'", KERNEL=="sda5", SYMLINK+="%c{5}"
518EOF
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
529SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'printf %%s \\\"foo1 foo2\\\" | grep \\\"foo1 foo2\\\"'", KERNEL=="sda5", SYMLINK+="%c{2}"
530EOF
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
541SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c \\\"printf %%s 'foo1 foo2' | grep 'foo1 foo2'\\\"", KERNEL=="sda5", SYMLINK+="%c{2}"
542EOF
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
553SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'printf \\\"%%s %%s\\\" \\\"foo1 foo2\\\" \\\"foo3\\\"| grep \\\"foo1 foo2\\\"'", KERNEL=="sda5", SYMLINK+="%c{2}"
554EOF
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
564SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="my-%c{7}"
565EOF
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
576SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="my-%c{6}"
577EOF
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
587SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="Major:\$major-minor:\$minor-kernelnumber:\$number-id:\$id"
588EOF
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
598SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="Major:\$major-minor:%m-kernelnumber:\$number-id:\$id"
599EOF
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
609SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="%M%m%b%n"
610EOF
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
620SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="\$major\$minor\$number"
621EOF
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
631SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="\$major%m%n\$id"
632EOF
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
643SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo", RESULT=="foo", SYMLINK+="foo"
644KERNEL=="console", SYMLINK+="TTY"
645EOF
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
656SUBSYSTEMS=="foo", ATTRS{dev}=="5:1", SYMLINK+="foo"
657KERNEL=="console", SYMLINK+="TTY"
658EOF
659        },
660        {
661                desc            => "ATTRS match",
662                devices => [
663                        {
664                                devpath         => "/devices/virtual/tty/console",
665                                exp_links       => ["foo", "TTY"],
666                        }],
667                rules           => <<EOF
668KERNEL=="console", SYMLINK+="TTY"
669ATTRS{dev}=="5:1", SYMLINK+="foo"
670EOF
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
681KERNEL=="sda", ATTR{test_empty_file}=="?*", SYMLINK+="something"
682KERNEL=="sda", ATTR{test_empty_file}!="", SYMLINK+="not-empty"
683KERNEL=="sda", ATTR{test_empty_file}=="", SYMLINK+="empty"
684KERNEL=="sda", ATTR{test_empty_file}!="?*", SYMLINK+="not-something"
685EOF
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
697KERNEL=="sda", ATTR{nofile}=="?*", SYMLINK+="something"
698KERNEL=="sda", ATTR{nofile}!="", SYMLINK+="not-empty"
699KERNEL=="sda", ATTR{nofile}=="", SYMLINK+="empty"
700KERNEL=="sda", ATTR{nofile}!="?*", SYMLINK+="not-something"
701KERNEL=="sda", TEST!="nofile", SYMLINK+="non-existent"
702KERNEL=="sda", SYMLINK+="wrong"
703EOF
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
713SUBSYSTEMS=="usb", PROGRAM=="/bin/echo -n usb-%b", SYMLINK+="%c"
714SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n scsi-%b", SYMLINK+="%c"
715SUBSYSTEMS=="foo", PROGRAM=="/bin/echo -n foo-%b", SYMLINK+="%c"
716EOF
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
726ATTRS{idProduct}=="007b", SYMLINK+="modem"
727EOF
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
739SUBSYSTEMS=="scsi", SYMLINK+="is/not/a/%k"
740SUBSYSTEM=="block", SYMLINK+="is/a/%k"
741KERNEL=="ttyACM0", SYMLINK+="modem"
742EOF
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
753KERNEL=="ttyACM0", SYMLINK+="modem"
754EOF
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
765SUBSYSTEMS=="usb", KERNELS=="0:0:0:0", SYMLINK+="not-scsi"
766SUBSYSTEMS=="scsi", KERNELS=="0:0:0:1", SYMLINK+="no-match"
767SUBSYSTEMS=="scsi", KERNELS==":0", SYMLINK+="short-id"
768SUBSYSTEMS=="scsi", KERNELS=="/0:0:0:0", SYMLINK+="no-match"
769SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="scsi-0:0:0:0"
770EOF
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
781SUBSYSTEMS=="scsi", KERNELS=="*:1", SYMLINK+="no-match"
782SUBSYSTEMS=="scsi", KERNELS=="*:0:1", SYMLINK+="no-match"
783SUBSYSTEMS=="scsi", KERNELS=="*:0:0:1", SYMLINK+="no-match"
784SUBSYSTEMS=="scsi", KERNEL=="0:0:0:0", SYMLINK+="before"
785SUBSYSTEMS=="scsi", KERNELS=="*", SYMLINK+="scsi-0:0:0:0"
786EOF
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
796SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="before"
797SUBSYSTEMS=="scsi", KERNELS=="*:0", SYMLINK+="scsi-0:0:0:0"
798EOF
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
808SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="before"
809SUBSYSTEMS=="scsi", KERNELS=="*:0:0:0", SYMLINK+="scsi-0:0:0:0"
810EOF
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
820SUBSYSTEMS=="scsi", SYMLINK+="driver-is-\$attr{driver}"
821EOF
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
831SUBSYSTEMS=="pci", SYMLINK+="driver-is-\$attr{driver}"
832EOF
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
842SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE  SPACE", SYMLINK+="ignored"
843EOF
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
854SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE  SPACE ", SYMLINK+="wrong-to-ignore"
855SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE  SPACE   ", SYMLINK+="matched-with-space"
856EOF
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
866KERNEL=="tty33", OWNER="bad", GROUP="name"
867EOF
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
878SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="1"
879EOF
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
890SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", GROUP="1"
891EOF
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
902SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="daemon"
903EOF
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
914SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", GROUP="daemon"
915EOF
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
926SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="root", GROUP="audio"
927EOF
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
938SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", MODE="0777"
939EOF
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
950SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="1", GROUP="1", MODE="0777"
951EOF
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
961KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", OWNER="1"
962EOF
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
972KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", GROUP="1"
973EOF
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
983KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", MODE="0060"
984EOF
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
994KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", OWNER="1", GROUP="1", MODE="0777"
995EOF
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
1005KERNEL=="ttyACM[0-9]*", OWNER="1", GROUP="1", MODE="0777"
1006KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444"
1007KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n"
1008EOF
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
1018SUBSYSTEM=="tty", OWNER="1"
1019SUBSYSTEM=="tty", GROUP="1"
1020SUBSYSTEM=="tty", MODE="0777"
1021KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444"
1022KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n"
1023EOF
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
1033SUBSYSTEM=="tty", OWNER="1"
1034SUBSYSTEM=="tty", GROUP="1"
1035SUBSYSTEM=="tty", MODE="0777"
1036KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444"
1037KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", GROUP="2"
1038EOF
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
1049SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node"
1050EOF
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
1061KERNEL=="misc-fake1", SYMLINK+="node"
1062EOF
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
1073KERNEL=="misc-fake89999", SYMLINK+="node"
1074EOF
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
1084KERNEL=="ttyACM[0-9]*", SYMLINK="symlink1-%n symlink2-%k symlink3-%b"
1085EOF
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
1096KERNEL=="ttyACM[0-9]*", SYMLINK="  one     two        "
1097EOF
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
1108ENV{WITH_WS}="one two three"
1109SYMLINK="name-\$env{WITH_WS}-end"
1110EOF
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
1121ENV{WITH_WS}="   one two three"
1122SYMLINK="name-\$env{WITH_WS}-end"
1123EOF
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
1134ENV{WITH_WS}="one two three   "
1135SYMLINK="name-\$env{WITH_WS}-end"
1136EOF
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
1147ENV{WITH_WS}="   one two three   "
1148SYMLINK="name-\$env{WITH_WS}-end"
1149EOF
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
1160ENV{WITH_WS}="   one  two  three   "
1161SYMLINK="name-\$env{WITH_WS}-end"
1162EOF
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
1174ENV{WITH_WS}="   one  two  three   "
1175SYMLINK="  first  name-\$env{WITH_WS}-end another_symlink a b c "
1176EOF
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
1188ENV{WITH_SLASH}="aaa/bbb/ccc"
1189OPTIONS="string_escape=replace", ENV{REPLACED}="\$env{WITH_SLASH}"
1190SYMLINK="  first  name-\$env{REPLACED}-end another_symlink a b c "
1191EOF
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
1201KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK="modem%n"
1202EOF
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
1212KERNEL=="ttyACM0", SYMLINK="first-%n second-%n third-%n"
1213EOF
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
1225SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="."
1226EOF
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
1239KERNEL=="tty0", SYMLINK+="tty0"
1240EOF
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
1250KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="symlink%n"
1251EOF
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
1261KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="symlink-%k"
1262EOF
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
1272KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="major-%M:%m"
1273EOF
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
1283SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="symlink-%b"
1284EOF
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
1294KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo test", SYMLINK+="%c"
1295EOF
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
1306KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo symlink test this", SYMLINK+="%c{2}"
1307EOF
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
1318KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo symlink test this", SYMLINK+="%c{2+}"
1319EOF
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
1330SUBSYSTEMS=="scsi", KERNEL=="sda", PROGRAM=="/bin/echo link test this" SYMLINK+="%c{2+}"
1331EOF
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
1341KERNEL=="ttyACM[0-9]*", SYMLINK+="%s{dev}"
1342EOF
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
1353SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n node link1 link2", RESULT=="node *", SYMLINK+="%c{2} %c{3}"
1354EOF
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
1365SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n node link1 link2 link3 link4", RESULT=="node *", SYMLINK+="%c{2+}"
1366EOF
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
1377SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match", SUBSYSTEM=="vc"
1378SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", SUBSYSTEM=="block"
1379SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match2", SUBSYSTEM=="vc"
1380EOF
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
1391SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match", DRIVERS=="sd-wrong"
1392SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", DRIVERS=="sd"
1393EOF
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
1403SUBSYSTEMS=="scsi", KERNEL=="sda", PROGRAM=="/usr/bin/test -b %N" SYMLINK+="node"
1404EOF
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
1414SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="%P-part-%n"
1415EOF
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
1425SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="start-%r-end"
1426EOF
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
1439SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="last", OPTIONS="last_rule"
1440SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="very-last"
1441EOF
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
1452SUBSYSTEMS=="scsi", KERNEL!="sda1", SYMLINK+="matches-but-is-negated"
1453SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1454SUBSYSTEMS=="scsi", KERNEL!="xsda1", SYMLINK+="match"
1455EOF
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
1466SUBSYSTEMS=="scsi", SUBSYSTEM=="block", KERNEL!="sda1", SYMLINK+="matches-but-is-negated"
1467SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1468SUBSYSTEMS=="scsi", SUBSYSTEM!="anything", SYMLINK+="not-anything"
1469EOF
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
1479SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1480KERNEL=="sda1", PROGRAM!="/bin/false", SYMLINK+="nonzero-program"
1481EOF
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
1492ENV{ENV_KEY_TEST}="test"
1493SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="go", SYMLINK+="wrong"
1494SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="test", SYMLINK+="true"
1495SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="bad", SYMLINK+="bad"
1496EOF
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
1507ENV{ENV_KEY_TEST}="test"
1508SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="go", SYMLINK+="wrong"
1509SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="yes", ENV{ACTION}=="add", ENV{DEVPATH}=="*/block/sda/sdax1", SYMLINK+="no"
1510SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="test", ENV{ACTION}=="add", ENV{DEVPATH}=="*/block/sda/sda1", SYMLINK+="true"
1511SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="bad", SYMLINK+="bad"
1512EOF
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
1523SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="true"
1524SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="yes", SYMLINK+="no"
1525SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1526SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="true", SYMLINK+="true"
1527EOF
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
1538SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="true"
1539SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="absolutely-\$env{ASSIGN}"
1540SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
1541SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="yes", SYMLINK+="no"
1542SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="true", SYMLINK+="bad"
1543SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="absolutely-true", SYMLINK+="true"
1544EOF
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
1561SUBSYSTEM=="block", KERNEL=="*[0-9]", ENV{PARTITION}="true", ENV{MAINDEVICE}="false"
1562SUBSYSTEM=="block", KERNEL=="*[!0-9]", ENV{PARTITION}="false", ENV{MAINDEVICE}="true"
1563ENV{MAINDEVICE}=="true", SYMLINK+="disk"
1564SUBSYSTEM=="block", SYMLINK+="before"
1565ENV{PARTITION}=="true", SYMLINK+="part"
1566EOF
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
1576SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e name; (/usr/bin/badprogram)", RESULT=="name_ _/usr/bin/badprogram_", SYMLINK+="sane"
1577EOF
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
1587SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e \\xc3\\xbcber" RESULT=="\xc3\xbcber", SYMLINK+="uber"
1588EOF
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
1598SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e \\xef\\xe8garbage", RESULT=="__garbage", SYMLINK+="replaced"
1599EOF
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
1609KERNEL=="ttyACM*", ATTRS{serial}=="?*", SYMLINK+="serial-%s{serial}"
1610EOF
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
1621KERNEL=="sda", ATTRS{nothing}!="", SYMLINK+="not-1-ok"
1622KERNEL=="sda", ATTRS{nothing}=="", SYMLINK+="not-2-ok"
1623KERNEL=="sda", ATTRS{vendor}!="", SYMLINK+="ok"
1624KERNEL=="sda", ATTRS{vendor}=="", SYMLINK+="not-3-ok"
1625EOF
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
1636ACTION=="unknown", KERNEL=="sda", SYMLINK+="unknown-not-ok"
1637ACTION=="add", KERNEL=="sda", SYMLINK+="ok"
1638EOF
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
1649KERNEL=="sda", GROUP:="tty"
1650KERNEL=="sda", GROUP="root", MODE="0640", SYMLINK+="ok"
1651EOF
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
1662KERNEL=="sda", GROUP:="tty"
1663SUBSYSTEM=="block", MODE:="640"
1664KERNEL=="sda", GROUP="root", MODE="0666", SYMLINK+="ok"
1665EOF
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
1675KERNEL=="sda", MODE="0666", SYMLINK+="node-\$env{ACTION}-me"
1676EOF
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
1687KERNEL=="ttyACM[0-9]*", SYMLINK+="one"
1688KERNEL=="ttyACM[0-9]*", SYMLINK+="two"
1689KERNEL=="ttyACM[0-9]*", SYMLINK="three"
1690EOF
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
1701KERNEL=="ttyACM[0-9]*", SYMLINK+="wrong"
1702KERNEL=="ttyACM[0-9]*", SYMLINK=""
1703KERNEL=="ttyACM[0-9]*", SYMLINK+="right"
1704EOF
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
1714KERNEL=="ttyACM*", SYMLINK+="before"
1715KERNEL=="ttyACM*|nothing", SYMLINK+="right"
1716EOF
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
1727KERNEL=="dontknow*|*nothing", SYMLINK+="nomatch"
1728KERNEL=="ttyACM*", SYMLINK+="before"
1729KERNEL=="dontknow*|ttyACM*|nothing*", SYMLINK+="right"
1730EOF
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
1741KERNEL=="dontknow|nothing", SYMLINK+="nomatch"
1742KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1"
1743KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2"
1744KERNEL=="dontknow|ttyACM0|nothing", SYMLINK+="right"
1745EOF
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
1756KERNEL=="dontknow|nothing", SYMLINK+="nomatch"
1757KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1"
1758KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2"
1759KERNEL=="all|dontknow|ttyACM0", SYMLINK+="right"
1760KERNEL=="ttyACM0a|nothing", SYMLINK+="wrong3"
1761EOF
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
1772KERNEL=="sda", TAG="foo"
1773TAGS=="|foo", SYMLINK+="found"
1774TAGS=="|aaa", SYMLINK+="bad"
1775EOF
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
1786KERNEL=="sda", TAG=""
1787TAGS=="|foo", SYMLINK+="found"
1788TAGS=="aaa|bbb", SYMLINK+="bad"
1789EOF
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
1800KERNEL=="sda", TAG="foo"
1801TAGS=="foo||bar", SYMLINK+="found"
1802TAGS=="aaa||bbb", SYMLINK+="bad"
1803EOF
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
1814KERNEL=="sda", TAG=""
1815TAGS=="foo||bar", SYMLINK+="found"
1816TAGS=="aaa|bbb", SYMLINK+="bad"
1817EOF
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
1828KERNEL=="sda", TAG="foo"
1829TAGS=="foo|", SYMLINK+="found"
1830TAGS=="aaa|", SYMLINK+="bad"
1831EOF
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
1842KERNEL=="sda", TAG=""
1843TAGS=="foo|", SYMLINK+="found"
1844TAGS=="aaa|bbb", SYMLINK+="bad"
1845EOF
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
1856KERNEL=="sda", TAG="c"
1857TAGS=="foo||bar||c", SYMLINK+="found"
1858TAGS=="aaa||bbb||ccc", SYMLINK+="bad"
1859EOF
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
1874KERNEL=="sda1", IMPORT{parent}="PARENT*", SYMLINK+="parentenv-\$env{PARENT_KEY}\$env{WRONG_PARENT_KEY}"
1875KERNEL=="sda", IMPORT{program}="/bin/echo -e \'PARENT_KEY=parent_right\\nWRONG_PARENT_KEY=parent_wrong'"
1876KERNEL=="sda", SYMLINK+="parent"
1877EOF
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
1888KERNEL=="sda1", GOTO="TEST"
1889KERNEL=="sda1", SYMLINK+="wrong"
1890KERNEL=="sda1", GOTO="BAD"
1891KERNEL=="sda1", SYMLINK+="", LABEL="NO"
1892KERNEL=="sda1", SYMLINK+="right", LABEL="TEST", GOTO="end"
1893KERNEL=="sda1", SYMLINK+="wrong2", LABEL="BAD"
1894LABEL="end"
1895EOF
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
1905KERNEL=="sda1", GOTO="does-not-exist"
1906KERNEL=="sda1", SYMLINK+="right",
1907LABEL="exists"
1908EOF
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
1919KERNEL=="sda1", SYMLINK+="link"
1920KERNEL=="sda1", SYMLINK=="link*", SYMLINK+="right"
1921KERNEL=="sda1", SYMLINK=="nolink*", SYMLINK+="wrong"
1922EOF
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
1933KERNEL="sda1", SYMLINK+="no"
1934KERNEL=="sda1", SYMLINK+="yes"
1935EOF
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
1945KERNEL=="sda", ATTR{test:colon+plus}=="?*", SYMLINK+="yes"
1946EOF
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
1959KERNEL=="sda1", SYMLINK+=="no"
1960KERNEL=="sda1", SYMLINK+="yes"
1961EOF
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
1971KERNEL=="sda", SYMLINK+="\$attr{[net/eth0]address}"
1972EOF
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
1983TEST=="/etc/machine-id", SYMLINK+="there"
1984TEST!="/etc/machine-id", SYMLINK+="notthere"
1985EOF
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
1995KERNEL=="sda", TEST=="[net/eth0]", SYMLINK+="yes"
1996EOF
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
2006KERNEL=="sda", TEST=="size", SYMLINK+="relative"
2007EOF
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
2017KERNEL=="sda", TEST=="*/nr_requests", SYMLINK+="found-subdir"
2018EOF
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
2029KERNEL=="sda", MODE="0000"
2030EOF
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
2040KERNEL=="sda", MODE="666"
2041KERNEL=="sda", PROGRAM=="/bin/echo 1 1 0400", OWNER="%c{1}", GROUP="%c{2}", MODE="%c{3}"
2042EOF
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
2053KERNEL=="sda", MODE="440"
2054KERNEL=="sda", PROGRAM=="/bin/echo 0 0 0400letsdoabuffferoverflow0123456789012345789012345678901234567890", OWNER="%c{1}", GROUP="%c{2}", MODE="%c{3}"
2055EOF
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
2066KERNEL=="sda", SYMLINK+="%k-%s{[dmi/id]product_name}-end"
2067EOF
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
2077KERNEL=="sda", IMPORT{builtin}="path_id"
2078KERNEL=="sda", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/\$env{ID_PATH}"
2079EOF
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
2090SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", TAG+="green"
2091TAGS=="green", SYMLINK+="found"
2092TAGS=="blue", SYMLINK+="bad"
2093EOF
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
2103TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="found"
2104EOF
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
2115TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="bad"
2116KERNEL=="sda",\\
2117# comment in continuation
2118TAG+="hoge1",\\
2119  # space before comment
2120TAG+="hoge2",\\
2121# spaces before and after token are dropped
2122  TAG+="hoge3",   \\
2123\\
2124 \\
2125TAG+="hoge4"
2126TAGS=="hoge1", TAGS=="hoge2", TAGS=="hoge3", TAGS=="hoge4", SYMLINK+="found"
2127EOF
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
2140KERNEL=="sda", TAG+="foo" \\
2141
2142KERNEL=="sdb", TAG+="hoge"
2143KERNEL=="sda", TAG+="aaa" \\
2144KERNEL=="sdb", TAG+="bbb"
2145TAGS=="foo", SYMLINK+="found"
2146TAGS=="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
2159KERNEL=="sda", TAG+="foo" \\
2160   \t
2161KERNEL=="sdb", TAG+="hoge"
2162KERNEL=="sda", TAG+="aaa" \\
2163KERNEL=="sdb", TAG+="bbb"
2164TAGS=="foo", SYMLINK+="found"
2165TAGS=="aaa", SYMLINK+="bad"
2166EOF
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
2201SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n"
2202EOF
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
2244SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n"
2245SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition"
2246KERNEL=="*7", OPTIONS+="link_priority=10"
2247EOF
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
2288SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n"
2289SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition"
2290KERNEL!="*7", OPTIONS+="link_priority=-10"
2291EOF
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
2333SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n"
2334SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition"
2335KERNEL=="*7", OPTIONS+="link_priority=10"
2336EOF
2337        },
2338        {
2339                desc           => 'all_block_devs',
2340                generator      => expect_for_some("\\/sda6\$", ["blockdev"]),
2341                repeat         => 10,
2342                rules          => <<EOF
2343SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sd*", SYMLINK+="blockdev"
2344KERNEL=="sda6", OPTIONS+="link_priority=10"
2345EOF
2346        }
2347);
2348
2349sub 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
2359sub 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
2373my $error = 0;
2374my $good = 0;
2375my $exp_good = 0;
2376
2377sub 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
2416sub 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
2442sub 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=755,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
2483sub 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
2496sub 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
2522sub 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
2534sub 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
2564sub 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
2592sub 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
2610sub 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
2627sub 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
2649sub 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
2661sub 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
2674sub 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
2719sub 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
2777sub 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
2785if (!($<==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
2791system("systemd-detect-virt", "-r", "-q");
2792if ($? >> 8 == 0) {
2793        print "Running in a chroot, skipping the test.\n";
2794        exit($EXIT_TEST_SKIP);
2795}
2796
2797if (!udev_setup()) {
2798        warn "Failed to set up the environment, skipping the test";
2799        cleanup();
2800        exit($EXIT_TEST_SKIP);
2801}
2802
2803if (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
2809my $test_num = 1;
2810my @list;
2811
2812foreach 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}
2826my $sema = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR | IPC_CREAT);
2827
2828if ($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;
2848print "$error errors occurred. $good/$exp_good good results.\n\n";
2849
2850cleanup();
2851
2852if ($error > 0) {
2853        exit(1);
2854}
2855exit(0);
2856