From 6ca95ae1956b215f73a5d214bea2b2ef7b6a94c8 Mon Sep 17 00:00:00 2001 From: ivcosla Date: Mon, 10 Jun 2019 23:07:27 +0200 Subject: [PATCH] added pid files. permission denied error --- pkg/node/node.go | 69 +++++++++++++++++++++++++++++++++++- pkg/router/app_manager.go | 3 +- pkg/util/pathutil/homedir.go | 62 ++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 3 deletions(-) diff --git a/pkg/node/node.go b/pkg/node/node.go index 4e5c4775d5..8c64c304e6 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -2,15 +2,19 @@ package node import ( + "bufio" "context" "errors" "fmt" + "github.com/skycoin/skywire/pkg/util/pathutil" "io" "net" "net/rpc" "os" "os/exec" "path/filepath" + "strconv" + "strings" "sync" "syscall" "time" @@ -151,6 +155,7 @@ func NewNode(config *Config) (*Node, error) { RoutingTable: node.rt, RouteFinder: routeFinder.NewHTTP(config.Routing.RouteFinder, time.Duration(config.Routing.RouteFinderTimeout)), SetupNodes: config.Routing.SetupNodes, + } r := router.New(rConfig) node.router = r @@ -203,6 +208,8 @@ func (node *Node) Start() error { } node.logger.Info("Connected to messaging servers") + pathutil.EnsureDir(node.dir()) + node.closePreviousApps() for _, ac := range node.appsConf { if !ac.AutoStart { continue @@ -239,6 +246,58 @@ func (node *Node) Start() error { return nil } +func (node *Node) dir() string { + return pathutil.NodeDir(node.config.Node.StaticPubKey) +} + +func (node *Node) pidFile() *os.File { + f, err := os.OpenFile(filepath.Join(node.dir(),"apps.pid"), os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + panic(err) + } + + return f +} + +func (node *Node) closePreviousApps() { + pids := node.pidFile() + defer pids.Close() // nocheck: err + + scanner := bufio.NewScanner(pids) + for scanner.Scan() { + appInfo := strings.Split(scanner.Text(), " ") + if len(appInfo) != 2 { + node.logger.Fatal("error parsing %s. Err: %s", pids.Name(), errors.New("line should be: [app name] [pid]")) + } + + pid, err := strconv.Atoi(appInfo[1]) + if err != nil { + node.logger.Fatal("error parsing %s. Err: %s", pids.Name(), err) + } + + node.stopUnhandledApp(appInfo[0], pid) + } + + // empty file + pathutil.AtomicWriteFile(pids.Name(), []byte{}) +} + +func (node *Node) stopUnhandledApp(name string, pid int) { + p, err := os.FindProcess(pid) + if err != nil { + node.logger.Infof("Previous app %s ran by this node with pid: %d not found", name, pid) + return + } + + err = p.Signal(syscall.SIGKILL) + if err != nil { + node.logger.Warnf("Found hanged app %s with pid %d previously ran by this node, but unable to kill it: %s", name, pid, err) + return + } + + node.logger.Infof("Found and killed hanged app %s with pid %d previously ran by this node", name, pid) +} + // Close safely stops spawned Apps and messaging Node. func (node *Node) Close() (err error) { if node.rpcListener != nil { @@ -357,6 +416,8 @@ func (node *Node) SpawnApp(config *AppConfig, startCh chan<- struct{}) error { bind.pid = pid node.startedMu.Unlock() appCh <- node.executer.Wait(cmd) + + node.persistPID(config.App, pid) }() srvCh := make(chan error) @@ -389,6 +450,11 @@ func (node *Node) SpawnApp(config *AppConfig, startCh chan<- struct{}) error { return appErr } +func (node *Node) persistPID(name string, pid int) { + pidF := node.pidFile() + pathutil.AtomicAppendToFile(pidF.Name(), []byte(fmt.Sprintf("%s %d\n", name, pid))) +} + // StopApp stops running App. func (node *Node) StopApp(appName string) error { node.startedMu.Lock() @@ -445,6 +511,7 @@ func (exc *osExecuter) Start(cmd *exec.Cmd) (int, error) { exc.mu.Lock() exc.processes = append(exc.processes, cmd.Process) exc.mu.Unlock() + return cmd.Process.Pid, nil } @@ -457,7 +524,7 @@ func (exc *osExecuter) Stop(pid int) (err error) { continue } - if sigErr := process.Signal(syscall.SIGTERM); sigErr != nil && err == nil { + if sigErr := process.Signal(syscall.SIGKILL); sigErr != nil && err == nil { err = sigErr } } diff --git a/pkg/router/app_manager.go b/pkg/router/app_manager.go index 49565f85c4..b7acc32ce3 100644 --- a/pkg/router/app_manager.go +++ b/pkg/router/app_manager.go @@ -3,9 +3,7 @@ package router import ( "encoding/json" "errors" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/pkg/app" ) @@ -97,3 +95,4 @@ func (am *appManager) forwardAppPacket(payload []byte) error { return am.callbacks.Forward(am.proto, packet) } + diff --git a/pkg/util/pathutil/homedir.go b/pkg/util/pathutil/homedir.go index 442a989938..6aa2fe5730 100644 --- a/pkg/util/pathutil/homedir.go +++ b/pkg/util/pathutil/homedir.go @@ -1,7 +1,12 @@ package pathutil import ( + "fmt" + "github.com/skycoin/skywire/pkg/cipher" + "io/ioutil" "os" + "path" + "path/filepath" "runtime" ) @@ -17,3 +22,60 @@ func HomeDir() string { } return os.Getenv("HOME") } + +// NodeDir returns a path to a directory used to store specific node configuration. Such dir is ~/.skywire/{PK} +func NodeDir(pk cipher.PubKey) string { + return filepath.Join(HomeDir(),".skycoin","skywire",pk.String()) +} + +// EnsureDir attempts to create given directory, panics if it fails to do so +func EnsureDir(path string) { + if _, err := os.Stat(path); os.IsNotExist(err) { + err := os.MkdirAll(path, 0644) + if err != nil { + panic(err) + } + } +} + +// AtomicWriteFile creates a temp file in which to write data, then calls syscall.Rename to swap it and write it on +// filename for an atomic write. On failure temp file is removed and panics. +func AtomicWriteFile(filename string, data []byte) { + fmt.Println("got filename: ", filename) + dir, name := path.Split(filename) + f, err := ioutil.TempFile(dir, name) + if err != nil { + panic(err) + } + + _, err = f.Write(data) + if err == nil { + err = f.Sync() + } + if closeErr := f.Close(); err == nil { + err = closeErr + } + if permErr := os.Chmod(f.Name(), 0644); err == nil { + err = permErr + } + if err == nil { + err = os.Rename(f.Name(), filename) + } + + if err != nil { + os.Remove(f.Name()) + } + panic(err) +} + +// AtomicAppendToFile calls AtomicWriteFile but appends new data to destiny file +func AtomicAppendToFile(filename string, data []byte) { + fmt.Println("got filename: ", filename) + oldFile, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) + } + + AtomicWriteFile(filename, append(oldFile, data...)) +} +