aboutsummaryrefslogtreecommitdiff
path: root/main.go
blob: 10a0d637d85bc907d06a6bba5f6d8e9b0abde839 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"time"
)

const (
	bareosWorkDir   = "/var/lib/bareos"
	bareosStateFile = "bareos-fd.9102.state"
	baculaWorkDir   = "/var/lib/bacula"
	baculaStateFile = "bacula-fd.9102.state"
	spoolFile       = "bareos-zabbix-check.spool"
)

// We declare globally the variables that will hold the command line arguments
var (
	verbose   bool
	quiet     bool
	stateFile string
	workDir   string
)

func main() {
	var (
		info           os.FileInfo
		err            error
		successfulJobs jobs
		errorJobs      jobs
		spoolJobs      jobs
		jobName        string
		ts             uint64
		now            uint64
		errorString    string
		missingString  string
	)

	// command line arguments parsing
	flag.BoolVar(&verbose, "v", false, "Activates verbose debugging output, defaults to false.")
	flag.BoolVar(&quiet, "q", false, "Suppress all output, suitable to force a silent update of the spool file.")
	flag.StringVar(&stateFile, "f", "", "Force the state file to use, defaults to "+bareosStateFile+" if it exists else "+baculaStateFile+".")
	flag.StringVar(&workDir, "w", "", "Force the work directory to use, defaults to "+bareosWorkDir+" if it exists else "+baculaWorkDir+".")
	flag.Parse()

	// Determine the work directory to use.
	if workDir != "" {
		info, err = os.Stat(workDir)
		if os.IsNotExist(err) || !info.IsDir() {
			fmt.Printf("INFO Invalid work directory %s : it does not exist or is not a directory.\n", workDir)
			os.Exit(0)
		}
	} else {
		workDir = "/var/lib/bareos"
		info, err = os.Stat(workDir)
		if os.IsNotExist(err) || !info.IsDir() {
			workDir = "/var/lib/bacula"
			info, err := os.Stat(workDir)
			if os.IsNotExist(err) || !info.IsDir() {
				fmt.Println("INFO Could not find a suitable work directory. Is bareos or bacula installed?")
				os.Exit(0)
			}
		}
	}
	workDir = path.Clean(workDir)
	if verbose {
		log.Println("Setting work directory to ", workDir)
	}

	// Finds the state file to parse
	if stateFile != "" {
		stateFile = path.Join(workDir, stateFile)
		info, err = os.Stat(stateFile)
		if os.IsNotExist(err) || info.IsDir() {
			fmt.Printf("INFO The state file %s does not exist.\n", stateFile)
			os.Exit(0)
		}
	} else {
		stateFile = path.Join(workDir, bareosStateFile)
		info, err = os.Stat(stateFile)
		if os.IsNotExist(err) || info.IsDir() {
			stateFile = path.Join(workDir, baculaStateFile)
			info, err = os.Stat(stateFile)
			if os.IsNotExist(err) || info.IsDir() {
				fmt.Println("INFO Could not find a suitable state file. Has a job ever run?")
				os.Exit(0)
			}
		}
	}
	if verbose {
		log.Println("Using state file ", stateFile)
	}

	successfulJobs, errorJobs, err = parseStateFile()
	if err != nil {
		fmt.Print(err)
		os.Exit(0)
	}
	// We will check for errors in loading the spool file only if necessary. If all jobs ran successfully without errors in the state file and we manage to write
	// a new spool file without errors, then we will ignore any error here to avoid false positives during backup bootstrap
	spoolJobs, err = loadSpool()

	// if we have jobs in the spool we merge this list with successfull jobs from the state file
	if err == nil {
		for jobName, ts = range spoolJobs {
			var (
				current uint64
				ok      bool
			)
			current, ok = successfulJobs[jobName]
			if !ok || current < ts {
				successfulJobs[jobName] = ts
			}
		}
	}
	// we write this new spool
	if err2 := saveSpool(successfulJobs); err2 != nil {
		fmt.Printf("AVERAGE: Couldn't save spool : %s\n", err2)
		os.Exit(0)
	}

	// We build the error string listing the jobs in error
	for jobName, ts = range errorJobs {
		if errorString == "" {
			errorString = fmt.Sprintf("errors: %s", jobName)
		} else {
			errorString = fmt.Sprintf("%s, %s", errorString, jobName)
		}
	}
	now = uint64(time.Now().Unix())
	// Next we check if all jobs ran recently and build the missing string
	for jobName, ts = range successfulJobs {
		if ts < now-24*3600 {
			if missingString == "" {
				missingString = fmt.Sprintf("missing: %s", jobName)
			} else {
				missingString = fmt.Sprintf("%s, %s", missingString, jobName)
			}
		}
	}
	if errorString != "" || missingString != "" {
		fmt.Printf("AVERAGE: %s %s", errorString, missingString)
		if err != nil {
			fmt.Printf(" additionnal errors: %s", err)
		}
	} else {
		fmt.Printf("OK")
	}
}