github.com/elfadel/cilium@v1.6.12/pkg/pidfile/pidfile.go (about) 1 // Copyright 2017-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package pidfile 16 17 import ( 18 "fmt" 19 "io/ioutil" 20 "os" 21 "strconv" 22 "strings" 23 "sync" 24 25 "github.com/cilium/cilium/pkg/cleanup" 26 ) 27 28 var ( 29 cleanUPSig = make(chan struct{}) 30 cleanUPWg = &sync.WaitGroup{} 31 ) 32 33 // Remove deletes the pidfile at the specified path. This does not clean up 34 // the corresponding process, so should only be used when it is known that the 35 // PID contained in the file at the specified path is no longer running. 36 func Remove(path string) error { 37 if err := os.Remove(path); err != nil && !os.IsNotExist(err) { 38 return err 39 } 40 41 return nil 42 } 43 44 func write(path string, pid int) error { 45 pidBytes := []byte(strconv.Itoa(pid) + "\n") 46 if err := ioutil.WriteFile(path, pidBytes, 0660); err != nil { 47 return err 48 } 49 50 cleanup.DeferTerminationCleanupFunction(cleanUPWg, cleanUPSig, func() { 51 Remove(path) 52 }) 53 54 return nil 55 } 56 57 // Write the pid of the process to the specified path, and attach a cleanup 58 // handler to the exit of the program so it's removed afterwards. 59 func Write(path string) error { 60 pid := os.Getpid() 61 return write(path, pid) 62 } 63 64 // Clean cleans up everything created by this package. 65 func Clean() { 66 close(cleanUPSig) 67 cleanUPWg.Wait() 68 } 69 70 // kill parses the PID in the provided slice and attempts to kill the process 71 // associated with that PID. 72 func kill(buf []byte, pidfile string) (int, error) { 73 pidStr := strings.TrimSpace(string(buf)) 74 pid, err := strconv.Atoi(pidStr) 75 if err != nil { 76 return 0, fmt.Errorf("failed to parse pid from %q: %s", pidStr, err) 77 } 78 oldProc, err := os.FindProcess(pid) 79 if err != nil { 80 return 0, fmt.Errorf("could not find process %d: %s", pid, err) 81 } 82 // According to the golang/pkg/os documentation: 83 // "On Unix systems, FindProcess always succeeds and returns a Process 84 // for the given pid, regardless of whether the process exists." 85 // 86 // It could return "os: process already finished", therefore we ignore 87 // the error, but return pid 0 to indicate that the process was not 88 // killed. 89 if err := oldProc.Kill(); err != nil { 90 // return pid 0 after releasing process 91 pid = 0 92 } 93 if err := oldProc.Release(); err != nil { 94 return 0, fmt.Errorf("couldn't release process %d: %s", pid, err) 95 } 96 return pid, nil 97 } 98 99 // Kill opens the pidfile at the specified path, attempts to read the PID and 100 // kill the process represented by that PID. If the file doesn't exist, the 101 // corresponding process doesn't exist, or the process is successfully killed, 102 // reports no error and returns the pid of the killed process (if no process 103 // was killed, returns pid 0). Otherwise, returns an error indicating the 104 // failure to kill the process. 105 // 106 // On success, deletes the pidfile from the filesystem. Otherwise, leaves it 107 // in place. 108 func Kill(pidfilePath string) (int, error) { 109 if _, err := os.Stat(pidfilePath); os.IsNotExist(err) { 110 return 0, nil 111 } 112 113 pidfile, err := ioutil.ReadFile(pidfilePath) 114 if err != nil { 115 return 0, err 116 } 117 118 pid, err := kill(pidfile, pidfilePath) 119 if err != nil { 120 return pid, err 121 } 122 123 return pid, Remove(pidfilePath) 124 }