Effective config: conntrack as flag; avoid key-only directives for empty non-flag values

- Model conntrack as Flag (parser, config bool, cache, registry FlagValueDisplay)
- Apply: for non-flag options with empty/invalid value, remove existing line
  and never write key-only directive (was producing malformed option= without value)
This commit is contained in:
2026-03-02 22:52:27 +10:00
parent 5f728900dc
commit baaba38e23
6 changed files with 23 additions and 8 deletions
@@ -211,6 +211,11 @@
word-break: break-word;
}
/* Row-level ok/cancel (and revert) for multi-value: right-align on the row below the list */
::deep .ec-field .ec-field-value-multi + .status-badge-inline {
margin-left: auto;
}
::deep .ec-field-value-list {
margin: 0;
padding-left: 1.25rem;
@@ -286,7 +286,7 @@ public static class EffectiveConfigParserBehaviorMap
[DnsmasqConfKeys.LogDhcp] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.KeepInForeground] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.NoDaemon] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.Conntrack] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.Conntrack] = EffectiveConfigParserBehavior.Flag,
};
/// <summary>Returns LastWins, Flag, or Multi for the given option name; defaults to LastWins if unknown.</summary>
@@ -391,8 +391,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var dhcpScriptPath = string.IsNullOrWhiteSpace(dhcpScriptVal) ? null : DnsmasqConfIncludeParser.ResolvePath(dhcpScriptVal?.Trim(), dhcpScriptDir);
var (mxTargetVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.MxTarget);
var mxTarget = string.IsNullOrWhiteSpace(mxTargetVal) ? null : mxTargetVal.Trim();
var (conntrackVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Conntrack);
var conntrack = string.IsNullOrWhiteSpace(conntrackVal) ? null : conntrackVal.Trim();
var conntrack = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Conntrack);
return new EffectiveDnsmasqConfig(
noHosts, addnHosts, hostsdirPath,
@@ -535,7 +534,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var (_, fastDnsRetrySource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.FastDnsRetry, managedFilePath);
var (_, dhcpScriptPathSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpScript, managedFilePath);
var (_, mxTargetSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.MxTarget, managedFilePath);
var (_, conntrackSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Conntrack, managedFilePath);
var (_, conntrackSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Conntrack, managedFilePath);
return new EffectiveConfigSources(
noHostsSource, addnHostsWithSource.Select(t => new PathWithSource(t.Path, t.Source)).ToList(), hostsdirPathSource,
@@ -718,7 +717,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
LogFacility: null, LogQueries: null, AuthTtl: null, EdnsPacketMax: null, QueryPort: null, PortLimit: null, MinPort: null, MaxPort: null, LogAsync: null, LocalService: null,
DhcpLeaseMax: null, NegTtl: null, MaxTtl: null, MaxCacheTtl: null, MinCacheTtl: null, DhcpTtl: null,
TftpRootPath: null, PxePrompt: null, EnableDbus: null, EnableUbus: null, FastDnsRetry: null,
DhcpScriptPath: null, MxTarget: null, Conntrack: null
DhcpScriptPath: null, MxTarget: null, Conntrack: false
);
private static EffectiveConfigSources CreateDefaultEffectiveConfigSources() =>
@@ -271,13 +271,23 @@ public class DnsmasqConfigService : IDnsmasqConfigService
if (idx >= 0) list.RemoveAt(idx);
continue;
}
if (!isFlag)
{
var v = ToConfValue(c.NewValue);
if (string.IsNullOrWhiteSpace(v))
{
// Non-flag option with no value: remove existing line if any; never write key-only (malformed).
if (idx >= 0) list.RemoveAt(idx);
continue;
}
}
string rawLine;
if (isFlag)
rawLine = confKey;
else
{
var v = ToConfValue(c.NewValue);
rawLine = string.IsNullOrEmpty(v) ? confKey : confKey + "=" + v;
var v = ToConfValue(c.NewValue).Trim();
rawLine = confKey + "=" + v;
}
var newLine = new OtherLine { LineNumber = maxLineNumber + 1, RawLine = rawLine };
maxLineNumber++;
@@ -75,6 +75,7 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.LogDhcp);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.KeepInForeground);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.NoDaemon);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.Conntrack);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDnssec, DnsmasqConfKeys.ProxyDnssec);
// log-queries: dropdown (Off / On / extra / proto / auth).
@@ -139,7 +139,7 @@ public record EffectiveDnsmasqConfig(
string? FastDnsRetry,
string? DhcpScriptPath,
string? MxTarget,
string? Conntrack
bool Conntrack
)
{
/// <summary>