@@ -3,13 +3,8 @@ package snclient
33import (
44 "cmp"
55 "context"
6- _ "embed"
76 "fmt"
8- "os"
9- "regexp"
10- "runtime"
117 "slices"
12- "strings"
138
149 "github.com/consol-monitoring/snclient/pkg/convert"
1510)
@@ -18,17 +13,6 @@ func init() {
1813 AvailableChecks ["check_os_updates" ] = CheckEntry {"check_os_updates" , NewCheckOSUpdates }
1914}
2015
21- //go:embed embed/scripts/windows/check_os_updates.ps1
22- var checkOSupdatesPS1 string
23-
24- var (
25- reAPTSecurity = regexp .MustCompile (`(Debian-Security:|Ubuntu:[^/]*/[^-]*-security)` )
26- reAPTEntry = regexp .MustCompile (`^Inst\s+(\S+)\s+\[([^\[]+)\]\s+\((\S+)\s+(.*)\s+\[(\S+)\]\)` )
27- reYUMEntry = regexp .MustCompile (`^(\S+)\.(\S+)\s+(\S+)\s+(\S+)` )
28- reOSXEntry = regexp .MustCompile (`^\*\s+Label:\s+(.*)$` )
29- reOSXDetails = regexp .MustCompile (`^Title:.*Version:\s(\S+), ` )
30- )
31-
3216type CheckOSUpdates struct {
3317 snc * Agent
3418 system string
@@ -81,41 +65,10 @@ If you only want to be notified about security related updates:
8165func (l * CheckOSUpdates ) Check (ctx context.Context , snc * Agent , check * CheckData , _ []Argument ) (* CheckResult , error ) {
8266 l .snc = snc
8367
84- found := 0
85- ok , err := l .addAPT (ctx , check )
86- if err != nil {
87- return nil , err
88- }
89- if ok {
90- found ++
91- }
92-
93- ok , err = l .addYUM (ctx , check )
94- if err != nil {
95- return nil , err
96- }
97- if ok {
98- found ++
99- }
100-
101- ok , err = l .addOSX (ctx , check )
102- if err != nil {
103- return nil , err
104- }
105- if ok {
106- found ++
107- }
68+ addedOsBackendCount , osBackendAddErr := l .addOSBackends (ctx , check )
10869
109- ok , err = l .addWindows (ctx , check )
110- if err != nil {
111- return nil , err
112- }
113- if ok {
114- found ++
115- }
116-
117- if found == 0 {
118- return nil , fmt .Errorf ("no suitable package system found, supported systems are apt, yum, osx and windows" )
70+ if addedOsBackendCount == 0 {
71+ return nil , fmt .Errorf ("no suitable package system found, supported systems are apt, yum, osx and windows. found errors: %w" , osBackendAddErr )
11972 }
12073
12174 count := 0
@@ -173,250 +126,3 @@ func (l *CheckOSUpdates) Check(ctx context.Context, snc *Agent, check *CheckData
173126
174127 return check .Finalize ()
175128}
176-
177- // get packages from apt
178- func (l * CheckOSUpdates ) addAPT (ctx context.Context , check * CheckData ) (bool , error ) {
179- switch l .system {
180- case "auto" :
181- if runtime .GOOS != "linux" {
182- return false , nil
183- }
184- _ , err := os .Stat ("/usr/bin/apt-get" )
185- if os .IsNotExist (err ) {
186- return false , nil
187- }
188- case "apt" :
189- default :
190- return false , nil
191- }
192-
193- if l .update {
194- output , stderr , rc , err := l .snc .execCommand (ctx , "apt-get update" , DefaultCmdTimeout )
195- if err != nil {
196- return true , fmt .Errorf ("apt-get update failed: %s\n %s" , err .Error (), stderr )
197- }
198- if rc != 0 {
199- return true , fmt .Errorf ("apt-get update failed: %s\n %s" , output , stderr )
200- }
201- }
202-
203- output , stderr , rc , err := l .snc .execCommand (ctx , "apt-get upgrade -o 'Debug::NoLocking=true' -s -qq" , DefaultCmdTimeout )
204- if err != nil {
205- return true , fmt .Errorf ("apt-get upgrade failed: %s\n %s" , err .Error (), stderr )
206- }
207- if rc != 0 {
208- return true , fmt .Errorf ("apt-get upgrade failed: %s\n %s" , output , stderr )
209- }
210-
211- l .parseAPT (output , check )
212-
213- return true , nil
214- }
215-
216- func (l * CheckOSUpdates ) parseAPT (output string , check * CheckData ) {
217- for line := range strings .SplitSeq (output , "\n " ) {
218- matches := reAPTEntry .FindStringSubmatch (line )
219- security := "0"
220- if reAPTSecurity .MatchString (line ) {
221- security = "1"
222- }
223- if len (matches ) < 5 {
224- continue
225- }
226- check .listData = append (check .listData , map [string ]string {
227- "security" : security ,
228- "package" : matches [1 ],
229- "version" : matches [3 ],
230- "old_version" : matches [2 ],
231- "repository" : matches [4 ],
232- "arch" : matches [5 ],
233- })
234- }
235- }
236-
237- // get packages from yum
238- func (l * CheckOSUpdates ) addYUM (ctx context.Context , check * CheckData ) (bool , error ) {
239- switch l .system {
240- case "auto" :
241- if runtime .GOOS != "linux" {
242- return false , nil
243- }
244- _ , err := os .Stat ("/usr/bin/yum" )
245- if os .IsNotExist (err ) {
246- return false , nil
247- }
248- case "yum" :
249- default :
250- return false , nil
251- }
252-
253- yumOpts := " -C"
254- if l .update {
255- yumOpts = ""
256- }
257-
258- output , stderr , exitCode , err := l .snc .execCommand (ctx , "yum check-update --security -q" + yumOpts , DefaultCmdTimeout )
259- if err != nil {
260- return true , fmt .Errorf ("yum check-update failed: %s\n %s" , err .Error (), stderr )
261- }
262- if exitCode != 0 && exitCode != 100 {
263- return true , fmt .Errorf ("yum check-update failed: %s\n %s" , output , stderr )
264- }
265- packageLookup := l .parseYUM (output , "1" , check , nil )
266-
267- output , stderr , exitCode , err = l .snc .execCommand (ctx , "yum check-update -q" + yumOpts , DefaultCmdTimeout )
268- if err != nil {
269- return true , fmt .Errorf ("yum check-update failed: %s\n %s" , err .Error (), stderr )
270- }
271- if exitCode != 0 && exitCode != 100 {
272- return true , fmt .Errorf ("yum check-update failed: %s\n %s" , output , stderr )
273- }
274- l .parseYUM (output , "0" , check , packageLookup )
275-
276- return true , nil
277- }
278-
279- func (l * CheckOSUpdates ) parseYUM (output , security string , check * CheckData , skipPackages map [string ]bool ) map [string ]bool {
280- packages := map [string ]bool {}
281- for line := range strings .SplitSeq (output , "\n " ) {
282- if strings .HasPrefix (line , "Obsoleting Packages" ) {
283- break
284- }
285- matches := reYUMEntry .FindStringSubmatch (line )
286- if len (matches ) < 3 {
287- continue
288- }
289- if skipPackages [matches [1 ]] {
290- continue
291- }
292- packages [matches [1 ]] = true
293- check .listData = append (check .listData , map [string ]string {
294- "security" : security ,
295- "package" : matches [1 ],
296- "version" : matches [2 ],
297- "old_version" : "" ,
298- "repository" : matches [3 ],
299- "arch" : matches [2 ],
300- })
301- }
302-
303- return packages
304- }
305-
306- // get packages from osx softwareupdate
307- func (l * CheckOSUpdates ) addOSX (ctx context.Context , check * CheckData ) (bool , error ) {
308- switch l .system {
309- case "auto" :
310- if runtime .GOOS != "darwin" {
311- return false , nil
312- }
313- _ , err := os .Stat ("/usr/sbin/softwareupdate" )
314- if os .IsNotExist (err ) {
315- return false , nil
316- }
317- case "osx" :
318- default :
319- return false , nil
320- }
321-
322- opts := " --no-scan"
323- if l .update {
324- opts = ""
325- }
326-
327- output , stderr , exitCode , err := l .snc .execCommand (ctx , "softwareupdate -l" + opts , DefaultCmdTimeout )
328- if err != nil {
329- return true , fmt .Errorf ("softwareupdate failed: %s\n %s" , err .Error (), stderr )
330- }
331- if exitCode != 0 {
332- return true , fmt .Errorf ("softwareupdate failed: %s\n %s" , output , stderr )
333- }
334-
335- l .parseOSX (output , check )
336-
337- return true , nil
338- }
339-
340- func (l * CheckOSUpdates ) parseOSX (output string , check * CheckData ) {
341- var lastEntry map [string ]string
342- for line := range strings .SplitSeq (output , "\n " ) {
343- if lastEntry != nil {
344- matches := reOSXDetails .FindStringSubmatch (line )
345- if len (matches ) > 1 {
346- lastEntry ["version" ] = matches [1 ]
347-
348- continue
349- }
350- }
351- matches := reOSXEntry .FindStringSubmatch (line )
352- if len (matches ) < 2 {
353- continue
354- }
355- entry := map [string ]string {
356- "security" : "0" ,
357- "package" : matches [1 ],
358- "version" : "" ,
359- "old_version" : "" ,
360- "repository" : "" ,
361- "arch" : "" ,
362- }
363- check .listData = append (check .listData , entry )
364- lastEntry = entry
365- }
366- }
367-
368- // get packages from windows powershell
369- func (l * CheckOSUpdates ) addWindows (ctx context.Context , check * CheckData ) (bool , error ) {
370- switch l .system {
371- case "auto" :
372- if runtime .GOOS != "windows" {
373- return false , nil
374- }
375- case "windows" :
376- default :
377- return false , nil
378- }
379-
380- // https://learn.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdatesearcher-search
381- // https://learn.microsoft.com/en-us/windows/win32/api/wuapi/nn-wuapi-iupdate
382- cmd := powerShellCmd (ctx , checkOSupdatesPS1 )
383- if l .update {
384- cmd .Env = append (cmd .Env , "ONLINE_SEARCH=1" )
385- }
386- output , stderr , exitCode , _ , err := l .snc .runExternalCommand (ctx , cmd , DefaultCmdTimeout )
387- if err != nil {
388- return true , fmt .Errorf ("getting pending updates failed: %s\n %s" , err .Error (), stderr )
389- }
390- if exitCode != 0 {
391- return true , fmt .Errorf ("getting pending updates failed: %s\n %s" , output , stderr )
392- }
393-
394- l .parseWindows (output , check )
395-
396- return true , nil
397- }
398-
399- func (l * CheckOSUpdates ) parseWindows (output string , check * CheckData ) {
400- var lastEntry map [string ]string
401- for line := range strings .SplitSeq (output , "\n " ) {
402- if strings .HasPrefix (line , "Category: " ) {
403- if strings .Contains (line , "Security" ) || strings .Contains (line , "Critical" ) {
404- lastEntry ["security" ] = "1"
405- }
406-
407- continue
408- }
409- if pkg , ok := strings .CutPrefix (line , "Title: " ); ok {
410- entry := map [string ]string {
411- "security" : "0" ,
412- "package" : pkg ,
413- "version" : "" ,
414- "old_version" : "" ,
415- "repository" : "" ,
416- "arch" : "" ,
417- }
418- check .listData = append (check .listData , entry )
419- lastEntry = entry
420- }
421- }
422- }
0 commit comments