github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/osutil/osutil_unix.go (about) 1 // Copyright 2017 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 //go:build freebsd || netbsd || openbsd || linux || darwin 5 6 package osutil 7 8 import ( 9 "fmt" 10 "io" 11 "os" 12 "os/signal" 13 "path/filepath" 14 "strconv" 15 "syscall" 16 ) 17 18 // ProcessTempDir creates a new temp dir in where and returns its path and an unique index. 19 // It also cleans up old, unused temp dirs after dead processes. 20 func ProcessTempDir(where string) (string, error) { 21 lk := filepath.Join(where, "instance-lock") 22 lkf, err := syscall.Open(lk, syscall.O_RDWR|syscall.O_CREAT, DefaultFilePerm) 23 if err != nil { 24 return "", err 25 } 26 defer syscall.Close(lkf) 27 if err := syscall.Flock(lkf, syscall.LOCK_EX); err != nil { 28 return "", err 29 } 30 defer syscall.Flock(lkf, syscall.LOCK_UN) 31 32 for i := 0; i < 1e3; i++ { 33 path := filepath.Join(where, fmt.Sprintf("instance-%v", i)) 34 pidfile := filepath.Join(path, ".pid") 35 err := os.Mkdir(path, DefaultDirPerm) 36 if os.IsExist(err) { 37 // Try to clean up. 38 if cleanupTempDir(path, pidfile) { 39 i-- 40 } 41 continue 42 } 43 if err != nil { 44 return "", err 45 } 46 if err := WriteFile(pidfile, []byte(strconv.Itoa(syscall.Getpid()))); err != nil { 47 return "", err 48 } 49 return path, nil 50 } 51 return "", fmt.Errorf("too many live instances") 52 } 53 54 func cleanupTempDir(path, pidfile string) bool { 55 data, err := os.ReadFile(pidfile) 56 if err == nil && len(data) > 0 { 57 pid, err := strconv.Atoi(string(data)) 58 if err == nil && pid > 1 { 59 if err := syscall.Kill(pid, 0); err == syscall.ESRCH { 60 if os.Remove(pidfile) == nil { 61 return os.RemoveAll(path) == nil 62 } 63 } 64 } 65 } 66 // If err != nil, assume that the pid file is not created yet. 67 return false 68 } 69 70 // HandleInterrupts closes shutdown chan on first SIGINT 71 // (expecting that the program will gracefully shutdown and exit) 72 // and terminates the process on third SIGINT. 73 func HandleInterrupts(shutdown chan struct{}) { 74 go func() { 75 c := make(chan os.Signal, 3) 76 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 77 <-c 78 close(shutdown) 79 fmt.Fprint(os.Stderr, "SIGINT: shutting down...\n") 80 <-c 81 fmt.Fprint(os.Stderr, "SIGINT: shutting down harder...\n") 82 <-c 83 fmt.Fprint(os.Stderr, "SIGINT: terminating\n") 84 os.Exit(int(syscall.SIGINT)) 85 }() 86 } 87 88 func LongPipe() (io.ReadCloser, io.WriteCloser, error) { 89 r, w, err := os.Pipe() 90 if err != nil { 91 return nil, nil, fmt.Errorf("failed to create pipe: %w", err) 92 } 93 prolongPipe(r, w) 94 return r, w, err 95 } 96 97 // ProcessExitStatus returns process exit status. 98 // This is here only because of fuchsia that does not implement WaitStatus. 99 func ProcessExitStatus(ps *os.ProcessState) int { 100 return ps.Sys().(syscall.WaitStatus).ExitStatus() 101 } 102 103 // CreateMemMappedFile creates a temp file with the requested size and maps it into memory. 104 func CreateMemMappedFile(size int) (f *os.File, mem []byte, err error) { 105 f, err = CreateSharedMemFile(size) 106 if err != nil { 107 return 108 } 109 if err = f.Truncate(int64(size)); err != nil { 110 err = fmt.Errorf("failed to truncate shared mem file: %w", err) 111 CloseSharedMemFile(f) 112 return 113 } 114 115 mem, err = syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) 116 if err != nil { 117 err = fmt.Errorf("failed to mmap shm file: %w", err) 118 CloseSharedMemFile(f) 119 } 120 return 121 } 122 123 // CloseMemMappedFile destroys memory mapping created by CreateMemMappedFile. 124 func CloseMemMappedFile(f *os.File, mem []byte) error { 125 err1 := syscall.Munmap(mem) 126 err2 := CloseSharedMemFile(f) 127 switch { 128 case err1 != nil: 129 return err1 130 case err2 != nil: 131 return err2 132 default: 133 return nil 134 } 135 }