@@ -69,17 +69,19 @@ func parseZpoolEvents(reader io.Reader, startTime time.Time, onUpdate func()) {
6969 isTargetAction := false
7070 var eventTime time.Time
7171
72+ // Closure to handle the trigger logic cleanly (used for both empty lines and EOF)
73+ triggerIfValid := func () {
74+ if inHistoryEvent && isTargetAction && eventTime .After (startTime ) {
75+ onUpdate ()
76+ }
77+ }
78+
7279 for scanner .Scan () {
7380 line := strings .TrimSpace (scanner .Text ())
7481
7582 // Empty line marks the end of an event block
7683 if line == "" {
77- if inHistoryEvent && isTargetAction {
78- // Only trigger if the event happened AFTER the watcher started
79- if eventTime .After (startTime ) {
80- onUpdate ()
81- }
82- }
84+ triggerIfValid ()
8385
8486 // Reset block state for the next event
8587 inHistoryEvent = false
@@ -94,38 +96,52 @@ func parseZpoolEvents(reader io.Reader, startTime time.Time, onUpdate func()) {
9496 }
9597
9698 if inHistoryEvent {
97- // 1. Check if the action is relevant
98- if strings .HasPrefix (line , "history_str =" ) || strings .HasPrefix (line , "history_internal_name =" ) {
99- if strings .Contains (line , "\" snapshot\" " ) || strings .Contains (line , "\" destroy\" " ) ||
100- strings .Contains (line , "\" zfs snapshot " ) || strings .Contains (line , "\" zfs destroy " ) {
101- isTargetAction = true
102- }
103- }
104-
105- // 2. Extract and parse the event timestamp
106- if strings .HasPrefix (line , "time = " ) {
107- parts := strings .Fields (line )
108- if len (parts ) >= 3 {
109- secHex := strings .TrimPrefix (parts [2 ], "0x" )
110- sec , err := strconv .ParseInt (secHex , 16 , 64 )
111- if err == nil {
112- nsec := int64 (0 )
113- if len (parts ) >= 4 {
114- nsecHex := strings .TrimPrefix (parts [3 ], "0x" )
115- nsec , _ = strconv .ParseInt (nsecHex , 16 , 64 )
116- }
117- eventTime = time .Unix (sec , nsec )
118- }
119- }
99+ if isTargetZfsAction (line ) {
100+ isTargetAction = true
101+ } else if parsedTime , ok := parseEventTime (line ); ok {
102+ eventTime = parsedTime
120103 }
121104 }
122- } // End of scanner loop
105+ }
123106
124- if inHistoryEvent && isTargetAction {
125- if eventTime .After (startTime ) {
126- onUpdate ()
127- }
107+ // Catch the final event if the file didn't end with an empty line
108+ triggerIfValid ()
109+ }
110+
111+ // isTargetZfsAction checks if the line indicates a snapshot creation or destruction
112+ func isTargetZfsAction (line string ) bool {
113+ if ! strings .HasPrefix (line , "history_str =" ) && ! strings .HasPrefix (line , "history_internal_name =" ) {
114+ return false
128115 }
116+
117+ return strings .Contains (line , "\" snapshot\" " ) || strings .Contains (line , "\" destroy\" " ) ||
118+ strings .Contains (line , "\" zfs snapshot " ) || strings .Contains (line , "\" zfs destroy " )
119+ }
120+
121+ // parseEventTime extracts and parses the hex timestamp from a ZFS event time line
122+ func parseEventTime (line string ) (time.Time , bool ) {
123+ if ! strings .HasPrefix (line , "time = " ) {
124+ return time.Time {}, false
125+ }
126+
127+ parts := strings .Fields (line )
128+ if len (parts ) < 3 {
129+ return time.Time {}, false
130+ }
131+
132+ secHex := strings .TrimPrefix (parts [2 ], "0x" )
133+ sec , err := strconv .ParseInt (secHex , 16 , 64 )
134+ if err != nil {
135+ return time.Time {}, false
136+ }
137+
138+ nsec := int64 (0 )
139+ if len (parts ) >= 4 {
140+ nsecHex := strings .TrimPrefix (parts [3 ], "0x" )
141+ nsec , _ = strconv .ParseInt (nsecHex , 16 , 64 )
142+ }
143+
144+ return time .Unix (sec , nsec ), true
129145}
130146
131147func AddZpoolEventWatcherActor (g * run.Group , ctx context.Context ) {
0 commit comments