github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/events/onSignalReceived/signaltrap.go (about) 1 //go:build !plan9 && !js 2 // +build !plan9,!js 3 4 package signaltrap 5 6 import ( 7 "fmt" 8 "os" 9 "sort" 10 "syscall" 11 12 "github.com/lmorg/murex/builtins/events" 13 "github.com/lmorg/murex/lang" 14 "github.com/lmorg/murex/lang/ref" 15 "github.com/lmorg/murex/utils/lists" 16 ) 17 18 var eventType = "onSignalReceived" 19 20 const errInvalidInterrupt = `invalid interrupt, '%s'. Expecting a signal name, like "SIGINT"` 21 22 func init() { 23 event := newSignalTrap() 24 events.AddEventType(eventType, event, nil) 25 go event.signalHandler() 26 } 27 28 // Interrupt is a JSONable structure passed to the murex function 29 type Interrupt struct { 30 Name string 31 Signal string 32 } 33 34 type sigEvent struct { 35 Key string 36 Block []rune 37 FileRef *ref.File 38 } 39 40 type sigEvents struct { 41 events []sigEvent 42 //mutex sync.Mutex 43 } 44 45 func newSignalTrap() *sigEvents { 46 return new(sigEvents) 47 } 48 49 // Add a command to the onPrompt 50 func (evt *sigEvents) Add(name, interrupt string, block []rune, fileRef *ref.File) error { 51 if !isValidInterrupt(interrupt) { 52 return fmt.Errorf(errInvalidInterrupt, interrupt) 53 } 54 55 //evt.mutex.Lock() 56 57 key := compileInterruptKey(interrupt, name) 58 event := sigEvent{ 59 Key: key, 60 Block: block, 61 FileRef: fileRef, 62 } 63 64 if evt.noRegisteredSignal(interrupt) { 65 if err := register(interrupt); err != nil { 66 return err 67 } 68 } 69 70 i := evt.exists(key) 71 if i == doesNotExist { 72 evt.events = append(evt.events, event) 73 sort.SliceStable(evt.events, func(i, j int) bool { 74 return evt.events[i].Key < evt.events[j].Key 75 }) 76 } else { 77 evt.events[i] = event 78 } 79 80 //evt.mutex.Unlock() 81 82 return nil 83 } 84 85 func (evt *sigEvents) Remove(key string) error { 86 //evt.mutex.Lock() 87 //defer evt.mutex.Unlock() 88 89 i := evt.exists(key) 90 if i != doesNotExist { 91 events, err := lists.RemoveOrdered(evt.events, i) 92 if err != nil { 93 return fmt.Errorf("unable to remove %s: %s", key, err.Error()) 94 } 95 evt.events = events 96 return nil 97 } 98 99 var ( 100 success bool 101 remove []syscall.Signal 102 ) 103 104 for name := range interrupts { 105 newKey := compileInterruptKey(name, key) 106 i = evt.exists(newKey) 107 if i != doesNotExist { 108 events, err := lists.RemoveOrdered(evt.events, i) 109 if err != nil { 110 return fmt.Errorf("unable to remove %s: %s", newKey, err.Error()) 111 } 112 evt.events = events 113 success = true 114 remove = append(remove, interrupts[name]) 115 } 116 } 117 118 if success { 119 for i := range remove { 120 if evt.noRegisteredSignal(remove[i].String()) { 121 deregister(remove[i]) 122 } 123 } 124 return nil 125 } 126 return fmt.Errorf("no %s event found called `%s`", eventType, key) 127 } 128 129 func (evt *sigEvents) callback(sig os.Signal) { 130 var interrupt string 131 for name, signal := range interrupts { 132 if signal.String() == sig.String() { 133 interrupt = name 134 goto event 135 } 136 } 137 138 panic(fmt.Sprintf("unknown signal: %V", sig)) 139 140 event: 141 142 //evt.mutex.Lock() 143 144 for i := range evt.events { 145 split := getInterruptFromKey(evt.events[i].Key) 146 if split[0] == interrupt { 147 interruptValue := Interrupt{ 148 Name: split[1], 149 Signal: interrupt, 150 } 151 events.Callback(evt.events[i].Key, interruptValue, evt.events[i].Block, evt.events[i].FileRef, lang.ShellProcess.Stdout, false) 152 } 153 } 154 155 //evt.mutex.Unlock() 156 } 157 158 const doesNotExist = -1 159 160 func (evt *sigEvents) exists(key string) int { 161 //evt.mutex.Lock() 162 163 for i := range evt.events { 164 if evt.events[i].Key == key { 165 return i 166 } 167 } 168 169 //evt.mutex.Unlock() 170 171 return doesNotExist 172 } 173 174 func (evt *sigEvents) noRegisteredSignal(sig string) bool { 175 //evt.mutex.Lock() 176 177 for i := range evt.events { 178 split := getInterruptFromKey(evt.events[i].Key) 179 if split[0] == sig { 180 return false 181 } 182 } 183 184 //evt.mutex.Unlock() 185 186 return true 187 } 188 189 func (evt *sigEvents) signalHandler() { 190 for { 191 sig := <-signalChan 192 evt.callback(sig) 193 } 194 } 195 196 func (evt *sigEvents) Dump() map[string]events.DumpT { 197 dump := make(map[string]events.DumpT) 198 199 //evt.mutex.Lock() 200 201 for i := range evt.events { 202 dump[evt.events[i].Key] = events.DumpT{ 203 Interrupt: getInterruptFromKey(evt.events[i].Key)[0], 204 Block: string(evt.events[i].Block), 205 FileRef: evt.events[i].FileRef, 206 } 207 } 208 209 //evt.mutex.Unlock() 210 211 return dump 212 }