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  }