Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ fun getAllAIGeneratedValidators(): Map<Validator, OptionValueInformation> {
Validator("config_parse_codel_bool", "QDISC_KIND_CODEL") to ConfigParseCodelBoolOptionValue() as OptionValueInformation,
Validator("config_parse_codel_u32", "QDISC_KIND_CODEL") to ConfigParseCodelU32OptionValue() as OptionValueInformation,
Validator("config_parse_collect_mode", "0") to ConfigParseCollectModeOptionValue() as OptionValueInformation,
Validator("config_parse_colon_separated_paths", "0") to ConfigParseColonSeparatedPathsOptionValue() as OptionValueInformation,
Validator("config_parse_cpu_quota", "0") to ConfigParseCpuQuotaOptionValue() as OptionValueInformation,
Validator("config_parse_cpuset_partition", "0") to ConfigParseCpusetPartitionOptionValue() as OptionValueInformation,
Validator("config_parse_delegate", "0") to ConfigParseDelegateOptionValue() as OptionValueInformation,
Expand All @@ -80,9 +81,12 @@ fun getAllAIGeneratedValidators(): Map<Validator, OptionValueInformation> {
Validator("config_parse_erspan_index", "0") to ConfigParseErspanIndexOptionValue() as OptionValueInformation,
Validator("config_parse_erspan_version", "0") to ConfigParseErspanVersionOptionValue() as OptionValueInformation,
Validator("config_parse_ets_u8", "QDISC_KIND_ETS") to ConfigParseEtsU8OptionValue() as OptionValueInformation,
Validator("config_parse_exec_cpu_affinity", "0") to ConfigParseExecCpuAffinityOptionValue() as OptionValueInformation,
Validator("config_parse_exec_cpu_sched_prio", "0") to ConfigParseExecCpuSchedPrioOptionValue() as OptionValueInformation,
Validator("config_parse_exec_input", "0") to ConfigParseExecInputOptionValue() as OptionValueInformation,
Validator("config_parse_exec_input_text", "0") to ConfigParseExecInputTextOptionValue() as OptionValueInformation,
Validator("config_parse_exec_io_priority", "0") to ConfigParseExecIoPriorityOptionValue() as OptionValueInformation,
Validator("config_parse_exec_memory_thp", "0") to ConfigParseExecMemoryThpOptionValue() as OptionValueInformation,
Validator("config_parse_exec_mount_propagation_flag", "0") to ConfigParseExecMountPropagationFlagOptionValue() as OptionValueInformation,
Validator("config_parse_exec_nice", "0") to ConfigParseExecNiceOptionValue() as OptionValueInformation,
Validator("config_parse_exec_oom_score_adjust", "0") to ConfigParseExecOomScoreAdjustOptionValue() as OptionValueInformation,
Expand Down Expand Up @@ -189,6 +193,7 @@ fun getAllAIGeneratedValidators(): Map<Validator, OptionValueInformation> {
Validator("config_parse_sr_iov_uint32", "0") to ConfigParseSrIovUint32OptionValue() as OptionValueInformation,
Validator("config_parse_sr_iov_vlan_proto", "0") to ConfigParseSrIovVlanProtoOptionValue() as OptionValueInformation,
Validator("config_parse_swap_priority", "0") to ConfigParseSwapPriorityOptionValue() as OptionValueInformation,
Validator("config_parse_syscall_archs", "0") to ConfigParseSyscallArchsOptionValue() as OptionValueInformation,
Validator("config_parse_syscall_errno", "0") to ConfigParseSyscallErrnoOptionValue() as OptionValueInformation,
Validator("config_parse_tasks_max", "0") to ConfigParseTasksMaxOptionValue() as OptionValueInformation,
Validator("config_parse_tbf_size", "QDISC_KIND_TBF") to ConfigParseTbfSizeOptionValue() as OptionValueInformation,
Expand All @@ -205,6 +210,7 @@ fun getAllAIGeneratedValidators(): Map<Validator, OptionValueInformation> {
Validator("config_parse_unit_mounts_for", "0") to ConfigParseUnitMountsForOptionValue() as OptionValueInformation,
Validator("config_parse_unit_slice", "0") to ConfigParseUnitSliceOptionValue() as OptionValueInformation,
Validator("config_parse_use_domains", "0") to ConfigParseUseDomainsOptionValue() as OptionValueInformation,
Validator("config_parse_user_group_strv_compat", "0") to ConfigParseUserGroupStrvCompatOptionValue() as OptionValueInformation,
Validator("config_parse_userns_chown", "0") to ConfigParseUsernsChownOptionValue() as OptionValueInformation,
Validator("config_parse_userns_ownership", "0") to ConfigParseUsernsOwnershipOptionValue() as OptionValueInformation,
Validator("config_parse_vlanid", "0") to ConfigParseVlanidOptionValue() as OptionValueInformation,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai

import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*

/**
* Validator for ExecSearchPath= (and similar colon-separated path lists).
*
* C function: config_parse_colon_separated_paths in src/core/load-fragment.c. Tokenizes with
* extract_first_word using ":" as the separator (no coalescing), expands specifiers via
* unit_path_printf, then requires each path to be absolute via path_simplify_and_warn with
* PATH_CHECK_ABSOLUTE.
*
* Grammar matches one or more absolute paths (each starting with "/") separated by ":".
* Per-path content excludes ":" since that's the separator. "%X" specifiers are allowed
* inline since the C parser expands them before the absolute-path check.
*/
class ConfigParseColonSeparatedPathsOptionValue : SimpleGrammarOptionValues(
"config_parse_colon_separated_paths",
SequenceCombinator(
RegexTerminal("/[^\\s:]*", "/[^\\s:]*"),
ZeroOrMore(SequenceCombinator(
LiteralChoiceTerminal(":"),
RegexTerminal("/[^\\s:]*", "/[^\\s:]*")
)),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai

import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*

/**
* Validator for CPUAffinity=.
*
* C function: config_parse_exec_cpu_affinity in src/core/load-fragment.c. Either the literal
* "numa" (use NUMA-derived CPU set) or a CPU set list parsed by the same code path as
* NUMAMask= / AllowedCPUs=: whitespace- or comma-separated integers and `N-M` ranges.
*
* The "numa" literal goes first explicitly; otherwise the CPU set grammar matches integers
* and ranges. Note the existing AllowedCpuSetOptionValue handles the same syntax for
* config_parse_unit_cpu_set — duplicated here as a grammar so it composes with the "numa"
* alternative.
*/
class ConfigParseExecCpuAffinityOptionValue : SimpleGrammarOptionValues(
"config_parse_exec_cpu_affinity",
SequenceCombinator(
AlternativeCombinator(
LiteralChoiceTerminal("numa"),
SequenceCombinator(
CPU_RANGE,
ZeroOrMore(SequenceCombinator(
AlternativeCombinator(WhitespaceTerminal(), LiteralChoiceTerminal(",")),
CPU_RANGE
))
)
),
EOF()
)
) {
companion object {
// `N` or `N-M` (each side a non-negative integer)
private val CPU_RANGE = RegexTerminal("[0-9]+(-[0-9]+)?", "[0-9]+(-[0-9]+)?")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai

import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*

/**
* Validator for StandardInput=.
*
* C function: config_parse_exec_input in src/core/load-fragment.c. Accepts:
* - "fd:NAME" — fdname_is_valid name (alphanumerics-ish, specifier-aware)
* - "file:/PATH" — absolute path (specifier-aware)
* - one of the bare enum names from exec_input_table in src/core/execute.c:
* null, tty, tty-force, tty-fail, socket, data
* (fd / file appear as prefixes above; their bare form isn't accepted)
*
* The structured branches (fd:/file:) come before the enum so that a literal "fd"/"file"
* (which aren't in the enum table anyway) wouldn't accidentally match. Order also matters
* because LiteralChoiceTerminal does first-match-by-length.
*/
class ConfigParseExecInputOptionValue : SimpleGrammarOptionValues(
"config_parse_exec_input",
SequenceCombinator(
AlternativeCombinator(
// fd:NAME (NAME is allowed to be empty per the C code's isempty check)
SequenceCombinator(LiteralChoiceTerminal("fd:"), RegexTerminal("\\S*", "\\S*")),
// file:/PATH
SequenceCombinator(LiteralChoiceTerminal("file:"), RegexTerminal("/\\S*", "/\\S*")),
LiteralChoiceTerminal("null", "tty", "tty-force", "tty-fail", "socket", "data")
),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai

import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*

/**
* Validator for MemoryTHP=.
*
* C function: config_parse_exec_memory_thp, expanded via DEFINE_CONFIG_PARSE_ENUM in
* src/core/load-fragment.c. Accepts exactly the entries of exec_memory_thp_table in
* src/core/execute.c.
*/
class ConfigParseExecMemoryThpOptionValue : SimpleGrammarOptionValues(
"config_parse_exec_memory_thp",
SequenceCombinator(
LiteralChoiceTerminal("inherit", "disable", "madvise", "system"),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai

import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*

/**
* Validator for SystemCallArchitectures=.
*
* C function: config_parse_syscall_archs in src/core/load-fragment.c → seccomp_arch_from_string
* in src/shared/seccomp-util.c. Whitespace-separated list of architecture names — the table
* below mirrors the streq checks in the C source, including arches gated by libseccomp build
* options (loongarch64, riscv64): they're always accepted by the validator since users won't
* know which seccomp build the host has, and the runtime check is what's authoritative anyway.
*/
class ConfigParseSyscallArchsOptionValue : SimpleGrammarOptionValues(
"config_parse_syscall_archs",
SequenceCombinator(
ARCH,
ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), ARCH)),
EOF()
)
) {
companion object {
private val ARCH = LiteralChoiceTerminal(
"native", "x86", "x86-64", "x32",
"arm", "arm64",
"loongarch64",
"mips", "mips64", "mips64-n32", "mips-le", "mips64-le", "mips64-le-n32",
"parisc", "parisc64",
"ppc", "ppc64", "ppc64-le",
"riscv64",
"s390", "s390x"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai

import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*

/**
* Validator for SupplementaryGroups= (and any other key parsed by config_parse_user_group_strv_compat).
*
* C function: config_parse_user_group_strv_compat in src/core/load-fragment.c → valid_user_group_name
* with VALID_USER_ALLOW_NUMERIC | VALID_USER_RELAX | VALID_USER_WARN. The relaxed mode accepts a wide
* range of user/group names because real-world auth stacks (SSSD, Samba, …) are permissive. Per
* src/basic/user-util.c the constraints are:
* - non-empty
* - no leading/trailing whitespace
* - no colons (/etc/passwd field separator), no slashes
* - no control characters
* - not fully numeric (UID is allowed via numeric parse, but mixed numeric+text isn't)
* - not "." / ".."
*
* Pre-expansion, words may also contain "%X" specifiers (unit_full_printf expands before the
* validity check), so the grammar tolerates "%" inline.
*/
class ConfigParseUserGroupStrvCompatOptionValue : SimpleGrammarOptionValues(
"config_parse_user_group_strv_compat",
SequenceCombinator(
NAME,
ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), NAME)),
EOF()
)
) {
companion object {
// No colon, slash, whitespace, or shell metacharacters. Length unbounded in relaxed mode.
// Negative lookahead excludes "." and ".." as literal entries.
private val NAME = RegexTerminal(
"(?!\\.{1,2}\\Z)[^\\s:/]+",
"(?!\\.{1,2}\\Z)[^\\s:/]+"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai

import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest
import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection
import org.junit.Test

class ConfigParseColonSeparatedPathsOptionValueTest : AbstractUnitFileTest() {

@Test
fun testValidValues() {
// language="unit file (systemd)"
val file = """
[Service]
ExecSearchPath=/usr/local/bin
ExecSearchPath=/usr/local/bin:/usr/bin
ExecSearchPath=/opt/app/bin:/usr/local/bin:/usr/bin:/bin
ExecSearchPath=/var/lib/%n
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(0, highlights)
}

@Test
fun testInvalidValues() {
// language="unit file (systemd)"
val file = """
[Service]
ExecSearchPath=<error descr="Invalid value">relative/bin</error>
ExecSearchPath=<error descr="Invalid value">/usr/bin relative/bin</error>
ExecSearchPath=<error descr="Invalid value">/usr/bin:relative/bin</error>
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(3, highlights)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai

import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest
import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection
import org.junit.Test

class ConfigParseExecCpuAffinityOptionValueTest : AbstractUnitFileTest() {

@Test
fun testValidValues() {
// language="unit file (systemd)"
val file = """
[Service]
CPUAffinity=numa
CPUAffinity=0
CPUAffinity=0-3
CPUAffinity=0,2,4
CPUAffinity=0-3 5
CPUAffinity=0-3,5,8-11
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(0, highlights)
}

@Test
fun testInvalidValues() {
// language="unit file (systemd)"
val file = """
[Service]
CPUAffinity=<error descr="Invalid value">all</error>
CPUAffinity=<error descr="Invalid value">numa 0</error>
CPUAffinity=<error descr="Invalid value">-1</error>
CPUAffinity=<error descr="Invalid value">0-</error>
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(4, highlights)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai

import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest
import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection
import org.junit.Test

class ConfigParseExecInputOptionValueTest : AbstractUnitFileTest() {

@Test
fun testValidValues() {
// language="unit file (systemd)"
val file = """
[Service]
StandardInput=null
StandardInput=tty
StandardInput=tty-force
StandardInput=tty-fail
StandardInput=socket
StandardInput=data
StandardInput=fd:stdin
StandardInput=fd:
StandardInput=file:/var/log/input.log
StandardInput=file:/dev/null
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(0, highlights)
}

@Test
fun testInvalidValues() {
// language="unit file (systemd)"
val file = """
[Service]
StandardInput=<error descr="Invalid value">stdin</error>
StandardInput=<error descr="Invalid value">file:relative/path</error>
StandardInput=<error descr="Invalid value">file:</error>
StandardInput=<error descr="Invalid value">FD:stdin</error>
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(4, highlights)
}
}
Loading
Loading