blob: 7c69c489cb06ed45118ca2d1a7311ea38e8718c2 [file] [log] [blame]
Jörg Thalheim3e67e5c2017-05-01 02:26:56 +02001#!/usr/bin/env python3
Zbigniew Jędrzejewski-Szmek35df7442017-11-18 17:32:46 +01002# SPDX-License-Identifier: LGPL-2.1+
3#
Martin Pittb2ad12e2010-09-21 12:45:52 +02004# Simple udev rules syntax checker
5#
6# (C) 2010 Canonical Ltd.
7# Author: Martin Pitt <martin.pitt@ubuntu.com>
8#
Kay Sievers0228a7e2013-08-14 22:55:40 +02009# systemd is free software; you can redistribute it and/or modify it
10# under the terms of the GNU Lesser General Public License as published by
11# the Free Software Foundation; either version 2.1 of the License, or
Martin Pittb2ad12e2010-09-21 12:45:52 +020012# (at your option) any later version.
Kay Sievers0228a7e2013-08-14 22:55:40 +020013
14# systemd is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17# Lesser General Public License for more details.
Martin Pittb2ad12e2010-09-21 12:45:52 +020018#
Kay Sievers0228a7e2013-08-14 22:55:40 +020019# You should have received a copy of the GNU Lesser General Public License
20# along with systemd; If not, see <http://www.gnu.org/licenses/>.
Martin Pittb2ad12e2010-09-21 12:45:52 +020021
22import re
23import sys
Martin Pitte8015e62015-01-20 20:50:35 +010024import os
25from glob import glob
Martin Pittb2ad12e2010-09-21 12:45:52 +020026
Martin Pitte8015e62015-01-20 20:50:35 +010027if len(sys.argv) > 1:
28 # explicit rule file list
29 rules_files = sys.argv[1:]
30else:
31 # take them from the build dir
32 root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
33 rules_dir = os.path.join(os.environ.get('top_srcdir', root_dir), 'rules')
34 if not os.path.isdir(rules_dir):
Zbigniew Jędrzejewski-Szmek29563952017-11-22 12:29:46 +010035 print('No rules files given, and {} does not exist, aborting'.format(rules_dir), file=sys.stderr)
Martin Pitte8015e62015-01-20 20:50:35 +010036 sys.exit(2)
37 rules_files = glob(os.path.join(rules_dir, '*.rules'))
Martin Pittb2ad12e2010-09-21 12:45:52 +020038
Zbigniew Jędrzejewski-Szmekcda39972016-12-01 18:29:54 -050039no_args_tests = re.compile(r'(ACTION|DEVPATH|KERNELS?|NAME|SYMLINK|SUBSYSTEMS?|DRIVERS?|TAG|RESULT|TEST)\s*(?:=|!)=\s*"([^"]*)"$')
40args_tests = re.compile(r'(ATTRS?|ENV|TEST){([a-zA-Z0-9/_.*%-]+)}\s*(?:=|!)=\s*"([^"]*)"$')
41no_args_assign = re.compile(r'(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|PROGRAM|RUN|LABEL|GOTO|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*"([^"]*)"$')
42args_assign = re.compile(r'(ATTR|ENV|IMPORT|RUN){([a-zA-Z0-9/_.*%-]+)}\s*(=|\+=)\s*"([^"]*)"$')
Martin Pittb2ad12e2010-09-21 12:45:52 +020043
44result = 0
45buffer = ''
Martin Pitte8015e62015-01-20 20:50:35 +010046for path in rules_files:
Zbigniew Jędrzejewski-Szmek29563952017-11-22 12:29:46 +010047 print('# looking at {}'.format(path))
Martin Pittb2ad12e2010-09-21 12:45:52 +020048 lineno = 0
49 for line in open(path):
50 lineno += 1
51
52 # handle line continuation
53 if line.endswith('\\\n'):
54 buffer += line[:-2]
55 continue
56 else:
57 line = buffer + line
58 buffer = ''
59
60 # filter out comments and empty lines
61 line = line.strip()
62 if not line or line.startswith('#'):
63 continue
64
65 for clause in line.split(','):
66 clause = clause.strip()
67 if not (no_args_tests.match(clause) or args_tests.match(clause) or
68 no_args_assign.match(clause) or args_assign.match(clause)):
69
Zbigniew Jędrzejewski-Szmek29563952017-11-22 12:29:46 +010070 print('Invalid line {}:{}: {}'.format(path, lineno, line))
71 print(' clause:', clause)
72 print()
Martin Pittb2ad12e2010-09-21 12:45:52 +020073 result = 1
74 break
75
76sys.exit(result)