├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── bleep.go ├── bleep_test.go ├── go.mod └── go.sum /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | test: 11 | name: Test 12 | strategy: 13 | matrix: 14 | go-version: [1.12.x, 1.13.x, 1.14.x, 1.15.x] 15 | platform: [ubuntu-latest, macos-latest] 16 | runs-on: ${{ matrix.platform }} 17 | steps: 18 | - name: Install Go 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: ${{ matrix.go-version }} 22 | id: go 23 | - name: Check out code into the Go module directory 24 | uses: actions/checkout@v2 25 | - name: Get dependencies 26 | run: | 27 | go get -v -t -d ./... 28 | if [ -f Gopkg.toml ]; then 29 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 30 | dep ensure 31 | fi 32 | - name: Run tests 33 | run: go test -v -coverprofile=profile.cov ./... 34 | - name: Send coverage 35 | uses: shogo82148/actions-goveralls@v1 36 | with: 37 | path-to-profile: profile.cov 38 | flag-name: ${{ matrix.platform }}-go-${{ matrix.go-version }} 39 | parallel: true 40 | 41 | finish: 42 | name: Finish 43 | runs-on: ubuntu-latest 44 | needs: [test] 45 | steps: 46 | - name: Sending coverage finished 47 | uses: shogo82148/actions-goveralls@v1 48 | with: 49 | parallel-finished: true 50 | 51 | build: 52 | name: Build 53 | runs-on: ubuntu-latest 54 | needs: [test] 55 | steps: 56 | - name: Set up Go 1.x 57 | uses: actions/setup-go@v2 58 | with: 59 | go-version: ^1.14 60 | id: go 61 | - name: Check out code into the Go module directory 62 | uses: actions/checkout@v2 63 | - name: Get dependencies 64 | run: | 65 | go get -v -t -d ./... 66 | if [ -f Gopkg.toml ]; then 67 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 68 | dep ensure 69 | fi 70 | - name: Build 71 | run: go build -v . 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.cov 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Shubham Sinha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bleep 2 | 3 | [![GoDoc](https://godoc.org/github.com/sinhashubham95/bleep?status.svg)](https://pkg.go.dev/github.com/sinhashubham95/bleep) 4 | [![Release](https://img.shields.io/github/v/release/sinhashubham95/bleep?sort=semver)](https://github.com/sinhashubham95/bleep/releases) 5 | [![Report](https://goreportcard.com/badge/github.com/sinhashubham95/bleep)](https://goreportcard.com/report/github.com/sinhashubham95/bleep) 6 | [![Coverage Status](https://coveralls.io/repos/github/sinhashubham95/bleep/badge.svg?branch=master)](https://coveralls.io/github/sinhashubham95/bleep?branch=master) 7 | [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go#utilities) 8 | 9 | `Bleep` is used to peform actions on OS signals. It is highly extensible and goroutine safe. It is possible to add any number of actions and all of them are guaranteed to be performed simultaneously on the OS signals that `Bleep` will be listening for. 10 | 11 | ## Installation 12 | 13 | ```shell 14 | go get github.com/sinhashubham95/bleep 15 | ``` 16 | 17 | ## How to Use 18 | 19 | The `Bleep` package allows you to create a new instance of the handler and also has a default handler in place that can be used directly. 20 | 21 | Creating separate Bleep instances can be useful, when you want to perform different set of actions for different set of OS signals. 22 | 23 | ### Create a New OS Signal Handler 24 | 25 | This is used to create a new handler for performing actions on OS Signals. 26 | 27 | ```go 28 | import ( 29 | "os" 30 | "github.com/sinhashubham95/bleep" 31 | ) 32 | 33 | func New() { 34 | handler := bleep.New() 35 | // now this handler can be used to add or remove actions and listen to the OS signals 36 | } 37 | ``` 38 | 39 | ### Add an Action 40 | 41 | This is used to add an action to be executed on the OS signal listening for. 42 | 43 | ```go 44 | import ( 45 | "os" 46 | "github.com/sinhashubham95/bleep" 47 | ) 48 | 49 | fun Add() { 50 | key := bleep.Add(func (s os.Signal) { 51 | // do something 52 | }) 53 | // this key is the unique identifier for your added action 54 | } 55 | ``` 56 | 57 | ### Remove an Action 58 | 59 | This is used to remove an action added to Bleep. 60 | 61 | ```go 62 | import ( 63 | "github.com/sinhashubham95/bleep" 64 | ) 65 | 66 | func Remove() { 67 | action := bleep.Remove("some-key") // this key should be the same as the one returned during adding the action 68 | // the returned action is the one that was added using this key 69 | } 70 | ``` 71 | 72 | ### Listen 73 | 74 | This is used to listen for the OS signals. Note that this will wait for the signal in the go routine in which this is called. 75 | 76 | ```go 77 | import ( 78 | "syscall" 79 | "github.com/sinhashubham95/bleep" 80 | ) 81 | 82 | func Listen() { 83 | bleep.Listen(syscall.SIGINT, syscall.SIGTERM) 84 | } 85 | ``` -------------------------------------------------------------------------------- /bleep.go: -------------------------------------------------------------------------------- 1 | package bleep 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "sync" 7 | 8 | "github.com/google/uuid" 9 | ) 10 | 11 | var defaultBleep = New() 12 | 13 | // Action is what will be performed on an os signal 14 | type Action func(os.Signal) 15 | 16 | // Bleep is the type for the handler 17 | type Bleep struct { 18 | actions map[string]Action 19 | mu *sync.RWMutex 20 | } 21 | 22 | // New is used to create a new os signal handler 23 | func New() *Bleep { 24 | return &Bleep{ 25 | actions: make(map[string]Action), 26 | mu: &sync.RWMutex{}, 27 | } 28 | } 29 | 30 | // Add is used to add an action to the default bleep instance to be performed on an os signal 31 | func Add(action Action) string { 32 | return defaultBleep.Add(action) 33 | } 34 | 35 | // Remove is used to remove an action from the default bleep instance 36 | func Remove(key string) Action { 37 | return defaultBleep.Remove(key) 38 | } 39 | 40 | // Reset is used to reset the default handler instance 41 | func Reset() map[string]Action { 42 | return defaultBleep.Reset() 43 | } 44 | 45 | // Actions is used to get the set of actions part of the default bleep instance 46 | func Actions() map[string]Action { 47 | return defaultBleep.Actions() 48 | } 49 | 50 | // Listen is used to listen for the provided OS signals 51 | // If none are provided, then it will listen for any OS signal 52 | func Listen(signals ...os.Signal) { 53 | defaultBleep.Listen(signals...) 54 | } 55 | 56 | // Add is used to add an action to be performed on an os signal 57 | func (b *Bleep) Add(action Action) string { 58 | b.mu.Lock() 59 | defer b.mu.Unlock() 60 | key := uuid.New().String() 61 | b.actions[key] = action 62 | return key 63 | } 64 | 65 | // Remove is used to remove an action added previously 66 | func (b *Bleep) Remove(key string) Action { 67 | b.mu.Lock() 68 | defer b.mu.Unlock() 69 | if action, ok := b.actions[key]; ok { 70 | delete(b.actions, key) 71 | return action 72 | } 73 | return nil 74 | } 75 | 76 | // Reset is used to reset the current handler instance 77 | func (b *Bleep) Reset() map[string]Action { 78 | b.mu.Lock() 79 | defer b.mu.Unlock() 80 | actions := make(map[string]Action) 81 | for k, a := range b.actions { 82 | delete(b.actions, k) 83 | actions[k] = a 84 | } 85 | return actions 86 | } 87 | 88 | // Actions is used to get the set of actions part of the current bleep instance 89 | func (b *Bleep) Actions() map[string]Action { 90 | b.mu.RLock() 91 | defer b.mu.RUnlock() 92 | actions := make(map[string]Action) 93 | for k, a := range b.actions { 94 | actions[k] = a 95 | } 96 | return actions 97 | } 98 | 99 | // Listen is used to listen for the provided OS signals 100 | // If none are provided, then it will listen for any OS signal 101 | func (b *Bleep) Listen(signals ...os.Signal) { 102 | ch := make(chan os.Signal, 1) 103 | signal.Notify(ch, signals...) 104 | s := <-ch 105 | b.mu.RLock() 106 | defer b.mu.RUnlock() 107 | wg := sync.WaitGroup{} 108 | for _, a := range b.actions { 109 | wg.Add(1) 110 | action := a 111 | go func(s os.Signal) { 112 | defer wg.Done() 113 | action(s) 114 | }(s) 115 | } 116 | wg.Wait() 117 | } 118 | -------------------------------------------------------------------------------- /bleep_test.go: -------------------------------------------------------------------------------- 1 | package bleep_test 2 | 3 | import ( 4 | "os" 5 | "sync" 6 | "syscall" 7 | "testing" 8 | "time" 9 | 10 | "github.com/sinhashubham95/bleep" 11 | ) 12 | 13 | func Test(t *testing.T) { 14 | defer bleep.Reset() 15 | 16 | mu := sync.Mutex{} 17 | testData := make(map[int]int) 18 | 19 | // add some actions 20 | bleep.Add(func(s os.Signal) { 21 | mu.Lock() 22 | defer mu.Unlock() 23 | if s == syscall.SIGTERM { 24 | testData[1]++ 25 | } 26 | }) 27 | bleep.Add(func(s os.Signal) { 28 | mu.Lock() 29 | defer mu.Unlock() 30 | if s == syscall.SIGTERM { 31 | testData[2]++ 32 | } 33 | }) 34 | bleep.Add(func(s os.Signal) { 35 | mu.Lock() 36 | defer mu.Unlock() 37 | if s == syscall.SIGABRT { 38 | testData[3]++ 39 | } 40 | }) 41 | k4 := bleep.Add(func(s os.Signal) { 42 | mu.Lock() 43 | defer mu.Unlock() 44 | if s == syscall.SIGTERM { 45 | testData[4]++ 46 | } 47 | }) 48 | 49 | // remove an action 50 | a4 := bleep.Remove(k4) 51 | 52 | // remove a non existing action 53 | a := bleep.Remove("sample") 54 | if a != nil { 55 | t.Errorf("Action should not be existing but it does.") 56 | } 57 | 58 | actions := bleep.Actions() 59 | if len(actions) != 3 { 60 | t.Errorf("Invalid number of actions expected %d found %d.", 3, len(actions)) 61 | } 62 | 63 | if len(testData) != 0 { 64 | t.Errorf("Test data is not empty %+v.", testData) 65 | } 66 | 67 | // start a go routine that will produce a OS signal 68 | go func() { 69 | // wait for 100ms 70 | time.Sleep(100 * time.Millisecond) 71 | // send a signal 72 | p, err := os.FindProcess(os.Getpid()) 73 | if err != nil { 74 | panic(err.Error()) 75 | } 76 | p.Signal(syscall.SIGTERM) 77 | }() 78 | 79 | // now listen to the signal 80 | bleep.Listen() 81 | 82 | if len(testData) != 2 { 83 | t.Errorf("Test data invalid expected length %d found %d.", 2, len(testData)) 84 | } 85 | 86 | if testData[1] != 1 || testData[2] != 1 || testData[3] != 0 || testData[4] != 0 { 87 | t.Errorf("Invalid test data %+v.", testData) 88 | } 89 | 90 | a4(syscall.SIGTERM) 91 | if testData[4] != 1 { 92 | t.Errorf("Invalid removed action.") 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sinhashubham95/bleep 2 | 3 | go 1.15 4 | 5 | require github.com/google/uuid v1.1.3 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/uuid v1.1.3 h1:twObb+9XcuH5B9V1TBCvvvZoO6iEdILi2a76PYn5rJI= 2 | github.com/google/uuid v1.1.3/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 3 | --------------------------------------------------------------------------------