Black Lives Matter. Support the Equal Justice Initiative.

Source file src/syscall/exec_plan9.go

Documentation: syscall

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

View as plain text