1// Copyright 2009 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// Fork, exec, wait, etc.
6
7package syscall
8
9import (
10	"internal/itoa"
11	"runtime"
12	"sync"
13	"unsafe"
14)
15
16// ForkLock is not used on plan9.
17var ForkLock sync.RWMutex
18
19// gstringb reads a non-empty string from b, prefixed with a 16-bit length in little-endian order.
20// It returns the string as a byte slice, or nil if b is too short to contain the length or
21// the full string.
22//
23//go:nosplit
24func gstringb(b []byte) []byte {
25	if len(b) < 2 {
26		return nil
27	}
28	n, b := gbit16(b)
29	if int(n) > len(b) {
30		return nil
31	}
32	return b[:n]
33}
34
35// Offset of the name field in a 9P directory entry - see UnmarshalDir() in dir_plan9.go
36const nameOffset = 39
37
38// gdirname returns the first filename from a buffer of directory entries,
39// and a slice containing the remaining directory entries.
40// If the buffer doesn't start with a valid directory entry, the returned name is nil.
41//
42//go:nosplit
43func gdirname(buf []byte) (name []byte, rest []byte) {
44	if len(buf) < 2 {
45		return
46	}
47	size, buf := gbit16(buf)
48	if size < STATFIXLEN || int(size) > len(buf) {
49		return
50	}
51	name = gstringb(buf[nameOffset:size])
52	rest = buf[size:]
53	return
54}
55
56// StringSlicePtr converts a slice of strings to a slice of pointers
57// to NUL-terminated byte arrays. If any string contains a NUL byte
58// this function panics instead of returning an error.
59//
60// Deprecated: Use SlicePtrFromStrings instead.
61func StringSlicePtr(ss []string) []*byte {
62	bb := make([]*byte, len(ss)+1)
63	for i := 0; i < len(ss); i++ {
64		bb[i] = StringBytePtr(ss[i])
65	}
66	bb[len(ss)] = nil
67	return bb
68}
69
70// SlicePtrFromStrings converts a slice of strings to a slice of
71// pointers to NUL-terminated byte arrays. If any string contains
72// a NUL byte, it returns (nil, [EINVAL]).
73func SlicePtrFromStrings(ss []string) ([]*byte, error) {
74	var err error
75	bb := make([]*byte, len(ss)+1)
76	for i := 0; i < len(ss); i++ {
77		bb[i], err = BytePtrFromString(ss[i])
78		if err != nil {
79			return nil, err
80		}
81	}
82	bb[len(ss)] = nil
83	return bb, nil
84}
85
86// readdirnames returns the names of files inside the directory represented by dirfd.
87func readdirnames(dirfd int) (names []string, err error) {
88	names = make([]string, 0, 100)
89	var buf [STATMAX]byte
90
91	for {
92		n, e := Read(dirfd, buf[:])
93		if e != nil {
94			return nil, e
95		}
96		if n == 0 {
97			break
98		}
99		for b := buf[:n]; len(b) > 0; {
100			var s []byte
101			s, b = gdirname(b)
102			if s == nil {
103				return nil, ErrBadStat
104			}
105			names = append(names, string(s))
106		}
107	}
108	return
109}
110
111// name of the directory containing names and control files for all open file descriptors
112var dupdev, _ = BytePtrFromString("#d")
113
114// forkAndExecInChild forks the process, calling dup onto 0..len(fd)
115// and finally invoking exec(argv0, argvv, envv) in the child.
116// If a dup or exec fails, it writes the error string to pipe.
117// (The pipe write end is close-on-exec so if exec succeeds, it will be closed.)
118//
119// In the child, this function must not acquire any locks, because
120// they might have been locked at the time of the fork. This means
121// no rescheduling, no malloc calls, and no new stack segments.
122// The calls to RawSyscall are okay because they are assembly
123// functions that do not grow the stack.
124//
125//go:norace
126func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, attr *ProcAttr, pipe int, rflag int) (pid int, err error) {
127	// Declare all variables at top in case any
128	// declarations require heap allocation (e.g., errbuf).
129	var (
130		r1       uintptr
131		nextfd   int
132		i        int
133		clearenv int
134		envfd    int
135		errbuf   [ERRMAX]byte
136		statbuf  [STATMAX]byte
137		dupdevfd int
138		n        int
139		b        []byte
140	)
141
142	// Guard against side effects of shuffling fds below.
143	// Make sure that nextfd is beyond any currently open files so
144	// that we can't run the risk of overwriting any of them.
145	fd := make([]int, len(attr.Files))
146	nextfd = len(attr.Files)
147	for i, ufd := range attr.Files {
148		if nextfd < int(ufd) {
149			nextfd = int(ufd)
150		}
151		fd[i] = int(ufd)
152	}
153	nextfd++
154
155	if envv != nil {
156		clearenv = RFCENVG
157	}
158
159	// About to call fork.
160	// No more allocation or calls of non-assembly functions.
161	r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv|rflag), 0, 0)
162
163	if r1 != 0 {
164		if int32(r1) == -1 {
165			return 0, NewError(errstr())
166		}
167		// parent; return PID
168		return int(r1), nil
169	}
170
171	// Fork succeeded, now in child.
172
173	// Close fds we don't need.
174	r1, _, _ = RawSyscall(SYS_OPEN, uintptr(unsafe.Pointer(dupdev)), uintptr(O_RDONLY), 0)
175	dupdevfd = int(r1)
176	if dupdevfd == -1 {
177		goto childerror
178	}
179dirloop:
180	for {
181		r1, _, _ = RawSyscall6(SYS_PREAD, uintptr(dupdevfd), uintptr(unsafe.Pointer(&statbuf[0])), uintptr(len(statbuf)), ^uintptr(0), ^uintptr(0), 0)
182		n = int(r1)
183		switch n {
184		case -1:
185			goto childerror
186		case 0:
187			break dirloop
188		}
189		for b = statbuf[:n]; len(b) > 0; {
190			var s []byte
191			s, b = gdirname(b)
192			if s == nil {
193				copy(errbuf[:], ErrBadStat.Error())
194				goto childerror1
195			}
196			if s[len(s)-1] == 'l' {
197				// control file for descriptor <N> is named <N>ctl
198				continue
199			}
200			closeFdExcept(int(atoi(s)), pipe, dupdevfd, fd)
201		}
202	}
203	RawSyscall(SYS_CLOSE, uintptr(dupdevfd), 0, 0)
204
205	// Write new environment variables.
206	if envv != nil {
207		for i = 0; i < len(envv); i++ {
208			r1, _, _ = RawSyscall(SYS_CREATE, uintptr(unsafe.Pointer(envv[i].name)), uintptr(O_WRONLY), uintptr(0666))
209
210			if int32(r1) == -1 {
211				goto childerror
212			}
213
214			envfd = int(r1)
215
216			r1, _, _ = RawSyscall6(SYS_PWRITE, uintptr(envfd), uintptr(unsafe.Pointer(envv[i].value)), uintptr(envv[i].nvalue),
217				^uintptr(0), ^uintptr(0), 0)
218
219			if int32(r1) == -1 || int(r1) != envv[i].nvalue {
220				goto childerror
221			}
222
223			r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(envfd), 0, 0)
224
225			if int32(r1) == -1 {
226				goto childerror
227			}
228		}
229	}
230
231	// Chdir
232	if dir != nil {
233		r1, _, _ = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
234		if int32(r1) == -1 {
235			goto childerror
236		}
237	}
238
239	// Pass 1: look for fd[i] < i and move those up above len(fd)
240	// so that pass 2 won't stomp on an fd it needs later.
241	if pipe < nextfd {
242		r1, _, _ = RawSyscall(SYS_DUP, uintptr(pipe), uintptr(nextfd), 0)
243		if int32(r1) == -1 {
244			goto childerror
245		}
246		pipe = nextfd
247		nextfd++
248	}
249	for i = 0; i < len(fd); i++ {
250		if fd[i] >= 0 && fd[i] < i {
251			if nextfd == pipe { // don't stomp on pipe
252				nextfd++
253			}
254			r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(nextfd), 0)
255			if int32(r1) == -1 {
256				goto childerror
257			}
258
259			fd[i] = nextfd
260			nextfd++
261		}
262	}
263
264	// Pass 2: dup fd[i] down onto i.
265	for i = 0; i < len(fd); i++ {
266		if fd[i] == -1 {
267			RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
268			continue
269		}
270		if fd[i] == i {
271			continue
272		}
273		r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(i), 0)
274		if int32(r1) == -1 {
275			goto childerror
276		}
277	}
278
279	// Pass 3: close fd[i] if it was moved in the previous pass.
280	for i = 0; i < len(fd); i++ {
281		if fd[i] >= len(fd) {
282			RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0)
283		}
284	}
285
286	// Time to exec.
287	r1, _, _ = RawSyscall(SYS_EXEC,
288		uintptr(unsafe.Pointer(argv0)),
289		uintptr(unsafe.Pointer(&argv[0])), 0)
290
291childerror:
292	// send error string on pipe
293	RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&errbuf[0])), uintptr(len(errbuf)), 0)
294childerror1:
295	errbuf[len(errbuf)-1] = 0
296	i = 0
297	for i < len(errbuf) && errbuf[i] != 0 {
298		i++
299	}
300
301	RawSyscall6(SYS_PWRITE, uintptr(pipe), uintptr(unsafe.Pointer(&errbuf[0])), uintptr(i),
302		^uintptr(0), ^uintptr(0), 0)
303
304	for {
305		RawSyscall(SYS_EXITS, 0, 0, 0)
306	}
307}
308
309// close the numbered file descriptor, unless it is fd1, fd2, or a member of fds.
310//
311//go:nosplit
312func closeFdExcept(n int, fd1 int, fd2 int, fds []int) {
313	if n == fd1 || n == fd2 {
314		return
315	}
316	for _, fd := range fds {
317		if n == fd {
318			return
319		}
320	}
321	RawSyscall(SYS_CLOSE, uintptr(n), 0, 0)
322}
323
324func cexecPipe(p []int) error {
325	e := Pipe(p)
326	if e != nil {
327		return e
328	}
329
330	fd, e := Open("#d/"+itoa.Itoa(p[1]), O_RDWR|O_CLOEXEC)
331	if e != nil {
332		Close(p[0])
333		Close(p[1])
334		return e
335	}
336
337	Close(p[1])
338	p[1] = fd
339	return nil
340}
341
342type envItem struct {
343	name   *byte
344	value  *byte
345	nvalue int
346}
347
348type ProcAttr struct {
349	Dir   string    // Current working directory.
350	Env   []string  // Environment.
351	Files []uintptr // File descriptors.
352	Sys   *SysProcAttr
353}
354
355type SysProcAttr struct {
356	Rfork int // additional flags to pass to rfork
357}
358
359var zeroProcAttr ProcAttr
360var zeroSysProcAttr SysProcAttr
361
362func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
363	var (
364		p      [2]int
365		n      int
366		errbuf [ERRMAX]byte
367		wmsg   Waitmsg
368	)
369
370	if attr == nil {
371		attr = &zeroProcAttr
372	}
373	sys := attr.Sys
374	if sys == nil {
375		sys = &zeroSysProcAttr
376	}
377
378	p[0] = -1
379	p[1] = -1
380
381	// Convert args to C form.
382	argv0p, err := BytePtrFromString(argv0)
383	if err != nil {
384		return 0, err
385	}
386	argvp, err := SlicePtrFromStrings(argv)
387	if err != nil {
388		return 0, err
389	}
390
391	destDir := attr.Dir
392	if destDir == "" {
393		wdmu.Lock()
394		destDir = wdStr
395		wdmu.Unlock()
396	}
397	var dir *byte
398	if destDir != "" {
399		dir, err = BytePtrFromString(destDir)
400		if err != nil {
401			return 0, err
402		}
403	}
404	var envvParsed []envItem
405	if attr.Env != nil {
406		envvParsed = make([]envItem, 0, len(attr.Env))
407		for _, v := range attr.Env {
408			i := 0
409			for i < len(v) && v[i] != '=' {
410				i++
411			}
412
413			envname, err := BytePtrFromString("/env/" + v[:i])
414			if err != nil {
415				return 0, err
416			}
417			envvalue := make([]byte, len(v)-i)
418			copy(envvalue, v[i+1:])
419			envvParsed = append(envvParsed, envItem{envname, &envvalue[0], len(v) - i})
420		}
421	}
422
423	// Allocate child status pipe close on exec.
424	e := cexecPipe(p[:])
425
426	if e != nil {
427		return 0, e
428	}
429
430	// Kick off child.
431	pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, dir, attr, p[1], sys.Rfork)
432
433	if err != nil {
434		if p[0] >= 0 {
435			Close(p[0])
436			Close(p[1])
437		}
438		return 0, err
439	}
440
441	// Read child error status from pipe.
442	Close(p[1])
443	n, err = Read(p[0], errbuf[:])
444	Close(p[0])
445
446	if err != nil || n != 0 {
447		if n > 0 {
448			err = NewError(string(errbuf[:n]))
449		} else if err == nil {
450			err = NewError("failed to read exec status")
451		}
452
453		// Child failed; wait for it to exit, to make sure
454		// the zombies don't accumulate.
455		for wmsg.Pid != pid {
456			Await(&wmsg)
457		}
458		return 0, err
459	}
460
461	// Read got EOF, so pipe closed on exec, so exec succeeded.
462	return pid, nil
463}
464
465type waitErr struct {
466	Waitmsg
467	err error
468}
469
470var procs struct {
471	sync.Mutex
472	waits map[int]chan *waitErr
473}
474
475// startProcess starts a new goroutine, tied to the OS
476// thread, which runs the process and subsequently waits
477// for it to finish, communicating the process stats back
478// to any goroutines that may have been waiting on it.
479//
480// Such a dedicated goroutine is needed because on
481// Plan 9, only the parent thread can wait for a child,
482// whereas goroutines tend to jump OS threads (e.g.,
483// between starting a process and running Wait(), the
484// goroutine may have been rescheduled).
485func startProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
486	type forkRet struct {
487		pid int
488		err error
489	}
490
491	forkc := make(chan forkRet, 1)
492	go func() {
493		runtime.LockOSThread()
494		var ret forkRet
495
496		ret.pid, ret.err = forkExec(argv0, argv, attr)
497		// If fork fails there is nothing to wait for.
498		if ret.err != nil || ret.pid == 0 {
499			forkc <- ret
500			return
501		}
502
503		waitc := make(chan *waitErr, 1)
504
505		// Mark that the process is running.
506		procs.Lock()
507		if procs.waits == nil {
508			procs.waits = make(map[int]chan *waitErr)
509		}
510		procs.waits[ret.pid] = waitc
511		procs.Unlock()
512
513		forkc <- ret
514
515		var w waitErr
516		for w.err == nil && w.Pid != ret.pid {
517			w.err = Await(&w.Waitmsg)
518		}
519		waitc <- &w
520		close(waitc)
521	}()
522	ret := <-forkc
523	return ret.pid, ret.err
524}
525
526// Combination of fork and exec, careful to be thread safe.
527func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
528	return startProcess(argv0, argv, attr)
529}
530
531// StartProcess wraps [ForkExec] for package os.
532func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
533	pid, err = startProcess(argv0, argv, attr)
534	return pid, 0, err
535}
536
537// Ordinary exec.
538func Exec(argv0 string, argv []string, envv []string) (err error) {
539	if envv != nil {
540		r1, _, _ := RawSyscall(SYS_RFORK, RFCENVG, 0, 0)
541		if int32(r1) == -1 {
542			return NewError(errstr())
543		}
544
545		for _, v := range envv {
546			i := 0
547			for i < len(v) && v[i] != '=' {
548				i++
549			}
550
551			fd, e := Create("/env/"+v[:i], O_WRONLY, 0666)
552			if e != nil {
553				return e
554			}
555
556			_, e = Write(fd, []byte(v[i+1:]))
557			if e != nil {
558				Close(fd)
559				return e
560			}
561			Close(fd)
562		}
563	}
564
565	argv0p, err := BytePtrFromString(argv0)
566	if err != nil {
567		return err
568	}
569	argvp, err := SlicePtrFromStrings(argv)
570	if err != nil {
571		return err
572	}
573	_, _, e1 := Syscall(SYS_EXEC,
574		uintptr(unsafe.Pointer(argv0p)),
575		uintptr(unsafe.Pointer(&argvp[0])),
576		0)
577
578	return e1
579}
580
581// WaitProcess waits until the pid of a
582// running process is found in the queue of
583// wait messages. It is used in conjunction
584// with [ForkExec]/[StartProcess] to wait for a
585// running process to exit.
586func WaitProcess(pid int, w *Waitmsg) (err error) {
587	procs.Lock()
588	ch := procs.waits[pid]
589	procs.Unlock()
590
591	var wmsg *waitErr
592	if ch != nil {
593		wmsg = <-ch
594		procs.Lock()
595		if procs.waits[pid] == ch {
596			delete(procs.waits, pid)
597		}
598		procs.Unlock()
599	}
600	if wmsg == nil {
601		// ch was missing or ch is closed
602		return NewError("process not found")
603	}
604	if wmsg.err != nil {
605		return wmsg.err
606	}
607	if w != nil {
608		*w = wmsg.Waitmsg
609	}
610	return nil
611}
612