From 3c53920527c93a18afa20cd241711f31f0cc9492 Mon Sep 17 00:00:00 2001 From: Andrei Burdulescu Date: Sun, 8 Jan 2023 12:07:16 +0200 Subject: [PATCH] Add flag to stop command to forcefuly stop a service --- internal/daemon/daemon.go | 14 +++++++------- internal/ipc/ipc.go | 31 +++++++++++++++++++++++++------ internal/ipc/ipc_test.go | 17 ++++++++++++++++- internal/process/process.go | 25 ++++++++++++++----------- main.go | 8 +++++--- 5 files changed, 67 insertions(+), 28 deletions(-) diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 439282e..0d92ca8 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -62,7 +62,7 @@ func (d *Daemon) Run() error { go func() { <-c fmt.Println("[daemon] termination signal received") - d.ptable.StopAll() + d.ptable.StopAll(false) d.ipcListener.Close() }() @@ -118,22 +118,22 @@ func (d *Daemon) handleIpcConn(conn net.Conn) { case ipc.OpPs: rsp.Ps = d.ptable.Ps() case ipc.OpStop: - if len(req.Names) == 0 { - d.ptable.StopAll() + if len(req.Stop.Names) == 0 { + d.ptable.StopAll(req.Stop.Force) } else { - for _, name := range req.Names { - if err := d.ptable.Stop(name); err != nil { + for _, name := range req.Stop.Names { + if err := d.ptable.Stop(name, req.Stop.Force); err != nil { rsp.Err = err.Error() } } } case ipc.OpStart: - if len(req.Names) == 0 { + if len(req.Start.Names) == 0 { if err := d.ptable.StartAll(); err != nil { rsp.Err = err.Error() } } else { - for _, name := range req.Names { + for _, name := range req.Start.Names { if err := d.ptable.Start(name); err != nil { rsp.Err = err.Error() } diff --git a/internal/ipc/ipc.go b/internal/ipc/ipc.go index 2ecedb6..3479762 100644 --- a/internal/ipc/ipc.go +++ b/internal/ipc/ipc.go @@ -32,8 +32,8 @@ func (op Op) String() string { } type Request struct { - // for start/stop - Names []string `json:",omitempty"` + Stop *StopInput `json:",omitempty"` + Start *StartInput `json:",omitempty"` Op Op } @@ -41,10 +41,18 @@ type Request struct { type Response struct { Err string - // for ps Ps []PsResult `json:",omitempty"` } +type StopInput struct { + Names []string + Force bool +} + +type StartInput struct { + Names []string +} + type PsResult struct { Name string Status string @@ -62,8 +70,15 @@ func Ps() ([]PsResult, error) { return rsp.Ps, nil } -func Stop(names ...string) error { - rsp, err := execRequest(Request{Op: OpStop, Names: names}) +func Stop(force bool, names ...string) error { + req := Request{ + Op: OpStop, + Stop: &StopInput{ + Force: force, + Names: names, + }, + } + rsp, err := execRequest(req) if err != nil { return err } @@ -74,7 +89,11 @@ func Stop(names ...string) error { } func Start(names ...string) error { - rsp, err := execRequest(Request{Op: OpStart, Names: names}) + req := Request{ + Op: OpStart, + Start: &StartInput{Names: names}, + } + rsp, err := execRequest(req) if err != nil { return err } diff --git a/internal/ipc/ipc_test.go b/internal/ipc/ipc_test.go index 2d2def5..836d31e 100644 --- a/internal/ipc/ipc_test.go +++ b/internal/ipc/ipc_test.go @@ -136,7 +136,7 @@ func TestStop(t *testing.T) { <-started defer func() { <-done }() - if err := Stop(); err != nil { + if err := Stop(false); err != nil { t.Fatal(err) } } @@ -173,3 +173,18 @@ func TestOp(t *testing.T) { }) } } + +func TestWire(t *testing.T) { + t.Run("EmptyRequest", func(t *testing.T) { + var r Request + if err := json.NewEncoder(os.Stdout).Encode(r); err != nil { + t.Fatal(err) + } + }) + t.Run("EmptyResponse", func(t *testing.T) { + var r Response + if err := json.NewEncoder(os.Stdout).Encode(r); err != nil { + t.Fatal(err) + } + }) +} diff --git a/internal/process/process.go b/internal/process/process.go index b5634c4..d70eec7 100644 --- a/internal/process/process.go +++ b/internal/process/process.go @@ -70,17 +70,20 @@ func (p *Process) UpdateState() { } } -func (p *Process) Stop() { +func (p *Process) Stop(force bool) { if p.Cmd == nil { return } if p.State != Running { return } - if err := p.Cmd.Process.Signal(syscall.SIGTERM); err != nil { - fmt.Printf("[daemon] failed to kill [%s]: %v\n", p.Name, err) + sig := syscall.SIGTERM + if force { + sig = syscall.SIGKILL + } + if err := p.Cmd.Process.Signal(sig); err != nil { + fmt.Printf("[daemon] failed to send signal %d [%s]: %v\n", sig, p.Name, err) } - // TODO: this may not actually kill it => wait to see if it stopped then send SIGKILL } type Table struct { @@ -118,7 +121,7 @@ func (t *Table) StartAll() error { func (t *Table) startAll() error { for i, p := range t.procs { if err := t.start(&t.procs[i]); err != nil { - t.stopAll() + t.stopAll(false) return fmt.Errorf("[daemon] failed to start [%s]: %w", p.Name, err) } } @@ -184,24 +187,24 @@ func (t *Table) start(p *Process) error { return nil } -func (t *Table) StopAll() { +func (t *Table) StopAll(force bool) { t.mu.Lock() defer t.mu.Unlock() - t.stopAll() + t.stopAll(force) } -func (t *Table) stopAll() { +func (t *Table) stopAll(force bool) { for _, p := range t.procs { - p.Stop() + p.Stop(force) } } -func (t *Table) Stop(name string) error { +func (t *Table) Stop(name string, force bool) error { t.mu.Lock() defer t.mu.Unlock() for _, p := range t.procs { if p.Name == name { - p.Stop() + p.Stop(force) return nil } } diff --git a/main.go b/main.go index cafcfbe..fba550a 100644 --- a/main.go +++ b/main.go @@ -165,15 +165,17 @@ If no service name is given, stop all services. Options: -h, --help Print this message - + -k Force stop the service(s); i.e. send SIGKILL `) } + force := fset.Bool("k", false, "") + if err := fset.Parse(args); err != nil { return err } - return ipc.Stop(args...) + return ipc.Stop(*force, fset.Args()...) } func runStart(args []string) error { @@ -195,5 +197,5 @@ Options: return err } - return ipc.Start(args...) + return ipc.Start(fset.Args()...) }