146 lines
3.5 KiB
Go

package collector
import (
"encoding/xml"
"log"
"os"
"os/exec"
"strings"
)
type CronJob struct {
Source string `json:"source"` // "system" oder "opnsense"
Enabled bool `json:"enabled"`
Minutes string `json:"minutes"`
Hours string `json:"hours"`
Days string `json:"days"`
Months string `json:"months"`
Weekdays string `json:"weekdays"`
Command string `json:"command"`
Parameters string `json:"parameters,omitempty"`
Who string `json:"who,omitempty"`
Description string `json:"description,omitempty"`
UUID string `json:"uuid,omitempty"`
Origin string `json:"origin,omitempty"`
Schedule string `json:"schedule"` // Menschenlesbar: "*/4 * * * *"
}
func CollectCron() []CronJob {
var jobs []CronJob
// 1. System-Crontab (crontab -l)
sysJobs := collectSystemCrontab()
jobs = append(jobs, sysJobs...)
// 2. OPNsense config.xml Cron-Jobs
opnJobs := collectOPNsenseCron()
jobs = append(jobs, opnJobs...)
log.Printf("Cron: %d Jobs gesammelt (%d System, %d OPNsense)",
len(jobs), len(sysJobs), len(opnJobs))
return jobs
}
func collectSystemCrontab() []CronJob {
out, err := exec.Command("/bin/sh", "-c", "/usr/bin/crontab -l").Output()
output := string(out)
if err != nil {
return nil
}
var jobs []CronJob
for _, line := range strings.Split(output, "\n") {
line = strings.TrimSpace(line)
// Kommentare, leere Zeilen, Variablen ueberspringen
if line == "" || strings.HasPrefix(line, "#") || strings.Contains(line, "=") {
continue
}
// Crontab-Format: min hour day month weekday command
fields := strings.Fields(line)
if len(fields) < 6 {
continue
}
command := strings.Join(fields[5:], " ")
// Klammern und Redirects bereinigen
command = strings.TrimPrefix(command, "(")
command = strings.TrimSuffix(command, ") > /dev/null")
jobs = append(jobs, CronJob{
Source: "system",
Enabled: true,
Minutes: fields[0],
Hours: fields[1],
Days: fields[2],
Months: fields[3],
Weekdays: fields[4],
Command: command,
Schedule: strings.Join(fields[:5], " "),
})
}
return jobs
}
func collectOPNsenseCron() []CronJob {
data, err := os.ReadFile("/conf/config.xml")
if err != nil {
return nil
}
type cronJob struct {
UUID string `xml:"uuid,attr"`
Origin string `xml:"origin"`
Enabled string `xml:"enabled"`
Minutes string `xml:"minutes"`
Hours string `xml:"hours"`
Days string `xml:"days"`
Months string `xml:"months"`
Weekdays string `xml:"weekdays"`
Who string `xml:"who"`
Command string `xml:"command"`
Parameters string `xml:"parameters"`
Description string `xml:"description"`
}
type opnsenseCron struct {
Jobs []cronJob `xml:"OPNsense>cron>jobs>job"`
}
var cfg opnsenseCron
if err := xml.Unmarshal(data, &cfg); err != nil {
log.Printf("Cron XML parsen fehlgeschlagen: %v", err)
return nil
}
var jobs []CronJob
for _, j := range cfg.Jobs {
schedule := strings.Join([]string{j.Minutes, j.Hours, j.Days, j.Months, j.Weekdays}, " ")
cmd := j.Command
if j.Parameters != "" {
cmd += " " + j.Parameters
}
jobs = append(jobs, CronJob{
Source: "opnsense",
Enabled: j.Enabled == "1",
Minutes: j.Minutes,
Hours: j.Hours,
Days: j.Days,
Months: j.Months,
Weekdays: j.Weekdays,
Command: cmd,
Who: j.Who,
Description: strings.TrimSpace(j.Description),
UUID: j.UUID,
Origin: j.Origin,
Schedule: schedule,
})
}
return jobs
}