exec_plan9.mx raw

   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  //
  23  //go:nosplit
  24  func 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
  36  const 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
  43  func 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.
  61  func 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]).
  73  func 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.
  87  func 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
 112  var 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
 126  func 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  	}
 179  dirloop:
 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  
 291  childerror:
 292  	// send error string on pipe
 293  	RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&errbuf[0])), uintptr(len(errbuf)), 0)
 294  childerror1:
 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
 312  func 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  
 324  func 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  
 342  type envItem struct {
 343  	name   *byte
 344  	value  *byte
 345  	nvalue int
 346  }
 347  
 348  type ProcAttr struct {
 349  	Dir   string    // Current working directory.
 350  	Env   []string  // Environment.
 351  	Files []uintptr // File descriptors.
 352  	Sys   *SysProcAttr
 353  }
 354  
 355  type SysProcAttr struct {
 356  	Rfork int // additional flags to pass to rfork
 357  }
 358  
 359  var zeroProcAttr ProcAttr
 360  var zeroSysProcAttr SysProcAttr
 361  
 362  func 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  
 465  type waitErr struct {
 466  	Waitmsg
 467  	err error
 468  }
 469  
 470  var 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).
 485  func 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.
 527  func 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.
 532  func 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.
 538  func 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.
 586  func 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