// question

Examine the logs from the Linux system for events related to the systemd timer activation. Identify any records indicating that a new timer unit was started and enabled, and determine which timer name was used.

Reference KQL answer and model-by-model comparison.

f4983098-bb13-44fb-9b2c-46149961807b
question id
6/23
models correct
1
valid answer
26%
success rate
// reference answer

Human-validated correct answer for this question.

art-timer.timer
// model comparison
ModelAnswerKQL queryStatusTriesTimeCost
gemini-2.5-flash-preview-04-17
no answer
no query generatedfail50.00s0.0322
gpt-35-turbo
no answer
no query generatedfail50.00s0.0113
gpt-4-turbo-2024-04-09
no answer
no query generatedfail50.00s0.2301
gpt-4.1
art-timer.timer
DeviceProcessEvents
| where FileName in ("systemctl", "systemd")
| where ProcessCommandLine has_any ("start", "enable") and ProcessCommandLine has ".timer"
| extend TimerUnit = extract(@"([\w-]+\.timer)", 1, ProcessCommandLine)
| where isnotempty(TimerUnit)
| project TimeGenerated, DeviceName, AccountName, ActionType, ProcessCommandLine, TimerUnit
| sort by TimeGenerated desc
pass29.05s0.0218
gpt-4.1-finetuned
no answer
no query generatedfail50.00s0.0483
gpt-4.1-mini
no answer
no query generatedfail50.00s0.0068
gpt-4.1-nano
no answer
no query generatedfail50.00s0.0013
gpt-4o
no answer
no query generatedfail50.00s0.0472
gpt-5-high
art-timer.timer
// Identify systemd timer units that were both enabled and started on Linux devices
// Time range is controlled by the caller

let timer_name_rx = @"(?i)([A-Za-z0-9._@-]+\.timer)\b";

// Limit to Linux devices (join via DeviceInfo to avoid OSPlatform on event tables)
let linux_devices =
    DeviceInfo
    | where OSPlatform == "Linux"
    | summarize arg_max(TimeGenerated, *) by DeviceId
    | project DeviceId;

// Starts: `systemctl start <name>.timer` OR `systemctl enable --now <name>.timer`
let starts =
    DeviceProcessEvents
    | where DeviceId in (linux_devices)
    | where FileName =~ "systemctl" or ProcessCommandLine has "systemctl"
    | where ProcessCommandLine has ".timer"
    | extend TimerName = tostring(extract(timer_name_rx, 1, ProcessCommandLine))
    | where isnotempty(TimerName)
    | where ProcessCommandLine matches regex @"(?i)\b(start|enable\s+--now)\b"
    | project DeviceId, DeviceName, TimeGenerated, TimerName,
              StartEvidence = ProcessCommandLine,
              StartAccount = AccountName;

// Enables via `systemctl enable <name>.timer`
let enables_systemctl =
    DeviceProcessEvents
    | where DeviceId in (linux_devices)
    | where FileName =~ "systemctl" or ProcessCommandLine has "systemctl"
    | where ProcessCommandLine has ".timer"
    | extend TimerName = tostring(extract(timer_name_rx, 1, ProcessCommandLine))
    | where isnotempty(TimerName)
    | where ProcessCommandLine matches regex @"(?i)\benable\b"
    | project DeviceId, DeviceName, TimeGenerated, TimerName,
              EnableEvidence = ProcessCommandLine,
              EnableAccount = AccountName;

// Enables evidenced by symlink creation in timers.target.wants
let enables_symlink =
    DeviceFileEvents
    | where DeviceId in (linux_devices)
    | where FolderPath has_any ("/etc/systemd/system/timers.target.wants/", "/usr/lib/systemd/system/timers.target.wants/", "/lib/systemd/system/timers.target.wants/", "/etc/systemd/user/timers.target.wants/")
    | where FileName endswith ".timer"
    | where ActionType in ("FileCreated", "FileRenamed")
    | project DeviceId, DeviceName, TimeGenerated,
              TimerName = FileName,
              EnableEvidence = strcat(FolderPath, FileName),
              EnableAccount = InitiatingProcessAccountName;

let enables = union enables_systemctl, enables_symlink;

// Summarize earliest enable and start per device+timer
let enables_s =
    enables
    | summarize EnableTime = min(TimeGenerated),
                EnableEvidenceList = make_set(EnableEvidence, 5),
                EnableAccounts = make_set(EnableAccount, 5)
      by DeviceId, DeviceName, TimerName;

let starts_s =
    starts
    | summarize StartTime = min(TimeGenerated),
                StartEvidenceList = make_set(StartEvidence, 5),
                StartAccounts = make_set(StartAccount, 5)
      by DeviceId, DeviceName, TimerName;

// Correlate timers that were both enabled and started
enables_s
| join kind=inner starts_s on DeviceId, TimerName
| project DeviceId,
          DeviceName = coalesce(DeviceName, DeviceName1),
          TimerName,
          EnableTime,
          StartTime,
          EnableAccounts,
          StartAccounts,
          EnableEvidenceList,
          StartEvidenceList
| order by StartTime asc
pass2166.14s0.1070
gpt-5-mini-high
art-timer.timer
union isfuzzy=true
(
    DeviceEvents
    | where SourceSystem == "Linux" or DeviceName has "linux" or tostring(AdditionalFields) has "systemd" or ProcessCommandLine has "systemctl" or ProcessCommandLine has "systemd"
),
(
    DeviceProcessEvents
    | where SourceSystem == "Linux" or DeviceName has "linux" or tostring(AdditionalFields) has "systemd" or ProcessCommandLine has "systemctl" or ProcessCommandLine has "systemd"
)
// build a searchable raw message from common fields and any embedded Message in AdditionalFields
| extend raw = strcat(
    tostring(AdditionalFields), " ",
    extractjson("$.Message", tostring(AdditionalFields)), " ",
    ProcessCommandLine, " ", FileName, " ", ActionType
)
// narrow to likely systemd timer related lines
| where raw has_any ("timer", ".timer", "systemd", "started", "starting", "enabled", "created symlink", "loaded", "activated", "triggered")
// try multiple extraction patterns for timer unit names
| extend TimerName = coalesce(
    extract(@"([\w\-\.\@]+\.timer)", 1, raw),
    extract(@"Unit\s+([\w\-\.\@]+)\.timer", 1, raw),
    extract(@"Started\s+([\w\-\.\@]+)\.timer", 1, raw),
    extract(@"Created symlink.*?([^/\s]+\.timer)", 1, raw)
)
| extend lraw = tolower(raw)
| extend Action = case(
    lraw contains "enabled" or lraw contains "created symlink" or lraw contains "symlink", "Enabled/Linked",
    lraw contains "started" or lraw contains "starting", "Started",
    lraw contains "loaded", "Loaded",
    lraw contains "activated" or lraw contains "triggered", "Activated",
    "Other"
)
| where isnotempty(TimerName)
| project TimeGenerated, DeviceName, DeviceId, TimerName, Action, raw, SourceSystem, ReportId
| sort by TimeGenerated desc
| distinct TimeGenerated, DeviceName, DeviceId, TimerName, Action, raw, SourceSystem, ReportId
| take 200
pass238.92s0.0119
gpt-5-mini-low
no answer
no query generatedfail50.00s0.0125
gpt-5-mini-medium
no answer
no query generatedfail50.00s0.0128
gpt-5-nano-high
no answer
no query generatedfail50.00s0.0093
gpt-5-nano-medium
no answer
no query generatedfail50.00s0.0075
grok-3-beta
no answer
no query generatedfail50.00s0.0925
grok-3-mini-beta
art-timer.timer
DeviceProcessEvents
| where ProcessCommandLine contains "systemctl" and (ProcessCommandLine contains "start" or ProcessCommandLine contains "enable") and ProcessCommandLine contains ".timer"
| extend Action = extract(@"systemctl\s+(start|enable)\s+([^\s]+)", 1, ProcessCommandLine)
| extend TimerName = extract(@"systemctl\s+(start|enable)\s+([^\s]+)", 2, ProcessCommandLine)
| where Action in~ ("start", "enable")
| summarize Actions = make_set(Action), LatestTime = max(TimeGenerated) by TimerName, DeviceName
| where array_length(set_intersect(dynamic(["start", "enable"]), Actions)) == 2
| project DeviceName, TimerName, Actions, TimeGenerated = LatestTime
| order by TimeGenerated desc
pass454.34s0.0064
o1-high
art-timer
let LinuxDevices = DeviceInfo
| where OSPlatform == "Linux"
| distinct DeviceId;
DeviceProcessEvents
| where DeviceId in (LinuxDevices)
| where ActionType == "ProcessCreated"
| where ProcessCommandLine has_any ("systemctl enable", "systemctl start")
| where ProcessCommandLine contains ".timer"
| extend TimerName = extract(@"systemctl\s+(?:enable|start)\s+([^\s]+)\.timer", 1, ProcessCommandLine)
| where isnotempty(TimerName)
| project TimeGenerated, DeviceName, ActionType, TimerName, ProcessCommandLine
| order by TimeGenerated desc
fail379.83s0.4884
o1-low
art-timer.timer
let LinuxDevices = DeviceInfo
| where OSPlatform == "Linux"
| distinct DeviceId, DeviceName;
LinuxDevices
| join kind=inner (
    DeviceProcessEvents
    | where ActionType == "ProcessCreated"
    | where ProcessCommandLine has "systemctl" and ProcessCommandLine has ".timer"
    // Extract the operation (enable/start) and the timer name from the command line
    | extend Operation = extract(@"systemctl\s+(enable|start)", 1, ProcessCommandLine)
    | extend TimerName = extract(@"systemctl\s+(?:enable|start)\s+([^\s]+\.timer)", 1, ProcessCommandLine)
    | where isnotempty(TimerName)
    | project DeviceId, TimeGenerated, Operation, TimerName, ProcessCommandLine
) on DeviceId
| project TimeGenerated, DeviceName, Operation, TimerName, ProcessCommandLine
pass483.66s0.6390
o3-high
no answer
no query generatedfail50.00s0.0973
o3-mini-high
no answer
no query generatedfail50.00s0.0406
o3-mini-low
no answer
no query generatedfail50.00s0.0394
o4-mini-high
art-timer.timer
let SyslogTimers = DeviceEvents
| where ActionType == "Syslog"
| extend SyslogMessage = tostring(AdditionalFields['SyslogMessage']), SyslogTag = tostring(AdditionalFields['SyslogTag'])
| where SyslogTag == "systemd" and SyslogMessage contains ".timer"
| extend Action = extract(@"systemd\[\d+\]:\s+(Started|Loaded|Enabled)", 1, SyslogMessage),
         TimerName = extract(@"systemd\[\d+\]:\s+(?:Started|Loaded|Enabled)\s+([^\s]+\.timer)", 1, SyslogMessage)
| where isnotempty(Action) and isnotempty(TimerName)
| project TimeGenerated, DeviceName, EventType = "Syslog", Action, TimerName;

let ProcTimers = DeviceProcessEvents
| where ProcessCommandLine has "systemctl" and ProcessCommandLine has ".timer"
| extend Action = extract(@"systemctl\s+(start|enable)", 1, ProcessCommandLine),
         TimerName = extract(@"systemctl\s+(?:start|enable)\s+([^\s]+\.timer)", 1, ProcessCommandLine)
| where isnotempty(Action) and isnotempty(TimerName)
| project TimeGenerated, DeviceName, EventType = "Process", Action, TimerName;

union SyslogTimers, ProcTimers
| sort by TimeGenerated desc
pass1109.04s0.0177
o4-mini-low
no answer
no query generatedfail50.00s0.0593