github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/syscall/exec_linux.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build linux 6 7 package syscall 8 9 import ( 10 "unsafe" 11 ) 12 13 type SysProcAttr struct { 14 Chroot string // Chroot. 15 Credential *Credential // Credential. 16 Ptrace bool // Enable tracing. 17 Setsid bool // Create session. 18 Setpgid bool // Set process group ID to new pid (SYSV setpgrp) 19 Setctty bool // Set controlling terminal to fd Ctty (only meaningful if Setsid is set) 20 Noctty bool // Detach fd 0 from controlling terminal 21 Ctty int // Controlling TTY fd (Linux only) 22 Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only) 23 Cloneflags uintptr // Flags for clone calls (Linux only) 24 } 25 26 // Implemented in runtime package. 27 func runtime_BeforeFork() 28 func runtime_AfterFork() 29 30 // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. 31 // If a dup or exec fails, write the errno error to pipe. 32 // (Pipe is close-on-exec so if exec succeeds, it will be closed.) 33 // In the child, this function must not acquire any locks, because 34 // they might have been locked at the time of the fork. This means 35 // no rescheduling, no malloc calls, and no new stack segments. 36 // For the same reason compiler does not race instrument it. 37 // The calls to RawSyscall are okay because they are assembly 38 // functions that do not grow the stack. 39 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { 40 // Declare all variables at top in case any 41 // declarations require heap allocation (e.g., err1). 42 var ( 43 r1 uintptr 44 err1 Errno 45 nextfd int 46 i int 47 ) 48 49 // Guard against side effects of shuffling fds below. 50 // Make sure that nextfd is beyond any currently open files so 51 // that we can't run the risk of overwriting any of them. 52 fd := make([]int, len(attr.Files)) 53 nextfd = len(attr.Files) 54 for i, ufd := range attr.Files { 55 if nextfd < int(ufd) { 56 nextfd = int(ufd) 57 } 58 fd[i] = int(ufd) 59 } 60 nextfd++ 61 62 // About to call fork. 63 // No more allocation or calls of non-assembly functions. 64 runtime_BeforeFork() 65 r1, _, err1 = RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0) 66 if err1 != 0 { 67 runtime_AfterFork() 68 return 0, err1 69 } 70 71 if r1 != 0 { 72 // parent; return PID 73 runtime_AfterFork() 74 return int(r1), 0 75 } 76 77 // Fork succeeded, now in child. 78 79 // Parent death signal 80 if sys.Pdeathsig != 0 { 81 _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0) 82 if err1 != 0 { 83 goto childerror 84 } 85 86 // Signal self if parent is already dead. This might cause a 87 // duplicate signal in rare cases, but it won't matter when 88 // using SIGKILL. 89 r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0) 90 if r1 == 1 { 91 pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) 92 _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0) 93 if err1 != 0 { 94 goto childerror 95 } 96 } 97 } 98 99 // Enable tracing if requested. 100 if sys.Ptrace { 101 _, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0) 102 if err1 != 0 { 103 goto childerror 104 } 105 } 106 107 // Session ID 108 if sys.Setsid { 109 _, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0) 110 if err1 != 0 { 111 goto childerror 112 } 113 } 114 115 // Set process group 116 if sys.Setpgid { 117 _, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0) 118 if err1 != 0 { 119 goto childerror 120 } 121 } 122 123 // Chroot 124 if chroot != nil { 125 _, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0) 126 if err1 != 0 { 127 goto childerror 128 } 129 } 130 131 // User and groups 132 if cred := sys.Credential; cred != nil { 133 ngroups := uintptr(len(cred.Groups)) 134 groups := uintptr(0) 135 if ngroups > 0 { 136 groups = uintptr(unsafe.Pointer(&cred.Groups[0])) 137 } 138 _, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0) 139 if err1 != 0 { 140 goto childerror 141 } 142 _, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0) 143 if err1 != 0 { 144 goto childerror 145 } 146 _, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0) 147 if err1 != 0 { 148 goto childerror 149 } 150 } 151 152 // Chdir 153 if dir != nil { 154 _, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0) 155 if err1 != 0 { 156 goto childerror 157 } 158 } 159 160 // Pass 1: look for fd[i] < i and move those up above len(fd) 161 // so that pass 2 won't stomp on an fd it needs later. 162 if pipe < nextfd { 163 _, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0) 164 if err1 != 0 { 165 goto childerror 166 } 167 RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC) 168 pipe = nextfd 169 nextfd++ 170 } 171 for i = 0; i < len(fd); i++ { 172 if fd[i] >= 0 && fd[i] < int(i) { 173 _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0) 174 if err1 != 0 { 175 goto childerror 176 } 177 RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC) 178 fd[i] = nextfd 179 nextfd++ 180 if nextfd == pipe { // don't stomp on pipe 181 nextfd++ 182 } 183 } 184 } 185 186 // Pass 2: dup fd[i] down onto i. 187 for i = 0; i < len(fd); i++ { 188 if fd[i] == -1 { 189 RawSyscall(SYS_CLOSE, uintptr(i), 0, 0) 190 continue 191 } 192 if fd[i] == int(i) { 193 // dup2(i, i) won't clear close-on-exec flag on Linux, 194 // probably not elsewhere either. 195 _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0) 196 if err1 != 0 { 197 goto childerror 198 } 199 continue 200 } 201 // The new fd is created NOT close-on-exec, 202 // which is exactly what we want. 203 _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0) 204 if err1 != 0 { 205 goto childerror 206 } 207 } 208 209 // By convention, we don't close-on-exec the fds we are 210 // started with, so if len(fd) < 3, close 0, 1, 2 as needed. 211 // Programs that know they inherit fds >= 3 will need 212 // to set them close-on-exec. 213 for i = len(fd); i < 3; i++ { 214 RawSyscall(SYS_CLOSE, uintptr(i), 0, 0) 215 } 216 217 // Detach fd 0 from tty 218 if sys.Noctty { 219 _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0) 220 if err1 != 0 { 221 goto childerror 222 } 223 } 224 225 // Set the controlling TTY to Ctty 226 if sys.Setctty && sys.Ctty >= 0 { 227 _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0) 228 if err1 != 0 { 229 goto childerror 230 } 231 } 232 233 // Time to exec. 234 _, _, err1 = RawSyscall(SYS_EXECVE, 235 uintptr(unsafe.Pointer(argv0)), 236 uintptr(unsafe.Pointer(&argv[0])), 237 uintptr(unsafe.Pointer(&envv[0]))) 238 239 childerror: 240 // send error code on pipe 241 RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1)) 242 for { 243 RawSyscall(SYS_EXIT, 253, 0, 0) 244 } 245 } 246 247 // Try to open a pipe with O_CLOEXEC set on both file descriptors. 248 func forkExecPipe(p []int) (err error) { 249 err = Pipe2(p, O_CLOEXEC) 250 // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it 251 // might not be implemented. 252 if err == ENOSYS { 253 if err = Pipe(p); err != nil { 254 return 255 } 256 if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil { 257 return 258 } 259 _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC) 260 } 261 return 262 }