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