github.com/immortal/immortal@v0.0.0-20240201195854-d8073cd41019/signals.go (about)

     1  package immortal
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"log"
     7  	"net/http"
     8  	"os"
     9  	"strings"
    10  	"syscall"
    11  
    12  	"github.com/nbari/violetear"
    13  )
    14  
    15  // SignalResponse struct to return the error in json format
    16  type SignalResponse struct {
    17  	Err string
    18  }
    19  
    20  // HandleSignal send signals to the current process
    21  func (d *Daemon) HandleSignal(w http.ResponseWriter, r *http.Request) {
    22  	var err error
    23  
    24  	// get signal from request params
    25  	signal := violetear.GetParam("*", r)
    26  
    27  	if d.process.cmd != nil {
    28  		switch signal {
    29  		// a: Alarm. Send the service an ALRM signal.
    30  		case "a", "alrm", "ALRM":
    31  			err = d.process.Signal(syscall.SIGALRM)
    32  
    33  		// c: Continue. Send the service a CONT signal.
    34  		case "c", "cont", "CONT":
    35  			err = d.process.Signal(syscall.SIGCONT)
    36  
    37  		// d: Down. If the service is running, send it a TERM signal. After it stops, do not restart it.
    38  		case "d", "down":
    39  			d.lockOnce = 1
    40  			err = d.process.Signal(syscall.SIGTERM)
    41  
    42  		// h: Hangup. Send the service a HUP signal.
    43  		case "h", "hup", "HUP":
    44  			err = d.process.Signal(syscall.SIGHUP)
    45  
    46  		// halt: down + exit
    47  		// A restart will only happen when using immortaldir
    48  		case "halt", "restart":
    49  			d.lockOnce = 1
    50  			err = d.process.Signal(syscall.SIGTERM)
    51  			close(d.quit)
    52  
    53  		// i: Interrupt. Send the service an INT signal.
    54  		case "i", "int", "INT":
    55  			err = d.process.Signal(syscall.SIGINT)
    56  
    57  		// in: TTIN. Send the service a TTIN signal.
    58  		case "in", "ttin", "TTIN":
    59  			err = d.process.Signal(syscall.SIGTTIN)
    60  
    61  			// k: Kill. Send the service a KILL signal.
    62  		case "k", "kill", "KILL":
    63  			if d.fpid {
    64  				err = d.process.Signal(syscall.SIGKILL)
    65  			} else {
    66  				err = d.process.Kill()
    67  			}
    68  
    69  		// o: Once. If the service is not running, start it. Do not restart it if it stops.
    70  		case "o", "once":
    71  			d.lockOnce = 1
    72  			if !d.IsRunning(d.process.Pid()) {
    73  				d.lock = 0
    74  				d.run <- struct{}{}
    75  			}
    76  
    77  		// ou: TTOU. Send the service a TTOU signal.
    78  		case "ou", "ttou", "TTOU":
    79  			err = d.process.Signal(syscall.SIGTTOU)
    80  
    81  		// s: stop. Send the service a STOP signal.
    82  		case "s", "stop", "STOP":
    83  			err = d.process.Signal(syscall.SIGSTOP)
    84  
    85  		// q: QUIT. Send the service a QUIT signal.
    86  		case "q", "quit", "QUIT":
    87  			err = d.process.Signal(syscall.SIGQUIT)
    88  
    89  		// t: Terminate. Send the service a TERM signal.
    90  		case "t", "term", "TERM":
    91  			err = d.process.Signal(syscall.SIGTERM)
    92  
    93  		// u: Up. If the service is not running, start it. If the service stops, restart it.
    94  		case "u", "up", "start":
    95  			d.lockOnce = 0
    96  			if !d.IsRunning(d.process.Pid()) {
    97  				d.lock = 0
    98  			}
    99  			d.run <- struct{}{}
   100  
   101  		// 1: USR1. Send the service a USR1 signal.
   102  		case "1", "usr1", "USR1":
   103  			err = d.process.Signal(syscall.SIGUSR1)
   104  
   105  		// 2: USR2. Send the service a USR2 signal.
   106  		case "2", "usr2", "USR2":
   107  			err = d.process.Signal(syscall.SIGUSR2)
   108  
   109  		// w: WINCH. Send the service a WINCH signal.
   110  		case "w", "winch", "WINCH":
   111  			err = d.process.Signal(syscall.SIGWINCH)
   112  
   113  		// x: Exit. If you use this option on a stable system, you're doing something wrong.
   114  		// the supervisor is designed to run forever.
   115  		case "x", "exit":
   116  			if d.cfg.cli || os.Getenv("IMMORTAL_EXIT") != "" {
   117  				close(d.quit)
   118  			} else {
   119  				err = fmt.Errorf("set environment variable IMMORTAL_EXIT to exit")
   120  			}
   121  
   122  		default:
   123  			err = fmt.Errorf("unknown signal: %s", signal)
   124  		}
   125  	} else {
   126  		err = fmt.Errorf("%q not running", strings.Join(d.cfg.command, " "))
   127  	}
   128  
   129  	res := &SignalResponse{}
   130  	if err != nil {
   131  		res.Err = err.Error()
   132  		log.Printf("signal error: %s\n", err)
   133  	}
   134  
   135  	// return the error on the Response json encoded
   136  	w.Header().Set("Content-Type", "application/json")
   137  	if err := json.NewEncoder(w).Encode(res); err != nil {
   138  		log.Printf("error creating json response: %s\n", err)
   139  	}
   140  }