github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/install/install_nix.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 //go:build !windows 5 // +build !windows 6 7 package install 8 9 import ( 10 "io" 11 "os" 12 "path/filepath" 13 "strconv" 14 15 "github.com/keybase/client/go/lsof" 16 ) 17 18 // maybeKernelOpenFiles returns true if the kernel might currently 19 // have open files. If it returns false, the mount is definitely not 20 // in use. 21 func maybeKernelOpenFiles(mountDir string, log Log) bool { 22 // This file name is copied from kbfs/libfs/constants.go because 23 // importing that package bloats the `keybase` service binary and 24 // causes compilation issues. 25 p := filepath.Join(mountDir, ".kbfs_open_file_count") 26 f, err := os.Open(p) 27 if err != nil { 28 log.Debug("Couldn't check for open files in %s: %+v", p, err) 29 return true 30 } 31 defer f.Close() 32 33 b, err := io.ReadAll(f) 34 if err != nil { 35 log.Debug("Couldn't read the open file count in %s: %+v", p, err) 36 return true 37 } 38 39 numOpenFiles, err := strconv.ParseInt(string(b), 10, 64) 40 if err != nil { 41 log.Debug("Couldn't parse the open file count (%s) in %s: %+v", 42 string(b), p, err) 43 return true 44 } 45 46 return numOpenFiles != 0 47 } 48 49 // IsInUse returns true if the mount is in use. This may be used by the updater 50 // to determine if it's safe to apply an update and restart. 51 func IsInUse(mountDir string, log Log) bool { 52 // Shortcut to avoid expensive lsof call if KBFS tells us that 53 // there are definitely no open files. 54 if !maybeKernelOpenFiles(mountDir, log) { 55 log.Debug("Definitely no open files; skipping lsof") 56 return false 57 } 58 59 // ignore error 60 lsofResults, _ := LsofMount(mountDir, log) 61 return len(lsofResults) > 0 62 } 63 64 // LsofMount does not return an error if it was unable to lsof 65 // the mountpoint or the mountpoint does not exist. 66 func LsofMount(mountDir string, log Log) ([]CommonLsofResult, error) { 67 log.Debug("Mount dir to lsof: %s", mountDir) 68 if mountDir == "" { 69 return nil, nil 70 } 71 if _, serr := os.Stat(mountDir); os.IsNotExist(serr) { 72 log.Debug("%s, mount dir lsof target, doesn't exist", mountDir) 73 return nil, nil 74 } 75 76 log.Debug("Checking mount (lsof)") 77 processes, err := lsof.MountPoint(mountDir) 78 if err != nil { 79 // If there is an error in lsof it's ok to continue 80 // An exit status of 1 means that the mount is not in use, and is 81 // not really an error. 82 log.Debug("Continuing despite error in lsof: %s", err) 83 return nil, nil 84 } 85 var ret []CommonLsofResult 86 for _, process := range processes { 87 ret = append(ret, CommonLsofResult{process.PID, process.Command}) 88 } 89 return ret, nil 90 }