-- Copyright 2016 Christian Hesse -- systemd unit file LPeg lexer. local l = require('lexer') local token, word_match = l.token, l.word_match local P, R, S = lpeg.P, lpeg.R, lpeg.S local M = {_NAME = 'systemd'} -- Whitespace. local ws = token(l.WHITESPACE, l.space^1) -- Comments. local comment = token(l.COMMENT, l.starts_line(S(';#')) * l.nonnewline^0) -- Strings. local sq_str = l.delimited_range("'") local dq_str = l.delimited_range('"') local section_word = word_match{ 'Automount', 'BusName', 'Install', 'Mount', 'Path', 'Service', 'Service', 'Socket', 'Timer', 'Unit' } local string = token(l.STRING, sq_str + dq_str + '[' * section_word * ']') -- Numbers. local dec = l.digit^1 * ('_' * l.digit^1)^0 local oct_num = '0' * S('01234567_')^1 local integer = S('+-')^-1 * (l.hex_num + oct_num + dec) local number = token(l.NUMBER, (l.float + integer)) -- Keywords. local keyword = token(l.KEYWORD, word_match({ -- boolean values 'true', 'false', 'on', 'off', 'yes', 'no', -- service types 'forking', 'simple', 'oneshot', 'dbus', 'notify', 'idle', -- special system units 'basic.target', 'ctrl-alt-del.target', 'cryptsetup.target', 'dbus.service', 'dbus.socket', 'default.target', 'display-manager.service', 'emergency.target', 'exit.target', 'final.target', 'getty.target', 'graphical.target', 'hibernate.target', 'hybrid-sleep.target', 'halt.target', 'initrd-fs.target', 'kbrequest.target', 'kexec.target', 'local-fs.target', 'multi-user.target', 'network-online.target', 'paths.target', 'poweroff.target', 'reboot.target', 'remote-fs.target', 'rescue.target', 'initrd-root-fs.target', 'runlevel2.target', 'runlevel3.target', 'runlevel4.target', 'runlevel5.target', 'shutdown.target', 'sigpwr.target', 'sleep.target', 'slices.target', 'sockets.target', 'suspend.target', 'swap.target', 'sysinit.target', 'syslog.socket', 'system-update.target', 'timers.target', 'umount.target', -- special system units for devices 'bluetooth.target', 'printer.target', 'smartcard.target', 'sound.target', -- special passive system units 'cryptsetup-pre.target', 'local-fs-pre.target', 'network.target', 'network-pre.target', 'nss-lookup.target', 'nss-user-lookup.target', 'remote-fs-pre.target', 'rpcbind.target', 'time-sync.target', -- specail slice units '-.slice', 'system.slice', 'user.slice', 'machine.slice', -- environment variables 'PATH', 'LANG', 'USER', 'LOGNAME', 'HOME', 'SHELL', 'XDG_RUNTIME_DIR', 'XDG_SESSION_ID', 'XDG_SEAT', 'XDG_VTNR', 'MAINPID', 'MANAGERPID', 'LISTEN_FDS', 'LISTEN_PID', 'LISTEN_FDNAMES', 'NOTIFY_SOCKET', 'WATCHDOG_PID', 'WATCHDOG_USEC', 'TERM' }, '.-')) -- Options. local option_word = word_match{ -- unit section options 'Description', 'Documentation', 'Requires', 'Requisite', 'Wants', 'BindsTo', 'PartOf', 'Conflicts', 'Before', 'After', 'OnFailure', 'PropagatesReloadTo', 'ReloadPropagatedFrom', 'JoinsNamespaceOf', 'RequiresMountsFor', 'OnFailureJobMode', 'IgnoreOnIsolate', 'StopWhenUnneeded', 'RefuseManualStart', 'RefuseManualStop', 'AllowIsolate', 'DefaultDependencies', 'JobTimeoutSec', 'JobTimeoutAction', 'JobTimeoutRebootArgument', 'StartLimitInterval', 'StartLimitBurst', 'StartLimitAction', 'RebootArgument', 'ConditionArchitecture', 'ConditionVirtualization', 'ConditionHost', 'ConditionKernelCommandLine', 'ConditionSecurity', 'ConditionCapability', 'ConditionACPower', 'ConditionNeedsUpdate', 'ConditionFirstBoot', 'ConditionPathExists', 'ConditionPathExistsGlob', 'ConditionPathIsDirectory', 'ConditionPathIsSymbolicLink', 'ConditionPathIsMountPoint', 'ConditionPathIsReadWrite', 'ConditionDirectoryNotEmpty', 'ConditionFileNotEmpty', 'ConditionFileIsExecutable', 'AssertArchitecture', 'AssertVirtualization', 'AssertHost', 'AssertKernelCommandLine', 'AssertSecurity', 'AssertCapability', 'AssertACPower', 'AssertNeedsUpdate', 'AssertFirstBoot', 'AssertPathExists', 'AssertPathExistsGlob', 'AssertPathIsDirectory', 'AssertPathIsSymbolicLink', 'AssertPathIsMountPoint', 'AssertPathIsReadWrite', 'AssertDirectoryNotEmpty', 'AssertFileNotEmpty', 'AssertFileIsExecutable', 'SourcePath', -- install section options 'Alias', 'WantedBy', 'RequiredBy', 'Also', 'DefaultInstance', -- service section options 'Type', 'RemainAfterExit', 'GuessMainPID', 'PIDFile', 'BusName', 'BusPolicy', 'ExecStart', 'ExecStartPre', 'ExecStartPost', 'ExecReload', 'ExecStop', 'ExecStopPost', 'RestartSec', 'TimeoutStartSec', 'TimeoutStopSec', 'TimeoutSec', 'RuntimeMaxSec', 'WatchdogSec', 'Restart', 'SuccessExitStatus', 'RestartPreventExitStatus', 'RestartForceExitStatus', 'PermissionsStartOnly', 'RootDirectoryStartOnly', 'NonBlocking', 'NotifyAccess', 'Sockets', 'FailureAction', 'FileDescriptorStoreMax', 'USBFunctionDescriptors', 'USBFunctionStrings', -- socket section options 'ListenStream', 'ListenDatagram', 'ListenSequentialPacket', 'ListenFIFO', 'ListenSpecial', 'ListenNetlink', 'ListenMessageQueue', 'ListenUSBFunction', 'SocketProtocol', 'BindIPv6Only', 'Backlog', 'BindToDevice', 'SocketUser', 'SocketGroup', 'SocketMode', 'DirectoryMode', 'Accept', 'Writable', 'MaxConnections', 'KeepAlive', 'KeepAliveTimeSec', 'KeepAliveIntervalSec', 'KeepAliveProbes', 'NoDelay', 'Priority', 'DeferAcceptSec', 'ReceiveBuffer', 'SendBuffer', 'IPTOS', 'IPTTL', 'Mark', 'ReusePort', 'SmackLabel', 'SmackLabelIPIn', 'SmackLabelIPOut', 'SELinuxContextFromNet', 'PipeSize', 'MessageQueueMaxMessages', 'MessageQueueMessageSize', 'FreeBind', 'Transparent', 'Broadcast', 'PassCredentials', 'PassSecurity', 'TCPCongestion', 'ExecStartPre', 'ExecStartPost', 'ExecStopPre', 'ExecStopPost', 'TimeoutSec', 'Service', 'RemoveOnStop', 'Symlinks', 'FileDescriptorName', -- mount section options 'What', 'Where', 'Type', 'Options', 'SloppyOptions', 'DirectoryMode', 'TimeoutSec', -- path section options 'PathExists', 'PathExistsGlob', 'PathChanged', 'PathModified', 'DirectoryNotEmpty', 'Unit', 'MakeDirectory', 'DirectoryMode', -- timer section options 'OnActiveSec', 'OnBootSec', 'OnStartupSec', 'OnUnitActiveSec', 'OnUnitInactiveSec', 'OnCalendar', 'AccuracySec', 'RandomizedDelaySec', 'Unit', 'Persistent', 'WakeSystem', 'RemainAfterElapse', -- exec section options 'WorkingDirectory', 'RootDirectory', 'User', 'Group', 'SupplementaryGroups', 'Nice', 'OOMScoreAdjust', 'IOSchedulingClass', 'IOSchedulingPriority', 'CPUSchedulingPolicy', 'CPUSchedulingPriority', 'CPUSchedulingResetOnFork', 'CPUAffinity', 'UMask', 'Environment', 'EnvironmentFile', 'PassEnvironment', 'StandardInput', 'StandardOutput', 'StandardError', 'TTYPath', 'TTYReset', 'TTYVHangup', 'TTYVTDisallocate', 'SyslogIdentifier', 'SyslogFacility', 'SyslogLevel', 'SyslogLevelPrefix', 'TimerSlackNSec', 'LimitCPU', 'LimitFSIZE', 'LimitDATA', 'LimitSTACK', 'LimitCORE', 'LimitRSS', 'LimitNOFILE', 'LimitAS', 'LimitNPROC', 'LimitMEMLOCK', 'LimitLOCKS', 'LimitSIGPENDING', 'LimitMSGQUEUE', 'LimitNICE', 'LimitRTPRIO', 'LimitRTTIME', 'PAMName', 'CapabilityBoundingSet', 'AmbientCapabilities', 'SecureBits', 'Capabilities', 'ReadWriteDirectories', 'ReadOnlyDirectories', 'InaccessibleDirectories', 'PrivateTmp', 'PrivateDevices', 'PrivateNetwork', 'ProtectSystem', 'ProtectHome', 'MountFlags', 'UtmpIdentifier', 'UtmpMode', 'SELinuxContext', 'AppArmorProfile', 'SmackProcessLabel', 'IgnoreSIGPIPE', 'NoNewPrivileges', 'SystemCallFilter', 'SystemCallErrorNumber', 'SystemCallArchitectures', 'RestrictAddressFamilies', 'Personality', 'RuntimeDirectory', 'RuntimeDirectoryMode' } local preproc = token(l.PREPROCESSOR, option_word) -- Identifiers. local word = (l.alpha + '_') * (l.alnum + S('_.'))^0 local identifier = token(l.IDENTIFIER, word) -- Operators. local operator = token(l.OPERATOR, '=') M._rules = { {'whitespace', ws}, {'keyword', keyword}, {'string', string}, {'preproc', preproc}, {'identifier', identifier}, {'comment', comment}, {'number', number}, {'operator', operator}, } M._LEXBYLINE = true return M