Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,47 @@ fun getAllAIGeneratedValidators(): Map<Validator, OptionValueInformation> {
Validator("config_parse_socket_service", "0") to ConfigParseSocketServiceOptionValue() as OptionValueInformation,
Validator("config_parse_service_sockets", "0") to ConfigParseServiceSocketsOptionValue() as OptionValueInformation,

// Batch 2026-10: netdev-link
// systemd-networkd .netdev (src/network/netdev/) and udev .link (src/udev/net/) settings, pinned to
// systemd 0f0682b773. Every key using each (parser, ltype) pair was checked in the gperf files; where
// a parser branches on the key name (vxlan_address) the grammar is the union and says so.
Validator("config_parse_badadv_bandwidth", "0") to ConfigParseBadadvBandwidthOptionValue() as OptionValueInformation,
Validator("config_parse_vxlan_address", "0") to ConfigParseVxlanAddressOptionValue() as OptionValueInformation,
Validator("config_parse_wireguard_peer_key_file", "0") to ConfigParseWireguardPeerKeyFileOptionValue() as OptionValueInformation,
Validator("config_parse_vlanprotocol", "0") to ConfigParseVlanprotocolOptionValue() as OptionValueInformation,
Validator("config_parse_vlan_qos_maps", "0") to ConfigParseVlanQosMapsOptionValue() as OptionValueInformation,
Validator("config_parse_macsec_key_id", "0") to ConfigParseMacsecKeyIdOptionValue() as OptionValueInformation,
Validator("config_parse_macsec_key_file", "0") to ConfigParseMacsecKeyFileOptionValue() as OptionValueInformation,
Validator("config_parse_cpu_set", "0") to ConfigParseCpuSetOptionValue() as OptionValueInformation,
Validator("config_parse_wol_password", "0") to ConfigParseWolPasswordOptionValue() as OptionValueInformation,
Validator("config_parse_wireguard_route_table", "0") to ConfigParseWireguardRouteTableOptionValue() as OptionValueInformation,
Validator("config_parse_wireguard_private_key_file", "0") to ConfigParseWireguardPrivateKeyFileOptionValue() as OptionValueInformation,
Validator("config_parse_wireguard_private_key", "0") to ConfigParseWireguardPrivateKeyOptionValue() as OptionValueInformation,
Validator("config_parse_wireguard_peer_route_table", "0") to ConfigParseWireguardPeerRouteTableOptionValue() as OptionValueInformation,
Validator("config_parse_wireguard_endpoint", "0") to ConfigParseWireguardEndpointOptionValue() as OptionValueInformation,
Validator("config_parse_wireguard_allowed_ips", "0") to ConfigParseWireguardAllowedIpsOptionValue() as OptionValueInformation,
Validator("config_parse_vrf_table", "0") to ConfigParseVrfTableOptionValue() as OptionValueInformation,
Validator("config_parse_udev_property", "0") to ConfigParseUdevPropertyOptionValue() as OptionValueInformation,
Validator("config_parse_rps_cpu_mask", "0") to ConfigParseRpsCpuMaskOptionValue() as OptionValueInformation,
Validator("config_parse_port_range", "0") to ConfigParsePortRangeOptionValue() as OptionValueInformation,
Validator("config_parse_port", "0") to ConfigParsePortOptionValue() as OptionValueInformation,
Validator("config_parse_mac_address_policy", "0") to ConfigParseMacAddressPolicyOptionValue() as OptionValueInformation,
Validator("config_parse_l2tp_tunnel_remote_address", "0") to ConfigParseL2tpTunnelRemoteAddressOptionValue() as OptionValueInformation,
Validator("config_parse_l2tp_tunnel_local_address", "0") to ConfigParseL2tpTunnelLocalAddressOptionValue() as OptionValueInformation,
Validator("config_parse_l2tp_session_name", "0") to ConfigParseL2tpSessionNameOptionValue() as OptionValueInformation,
Validator("config_parse_irq_affinity_policy", "0") to ConfigParseIrqAffinityPolicyOptionValue() as OptionValueInformation,
Validator("config_parse_irq_affinity_numa", "0") to ConfigParseIrqAffinityNumaOptionValue() as OptionValueInformation,
Validator("config_parse_ipv6_flowlabel", "0") to ConfigParseIpv6FlowlabelOptionValue() as OptionValueInformation,
Validator("config_parse_ipoib_pkey", "0") to ConfigParseIpoibPkeyOptionValue() as OptionValueInformation,
Validator("config_parse_ifnames", "IFNAME_VALID_ALTERNATIVE") to ConfigParseIfnamesOptionValue() as OptionValueInformation,
Validator("config_parse_ifalias", "0") to ConfigParseIfaliasOptionValue() as OptionValueInformation,
Validator("config_parse_geneve_address", "0") to ConfigParseGeneveAddressOptionValue() as OptionValueInformation,
Validator("config_parse_default_port_vlanid", "0") to ConfigParseDefaultPortVlanidOptionValue() as OptionValueInformation,
Validator("config_parse_advertise", "0") to ConfigParseAdvertiseOptionValue() as OptionValueInformation,
// RxCoalesceCQENSec= registers config_parse_coalesce_sec with ltype `true` (parse_nsec instead of
// parse_sec); the `0` entry above (ConfigParseCoalesceSecOptionValue) covers the parse_sec keys.
Validator("config_parse_coalesce_sec", "true") to ConfigParseCoalesceNsecOptionValue() as OptionValueInformation,

)

return allValidators
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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.EOF
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.FlexibleLiteralChoiceTerminal
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.WhitespaceTerminal
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.ZeroOrMore

/*
* [Link] Advertise= and [EnergyEfficientEthernet] LinkMode= in a .link file.
*
* man https://www.freedesktop.org/software/systemd/man/latest/systemd.link.html#Advertise=
* parser https://github.com/systemd/systemd/blob/0f0682b773/src/shared/ethtool-util.c#L1125-L1177 config_parse_advertise (0)
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/shared/ethtool-util.c#L162-L168 ethtool_link_mode_bit_table (DEFINE_STRING_TABLE_LOOKUP)
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/shared/ethtool-link-mode.py (generates the table from enum ethtool_link_mode_bit_indices)
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/include/uapi/linux/ethtool.h#L1966-L2109 enum ethtool_link_mode_bit_indices
* linux https://man7.org/linux/man-pages/man8/ethtool.8.html
*/

/**
* The link-mode names ethtool-link-mode.py derives from the vendored kernel header: each
* `ETHTOOL_LINK_MODE_<name>_BIT` lower-cased with `_` turned into `-`, and `autoneg` renamed
* `autonegotiation`. 125 names at 0f0682b773.
*/
private val LINK_MODE = FlexibleLiteralChoiceTerminal(
"10baset-half", "10baset-full", "100baset-half", "100baset-full", "1000baset-half",
"1000baset-full", "autonegotiation", "tp", "aui", "mii", "fibre", "bnc", "10000baset-full",
"pause", "asym-pause", "2500basex-full", "backplane", "1000basekx-full", "10000basekx4-full",
"10000basekr-full", "10000baser-fec", "20000basemld2-full", "20000basekr2-full",
"40000basekr4-full", "40000basecr4-full", "40000basesr4-full", "40000baselr4-full",
"56000basekr4-full", "56000basecr4-full", "56000basesr4-full", "56000baselr4-full",
"25000basecr-full", "25000basekr-full", "25000basesr-full", "50000basecr2-full",
"50000basekr2-full", "100000basekr4-full", "100000basesr4-full", "100000basecr4-full",
"100000baselr4-er4-full", "50000basesr2-full", "1000basex-full", "10000basecr-full",
"10000basesr-full", "10000baselr-full", "10000baselrm-full", "10000baseer-full",
"2500baset-full", "5000baset-full", "fec-none", "fec-rs", "fec-baser", "50000basekr-full",
"50000basesr-full", "50000basecr-full", "50000baselr-er-fr-full", "50000basedr-full",
"100000basekr2-full", "100000basesr2-full", "100000basecr2-full", "100000baselr2-er2-fr2-full",
"100000basedr2-full", "200000basekr4-full", "200000basesr4-full", "200000baselr4-er4-fr4-full",
"200000basedr4-full", "200000basecr4-full", "100baset1-full", "1000baset1-full",
"400000basekr8-full", "400000basesr8-full", "400000baselr8-er8-fr8-full", "400000basedr8-full",
"400000basecr8-full", "fec-llrs", "100000basekr-full", "100000basesr-full",
"100000baselr-er-fr-full", "100000basecr-full", "100000basedr-full", "200000basekr2-full",
"200000basesr2-full", "200000baselr2-er2-fr2-full", "200000basedr2-full", "200000basecr2-full",
"400000basekr4-full", "400000basesr4-full", "400000baselr4-er4-fr4-full", "400000basedr4-full",
"400000basecr4-full", "100basefx-half", "100basefx-full", "10baset1l-full", "800000basecr8-full",
"800000basekr8-full", "800000basedr8-full", "800000basedr8-2-full", "800000basesr8-full",
"800000basevr8-full", "10baset1s-full", "10baset1s-half", "10baset1s-p2mp-half",
"10baset1brr-full", "200000basecr-full", "200000basekr-full", "200000basedr-full",
"200000basedr-2-full", "200000basesr-full", "200000basevr-full", "400000basecr2-full",
"400000basekr2-full", "400000basedr2-full", "400000basedr2-2-full", "400000basesr2-full",
"400000basevr2-full", "800000basecr4-full", "800000basekr4-full", "800000basedr4-full",
"800000basedr4-2-full", "800000basesr4-full", "800000basevr4-full", "1600000basecr8-full",
"1600000basekr8-full", "1600000basedr8-full", "1600000basedr8-2-full"
)

/**
* Validator for `[Link] Advertise=` and `[EnergyEfficientEthernet] LinkMode=` (.link).
*
* C function: config_parse_advertise. A whitespace-separated list (extract_first_word, no unquoting) of
* ethtool link-mode names, each looked up exactly (case-sensitive) in ethtool_link_mode_bit_table —
* speed/duplex modes like `1000baset-full` as well as the non-speed bits (`autonegotiation`, `pause`,
* `asym-pause`, `tp`, `fibre`, `fec-rs`, …).
*
* The table is generated at build time from the kernel's enum ethtool_link_mode_bit_indices in
* systemd's vendored copy of linux/ethtool.h (src/include/uapi, which meson puts first on the include
* path). That is wider than the ethtool-link-mode.xml table in the plugin's generated man data, which
* lists 102 names; this follows the header the C is built against. An unknown name is logged and
* skipped by udev; an empty assignment clears the list.
*/
class ConfigParseAdvertiseOptionValue : SimpleGrammarOptionValues(
"config_parse_advertise",
SequenceCombinator(LINK_MODE, ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), LINK_MODE)), EOF())
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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.EOF
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.NetdevLinkCheckedSpan
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.netdevLinkParseSizeSi
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.siSize
import java.math.BigInteger

/*
* [BatmanAdvanced] GatewayBandwidthDown= / GatewayBandwidthUp= in a .netdev file.
*
* man https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#GatewayBandwidthDown=
* parser https://github.com/systemd/systemd/blob/0f0682b773/src/network/netdev/batadv.c#L53-L89 config_parse_badadv_bandwidth (0)
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/network/netdev/batadv.c#L81-L84 (the 100 kbit uint32_t range warning)
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/basic/parse-util.c#L157-L283 parse_size (base 1000)
* tests https://github.com/systemd/systemd/blob/0f0682b773/src/test/test-parse-util.c#L178 TEST(parse_size_si)
* tests https://github.com/systemd/systemd/blob/0f0682b773/test/test-network/conf/25-batadv.netdev#L14-L15
*/

/**
* Validator for `[BatmanAdvanced] GatewayBandwidthDown=` and `GatewayBandwidthUp=` (.netdev), plus the
* misspelled `GatewayBandwithDown=` / `GatewayBandwithUp=` that systemd still accepts as aliases.
*
* C function: config_parse_badadv_bandwidth, which hands the whole value to parse_size(rvalue, 1000, …):
* a number with an optional case-sensitive SI suffix (`K`, `M`, `G`, `T`, `P`, `E`, `B`), a
* fraction allowed, and several elements with strictly descending suffixes (`1G 500M`) summed. See
* [siSize].
*
* The total is stored in units of 100 kbit in a uint32_t, so a total of 4294967296 × 100000
* (429496729600000, e.g. `12P` or `3E 2P`) or more is flagged. systemd logs that case ("is outside of
* 0...429496729500000 range") and then stores the total truncated to 32 bits, which is never what was
* meant, so it is treated as invalid here. Totals that overflow parse_size itself (`20E`) are refused
* outright. Confirmed against systemd's parser built at 0f0682b773: `429496729599999` is silent,
* `429496729600000` warns. Any other invalid value is logged and ignored by systemd.
*
* There is no isempty() branch, so an empty `GatewayBandwidthDown=` or `GatewayBandwidthUp=` is a parse error in systemd (logged and ignored), not
* a reset. The plugin never runs a grammar on an empty value, so it is not flagged here.
*/
class ConfigParseBadadvBandwidthOptionValue : SimpleGrammarOptionValues(
"config_parse_badadv_bandwidth",
SequenceCombinator(
NetdevLinkCheckedSpan(siSize(), "total < 429496729600000") { text ->
netdevLinkParseSizeSi(text)?.let { it < BATADV_BANDWIDTH_LIMIT } == true
},
EOF()
)
)

/** `k/1000/100 > UINT32_MAX` (batadv.c#L81) first holds at this total. */
private val BATADV_BANDWIDTH_LIMIT: BigInteger = BigInteger.valueOf(4_294_967_296L) * BigInteger.valueOf(100_000L)
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.EOF
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.NSEC_DURATION_FINITE
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator

/*
* [Link] RxCoalesceCQENSec= in a .link file.
*
* man https://www.freedesktop.org/software/systemd/man/latest/systemd.link.html#RxCoalesceCQENSec=
* parser https://github.com/systemd/systemd/blob/0f0682b773/src/shared/ethtool-util.c#L1370-L1415 config_parse_coalesce_sec (ltype "true", i.e. nsec)
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/basic/time-util.c#L1374-L1466 parse_nsec
* tests https://github.com/systemd/systemd/blob/0f0682b773/src/test/test-time-util.c#L141 TEST(parse_nsec)
*/

/**
* Validator for `[Link] RxCoalesceCQENSec=` (.link) — the one key that registers config_parse_coalesce_sec
* with ltype `true`, which switches the parser from parse_sec() to parse_nsec().
*
* parse_nsec reads one or more `number[unit]` elements, a bare number meaning nanoseconds (`500`,
* `1us`, `2.5`, `1s500ms`, `.7`). See [NSEC_DURATION_FINITE].
*
* `infinity` parses, but config_parse_coalesce_sec then refuses any result above UINT32_MAX ("Too large
* … value"), and NSEC_INFINITY is UINT64_MAX — so `infinity` is never a usable value here and is
* rejected. The same UINT32_MAX ceiling (about 4.29 seconds in nanoseconds) applies to every finite value
* too, but that compares the computed sum and is not modelled. An invalid value is logged and ignored.
*/
class ConfigParseCoalesceNsecOptionValue : SimpleGrammarOptionValues(
"config_parse_coalesce_sec",
SequenceCombinator(NSEC_DURATION_FINITE, EOF())
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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.CPU_SET
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.EOF
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator

/*
* [Link] IRQAffinity= (.link) and [Exec] CPUAffinity= (.nspawn).
*
* man https://www.freedesktop.org/software/systemd/man/latest/systemd.link.html#IRQAffinity=
* man https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#CPUAffinity=
* parser https://github.com/systemd/systemd/blob/0f0682b773/src/shared/cpu-set-util.c#L220-L307 config_parse_cpu_set (0)
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/basic/parse-util.c#L305-L340 parse_range
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/basic/parse-util.c#L429-L478 safe_atou_full (base 0)
* leaf https://github.com/systemd/systemd/blob/0f0682b773/src/shared/cpu-set-util.c#L186-L206 cpu_set_add_range (CPU_SET_MAX_NCPU)
* tests https://github.com/systemd/systemd/blob/0f0682b773/src/test/test-cpu-set-util.c#L36 TEST(parse_cpu_set)
* linux https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html
*/

/**
* Validator for `[Link] IRQAffinity=` (.link) and `[Exec] CPUAffinity=` (.nspawn).
*
* C function: config_parse_cpu_set. CPU indices and `N-M` ranges, separated by any mix of whitespace
* and commas (separators coalesce; leading/trailing ones are fine), each word optionally quoted. Each
* bound is a safe_atou() number (so hex works) below CPU_SET_MAX_NCPU = 8192. See [CPU_SET].
*
* Lenient on one point: a reversed range such as `3-0` is rejected by systemd after parsing (it
* compares the numbers); that is not modelled. An empty assignment clears the set.
*/
class ConfigParseCpuSetOptionValue : SimpleGrammarOptionValues(
"config_parse_cpu_set",
SequenceCombinator(CPU_SET, EOF())
)
Loading
Loading