예제 #1
0
func (t *tracerImpl) Run() (err error) {

	if t.cmd.SysProcAttr == nil {
		t.cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
	} else {
		t.cmd.SysProcAttr.Ptrace = true
	}

	runtime.LockOSThread()

	if err = t.cmd.Start(); err != nil {
		return
	}

	var waitStatus syscall.WaitStatus

	if _, err = syscall.Wait4(t.cmd.Process.Pid, &waitStatus, 0, nil); err != nil {
		return
	}

	if waitStatus.Exited() {
		return
	}

	// Set options to detect our syscalls
	if err = syscall.PtraceSetOptions(t.cmd.Process.Pid, syscall.PTRACE_O_TRACESYSGOOD); err != nil {
		return
	}

	var regsEntry, regsExit syscall.PtraceRegs
	// Get first syscall
	if err = syscall.PtraceGetRegs(t.cmd.Process.Pid, &regsEntry); err != nil {
		return
	}

	var exited bool
	for {
		if exited, err = wait_for_syscall(t.cmd.Process.Pid); exited || err != nil {
			return
		}

		// Get syscall info
		if err = syscall.PtraceGetRegs(t.cmd.Process.Pid, &regsEntry); err != nil {
			return
		}

		// Enter syscall
		t.callback(regsEntry, false)

		if exited, err = wait_for_syscall(t.cmd.Process.Pid); exited || err != nil {
			return
		}

		// Get syscall returned value
		if err = syscall.PtraceGetRegs(t.cmd.Process.Pid, &regsExit); err != nil {
			return
		}
		t.callback(regsExit, true)
	}
}
예제 #2
0
파일: init.go 프로젝트: snormore/flynn
func babySit(process *os.Process) int {
	// Forward all signals to the app
	sigchan := make(chan os.Signal, 1)
	sigutil.CatchAll(sigchan)
	go func() {
		for sig := range sigchan {
			if sig == syscall.SIGCHLD {
				continue
			}
			process.Signal(sig)
		}
	}()

	// Wait for the app to exit.  Also, as pid 1 it's our job to reap all
	// orphaned zombies.
	var wstatus syscall.WaitStatus
	for {
		pid, err := syscall.Wait4(-1, &wstatus, 0, nil)
		if err == nil && pid == process.Pid {
			break
		}
	}

	return wstatus.ExitStatus()
}
예제 #3
0
파일: driver.go 프로젝트: Gandi/docker
func (d *driver) Terminate(p *execdriver.Command) error {
	// lets check the start time for the process
	state, err := libcontainer.GetState(filepath.Join(d.root, p.ID))
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		// TODO: Remove this part for version 1.2.0
		// This is added only to ensure smooth upgrades from pre 1.1.0 to 1.1.0
		data, err := ioutil.ReadFile(filepath.Join(d.root, p.ID, "start"))
		if err != nil {
			// if we don't have the data on disk then we can assume the process is gone
			// because this is only removed after we know the process has stopped
			if os.IsNotExist(err) {
				return nil
			}
			return err
		}
		state = &libcontainer.State{InitStartTime: string(data)}
	}

	currentStartTime, err := system.GetProcessStartTime(p.ProcessConfig.Process.Pid)
	if err != nil {
		return err
	}

	if state.InitStartTime == currentStartTime {
		err = syscall.Kill(p.ProcessConfig.Process.Pid, 9)
		syscall.Wait4(p.ProcessConfig.Process.Pid, nil, 0, nil)
	}
	d.removeContainerRoot(p.ID)

	return err

}
예제 #4
0
// StartReaper starts a goroutine to reap processes if called from a process
// that has pid 1.
func StartReaper() {
	if os.Getpid() == 1 {
		glog.V(4).Infof("Launching reaper")
		go func() {
			sigs := make(chan os.Signal, 1)
			signal.Notify(sigs, syscall.SIGCHLD)
			for {
				// Wait for a child to terminate
				sig := <-sigs
				glog.V(4).Infof("Signal received: %v", sig)
				for {
					// Reap processes
					glog.V(4).Infof("Waiting to reap")
					cpid, _ := syscall.Wait4(-1, nil, syscall.WNOHANG, nil)
					if cpid < 1 {
						glog.V(4).Infof("No more processes to reap.")
						break
					}

					glog.V(4).Infof("Reaped process with pid %d", cpid)
				}
			}
		}()
	}
}
예제 #5
0
func (p *ProcessReaper) reap() {
	for {
		p.log.Debug("reap")
		var status syscall.WaitStatus
		var rusage syscall.Rusage
		wpid, err := syscall.Wait4(-1, &status, syscall.WNOHANG, &rusage)

		if wpid == 0 || (wpid == -1 && err.Error() == "no child processes") {
			break
		}

		if err != nil {
			p.log.Error("reaper-wait-error", err, lager.Data{"wpid": wpid})
			break
		}

		p.log.Info("reaped", lager.Data{"pid": wpid, "status": status, "rusage": rusage})

		if ch, ok := p.waitChan(wpid); ok {
			ch <- status.ExitStatus()
			p.log.Info("wait-once-sent-exit-status", lager.Data{"pid": wpid, "status": status, "rusage": rusage})
		} else {
			p.log.Info("wait-once-not-found", lager.Data{"pid": wpid, "status": status, "rusage": rusage})
		}
	}
}
예제 #6
0
파일: driver.go 프로젝트: jorik041/docker
func (d *driver) Terminate(c *execdriver.Command) error {
	defer d.cleanContainer(c.ID)
	// lets check the start time for the process
	active := d.activeContainers[c.ID]
	if active == nil {
		return fmt.Errorf("active container for %s does not exist", c.ID)
	}
	state, err := active.State()
	if err != nil {
		return err
	}
	pid := state.InitProcessPid

	currentStartTime, err := system.GetProcessStartTime(pid)
	if err != nil {
		return err
	}

	if state.InitProcessStartTime == currentStartTime {
		err = syscall.Kill(pid, 9)
		syscall.Wait4(pid, nil, 0, nil)
	}

	return err

}
예제 #7
0
파일: nelly.go 프로젝트: wxdublin/gop
// checkForDeath tries to clean up zombies and then checks if the
// process group is empty.
//
// It returns "true" if the answer is yes and there are no grace pings left
//
func (n *nelly) checkForDeath() bool {

	// Check if there are any zombies to eat. Process.Wait() doesn't
	// support the POSIX WNOHANG for portability reasons, so let's use
	// the syscall.Wait4() which is POSIX-only.
	var w syscall.WaitStatus
	rusage := syscall.Rusage{}
	zpid, err := syscall.Wait4(-1, &w, syscall.WNOHANG, &rusage)
	if err != nil {
		n.Error("Error in Wait4: %s", err.Error())
	}
	if zpid > 0 {
		n.Error("Ate a tasty zombie (pid was %d, status was %d)", zpid, w.ExitStatus())
	}

	if n.processGroupIsEmpty() {
		n.startGracePings--
		if n.startGracePings <= 0 {
			n.Error("Process group [%d] empty - exiting and hoping init sorts it all out", n.pgid)
			return true
		} else {
			n.Error("Process group [%d] empty - grace pings left [%d]", n.pgid, n.startGracePings)
		}
	} else {
		// We've had a good ping, no more Mr Nice Guy
		n.startGracePings = 0
	}
	return false
}
예제 #8
0
파일: os_unix.go 프로젝트: klaidliadon/oh
func monitor(active chan bool, notify chan notification) {
	for {
		monitoring := <-active
		for monitoring {
			var rusage syscall.Rusage
			var status syscall.WaitStatus
			options := syscall.WUNTRACED
			pid, err := syscall.Wait4(-1, &status, options, &rusage)
			if err != nil {
				println("Wait4:", err.Error())
			}
			if pid <= 0 {
				break
			}

			if status.Stopped() {
				if pid == task0.Job.Group {
					incoming <- syscall.SIGTSTP
				}
				continue
			}

			if status.Signaled() {
				if status.Signal() == syscall.SIGINT &&
					pid == task0.Job.Group {
					incoming <- syscall.SIGINT
				}
				status += 128
			}

			notify <- notification{pid, status}
			monitoring = <-active
		}
	}
}
예제 #9
0
// getPassword gets input hidden from the terminal from a user. This is
// accomplished by turning off terminal echo, reading input from the user and
// finally turning on terminal echo.
func getPassword() (password string, err error) {
	sig := make(chan os.Signal, 10)
	brk := make(chan bool)

	// File descriptors for stdin, stdout, and stderr.
	fd := []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()}

	// Setup notifications of termination signals to channel sig, create a process to
	// watch for these signals so we can turn back on echo if need be.
	signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT,
		syscall.SIGTERM)
	go catchSignal(fd, sig, brk)

	// Turn off the terminal echo.
	pid, err := echoOff(fd)
	if err != nil {
		return "", err
	}

	// Turn on the terminal echo and stop listening for signals.
	defer signal.Stop(sig)
	defer close(brk)
	defer echoOn(fd)

	syscall.Wait4(pid, nil, 0, nil)

	line, err := readline()
	if err == nil {
		password = strings.TrimSpace(line)
	} else {
		err = fmt.Errorf("failed during password entry: %s", err)
	}

	return password, err
}
예제 #10
0
파일: init.go 프로젝트: ably-forks/flynn
func babySit(process *os.Process) int {
	log := logger.New("fn", "babySit")

	// Forward all signals to the app
	sigchan := make(chan os.Signal, 1)
	sigutil.CatchAll(sigchan)
	go func() {
		for sig := range sigchan {
			log.Info("received signal", "type", sig)
			if sig == syscall.SIGCHLD {
				continue
			}
			log.Info("forwarding signal to command", "type", sig)
			process.Signal(sig)
		}
	}()

	// Wait for the app to exit.  Also, as pid 1 it's our job to reap all
	// orphaned zombies.
	var wstatus syscall.WaitStatus
	for {
		pid, err := syscall.Wait4(-1, &wstatus, 0, nil)
		if err == nil && pid == process.Pid {
			break
		}
	}

	if wstatus.Signaled() {
		log.Info("command exited due to signal")
		return 0
	}
	return wstatus.ExitStatus()
}
예제 #11
0
파일: tracer.go 프로젝트: yan/grace
// StartProcess kicks off the event loop and forever waits for signals from
// the traced process. This is currently done in a super-silly fashion and will
// hopefully benefit from Go channels/goroutines in the future.
func (p *Process) StartProcess() (ret int) {
	var status syscall.WaitStatus

L:
	for {
		_, err := syscall.Wait4( /*p.Pid*/ -1, &status, 0, nil)
		p.isRunning = false

		switch {
		// status == 0  means terminated??
		case status.Exited() || status == 0 || err != nil:
			ret = status.ExitStatus()
			break L
		case status.Stopped():
			if bp, hit := p.InBreakpoint(); hit {
				p.handleBreakpoint(bp)
			}

		//case status.Continued():
		//case status.CoreDump():
		//case status.Signaled():
		//case status.ExitStatus():
		//case status.StopSignal():
		//case status.TrapCause():
		default:
			// fmt.Printf("Got status: %v\n", status)
		}

		p.Continue()

	}
	return
}
예제 #12
0
파일: parent.go 프로젝트: rhenium/poe
func handleSigchld(mpid int) *resultPack {
	for {
		var status syscall.WaitStatus
		var spid int
		var err error
		spid, err = syscall.Wait4(-mpid, &status, syscall.WNOHANG|syscall.WALL, nil)
		if err != nil {
			poePanic(err, "wait4 failed")
		} else if spid == 0 {
			return nil
		}

		if spid == mpid && status.Exited() {
			return &resultPack{POE_SUCCESS, status.ExitStatus(), ""}
		} else if spid == mpid && status.Signaled() {
			return &resultPack{POE_SIGNALED, -1, fmt.Sprintf("Program terminated with signal %d (%s)", int(status.Signal()), status.Signal().String())}
		} else if status.Stopped() {
			e := status >> 16 & 0xff
			switch e {
			case PTRACE_EVENT_SECCOMP:
				if res := handleSyscall(spid); res != nil {
					return res
				}
			case syscall.PTRACE_EVENT_CLONE, syscall.PTRACE_EVENT_FORK, syscall.PTRACE_EVENT_VFORK:
				syscall.PtraceCont(spid, 0)
			default:
				syscall.PtraceCont(spid, int(status.StopSignal()))
			}
		}
	}
}
예제 #13
0
func waitForContainerToExit(dir string, containerPid int, signals chan os.Signal) (exitCode int) {
	for range signals {
		for {
			var status syscall.WaitStatus
			var rusage syscall.Rusage
			wpid, err := syscall.Wait4(-1, &status, syscall.WNOHANG, &rusage)
			if err != nil || wpid <= 0 {
				break // wait for next SIGCHLD
			}

			if wpid == containerPid {
				exitCode = status.ExitStatus()
				if status.Signaled() {
					exitCode = 128 + int(status.Signal())
				}

				ioWg.Wait() // wait for full output to be collected

				check(ioutil.WriteFile(filepath.Join(dir, "exitcode"), []byte(strconv.Itoa(exitCode)), 0700))
				return exitCode
			}
		}
	}

	panic("ran out of signals") // cant happen
}
예제 #14
0
func (c *CloneParams) CloneFrozen() (int, error) {
	pid := callClone(c)
	// TODO: clone errors?
	c.CommWriter.Close()
	c.stdhandles.Close()
	c.comm = make(chan CommStatus)
	go commReader(c.CommReader, c.comm)

	var status syscall.WaitStatus
	for {
		wpid, err := syscall.Wait4(pid, &status, 0, nil) // TODO: rusage
		if err != nil {
			return -1, os.NewSyscallError("Wait4", err)
		}
		if wpid == pid {
			break
		}
	}
	if status.Stopped() && status.StopSignal() == syscall.SIGTRAP {
		return pid, nil
	}
	if status.Exited() {
		co, ok := <-c.comm
		if ok {
			return -1, childError(co)
		}
		return -1, fmt.Errorf("DAFUQ")
	}
	err := syscall.Kill(pid, syscall.SIGKILL)
	if err != nil {
		return -1, os.NewSyscallError("Kill", err)
	}
	return -1, fmt.Errorf("traps, signals, dafuq is this")
}
예제 #15
0
func ChildWaitingFunc(pid int, sig chan *ChildWaitData) {
	var status syscall.WaitStatus
	var rusage syscall.Rusage
	result := &ChildWaitData{}
	for {
		wpid, err := syscall.Wait4(pid, &status, syscall.WUNTRACED|syscall.WCONTINUED, &rusage)
		if wpid != pid {
			continue
		}

		if status.Exited() {
			result.ExitCode = uint32(status.ExitStatus())
			break
		}
		if status.Stopped() {
			result.SuccessCode |= EF_STOPPED
			result.StopSignal = uint32(status.StopSignal())
			syscall.Kill(pid, syscall.SIGKILL)
		}
		if status.Signaled() {
			result.SuccessCode |= EF_KILLED_BY_OTHER
			result.KillSignal = uint32(status.Signal())
			break
		}
		if err != nil {
			break
		}
	}
	result.RusageCpuUser = time.Nanosecond * time.Duration(rusage.Utime.Nano())
	result.RusageCpuKernel = time.Nanosecond * time.Duration(rusage.Stime.Nano())
	sig <- result
	close(sig)
}
예제 #16
0
파일: main.go 프로젝트: slyrz/filetrace
func wait(pid int) (cpid int, status syscall.WaitStatus, err error) {
	cpid, err = syscall.Wait4(pid, &status, syscall.WALL, nil)
	if err != nil {
		return 0, 0, err
	}
	return cpid, status, nil
}
예제 #17
0
// echoOn turns back on the terminal echo.
func echoOn(fd []uintptr) {
	// Turn on the terminal echo.
	pid, e := syscall.ForkExec(sttyArg0, sttyArgvEOn, &syscall.ProcAttr{Dir: "", Files: fd})
	if e == nil {
		syscall.Wait4(pid, nil, 0, nil)
	}
}
예제 #18
0
파일: reaper.go 프로젝트: docker/containerd
// Reap reaps all child processes for the calling process and returns their
// exit information
func Reap(wait bool) (exits []Exit, err error) {
	var (
		ws  syscall.WaitStatus
		rus syscall.Rusage
	)
	flag := syscall.WNOHANG
	if wait {
		flag = 0
	}
	for {
		pid, err := syscall.Wait4(-1, &ws, flag, &rus)
		if err != nil {
			if err == syscall.ECHILD {
				return exits, nil
			}
			return exits, err
		}
		if pid <= 0 {
			return exits, nil
		}
		exits = append(exits, Exit{
			Pid:    pid,
			Status: exitStatus(ws),
		})
	}
}
예제 #19
0
파일: modules.go 프로젝트: WalterShe/wfdr
func (m *Module) IsRunning() bool {
	pid := 0
	if m.Name == "internal:shared" {
		pid = m.SyncProcess.Pid
	} else {
		pid = m.MainProcess.Pid
	}

	var waitstatus syscall.WaitStatus
	wpid, err := syscall.Wait4(pid, &waitstatus, syscall.WNOHANG|syscall.WUNTRACED, nil)
	if err != nil {
		// When would this happen?
		log.Println("Unable to get process wait status:", err)
		// Assume it is not running
		return false
	}

	// If status is not available, the pid is 0.
	if wpid == 0 {
		return true
	}
	if waitstatus.Exited() {
		delete(modules, m.Name)
		return false
	}
	return true
}
예제 #20
0
파일: checks.go 프로젝트: qanx/bmad
// Called on running checks, to determine if they have finished
// running.
//
// If the Check has not finished executing, returns false.
//
// If the Check has been running for longer than its Timeout,
// a SIGTERM (and failing that a SIGKILL) is issued to forcibly
// terminate the rogue Check process. In either case, this returns
// as if the check has not yet finished, and Reap() will need to be
// called again to fully reap the Check
//
// If the Check has finished execution (on its own, or via forced
// termination), it will return true.
//
// Once complete, some additional meta-stats for the check execution
// are appended to the check output, to be submit up to bolo
func (self *Check) Reap() bool {
	pid := self.process.Process.Pid

	var ws syscall.WaitStatus
	status, err := syscall.Wait4(pid, &ws, syscall.WNOHANG, nil)
	if err != nil {
		log.Error("Error waiting on check %s[%d]: %s", self.Name, pid, err.Error())
		return false
	}
	if status == 0 {
		// self to see if we need to sigkill due to failed sigterm
		if time.Now().After(self.started_at.Add(time.Duration(self.Timeout+2) * time.Second)) {
			log.Warn("Check %s[%d] has been running too long, sending SIGKILL", self.Name, pid)
			if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
				log.Error("Error sending SIGKILL to check %s[%d]: %s", self.Name, pid, err.Error())
			}
			self.sig_kill = true
		}
		// self to see if we need to sigterm due to self timeout expiry
		if !self.sig_kill && time.Now().After(self.started_at.Add(time.Duration(self.Timeout)*time.Second)) {
			log.Warn("Check %s[%d] has been running too long, sending SIGTERM", self.Name, pid)
			if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
				log.Error("Error sending SIGTERM to check %s[%d]: %s", self.Name, pid, err.Error())
			}
			self.sig_term = true
		}
		return false
	}

	self.ended_at = time.Now()
	self.running = false
	self.duration = time.Since(self.started_at)
	self.latency = self.started_at.Sub(self.next_run)
	self.output = string(self.stdout.Bytes())
	self.err_msg = string(self.stderr.Bytes())

	if ws.Exited() {
		self.rc = ws.ExitStatus()
	} else {
		log.Debug("Check %s[%d] exited abnormally (signaled/stopped). Setting rc to UNKNOWN", self.Name, pid)
		self.rc = UNKNOWN
	}
	if self.rc > UNKNOWN {
		log.Debug("Check %s[%d] returned with an invalid exit code. Setting rc to UNKOWN", self.Name, pid)
		self.rc = UNKNOWN
	}

	self.reschedule()

	if self.ended_at.After(self.next_run) {
		timeout_triggered := "not reached"
		if self.sig_term || self.sig_kill {
			timeout_triggered = "reached"
		}
		log.Warn("Check %s[%d] took %0.3f seconds to run, at interval %d (timeout of %d was %s)",
			self.Name, pid, self.duration.Seconds(), self.Every, self.Timeout, timeout_triggered)
	}
	return true
}
예제 #21
0
파일: hideInput.go 프로젝트: JonPulfer/pman
func echoOn(pa syscall.ProcAttr) {
	pid, err := syscall.ForkExec(sttyCmd, sttyArgvEchoOn, &pa)
	if err == nil {
		syscall.Wait4(pid, &waitStatus, 0, nil)
	} else {
		fmt.Printf("Error setting echo on: %s\n", err)
	}
}
예제 #22
0
파일: main.go 프로젝트: rdterner/debug
func wait(pid int) (wpid int, status syscall.WaitStatus, err error) {
	wpid, err = syscall.Wait4(pid, &status, syscall.WALL, nil)
	if err != nil {
		return 0, 0, err
	}
	fmt.Printf("\t\twait: wpid=%5d, status=0x%06x\n", wpid, status)
	return wpid, status, nil
}
예제 #23
0
func main() {
	flag.Parse()
	configureLog()

	// Load configuration
	conf, err := configuration.FromFile(configFilePath)
	if err != nil {
		log.Fatal(err)
	}

	eventBus := event_bus.New()

	// Wait for died children to avoid zombies
	signalChannel := make(chan os.Signal, 2)
	signal.Notify(signalChannel, os.Interrupt, syscall.SIGCHLD)
	go func() {
		for {
			sig := <-signalChannel
			if sig == syscall.SIGCHLD {
				r := syscall.Rusage{}
				syscall.Wait4(-1, nil, 0, &r)
			}
		}
	}()

	// Create StatsD client
	conf.StatsD.CreateClient()

	// Create Zookeeper connection
	zkConn := listenToZookeeper(conf, eventBus)

	// Create the storage backend
	storage, err := service.NewZKStorage(zkConn, conf.Bamboo.Zookeeper)
	if err != nil {
		log.Panicf("Failed to create ZK storage: %v", err)
	}

	appStorage, err := application.NewZKStorage(zkConn, conf.Bamboo.Zookeeper)
	if err != nil {
		log.Panicf("Failed to create application ZK storage: %v", err)
	}

	// Register handlers
	handlers := event_bus.Handlers{Conf: &conf, Storage: storage, AppStorage: appStorage}
	eventBus.Register(handlers.MarathonEventHandler)
	eventBus.Register(handlers.ServiceEventHandler)
	eventBus.Register(handlers.WeightEventHandler)
	eventBus.Publish(event_bus.MarathonEvent{EventType: "bamboo_startup", Timestamp: time.Now().Format(time.RFC3339)})

	// Handle gracefully exit
	registerOSSignals()

	// load config
	api.LoadConfig(conf)

	// Start server
	initServer(&conf, storage, appStorage, eventBus)
}
예제 #24
0
func Run(host string, port int) int {
	fmt.Printf("--> binding to %s:%d\n", host, port)

	addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		return -1
	}

	ln, err := net.ListenTCP("tcp", addr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		return -1
	}

	sigs := make(chan os.Signal, 1)

	signal.Notify(sigs, syscall.SIGCHLD)
	go func() {
		var status syscall.WaitStatus
		var rusage syscall.Rusage
		for {
			<-sigs
			_, err := syscall.Wait4(-1, &status, 0, &rusage)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s\n", err)
			}
		}
	}()

	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			continue
		}
		fmt.Printf(".")

		ret, _, errno := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
		if errno != 0 {
			return int(errno)
		}
		if ret != 0 {
			conn.Close() // parent process close
			continue
		}

		if err := handleClient(conn); err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			continue
		}

		conn.Close() // child process close
		os.Exit(0)   // child process exit
	}

	return 0
}
예제 #25
0
func processManagerLoop() {
	defer close(subscribeNotifications)
	defer close(unsubscribeNotifications)

	sigchlds := make(chan os.Signal, 1000)
	signal.Notify(sigchlds, syscall.SIGCHLD)
	defer signal.Stop(sigchlds)
	defer close(sigchlds)
	subscriptions := make(map[int]chan pidChangeNotification)

	for {
		select {
		case subscribe := <-subscribeNotifications:
			{
				_, assigned := subscriptions[subscribe.pid]
				if !assigned {
					subscriptions[subscribe.pid] = subscribe.replyChan
				} else {
					panic("Duplicate subscription for a PID")
				}
			}
		case unsubscribe := <-unsubscribeNotifications:
			{
				delete(subscriptions, unsubscribe.pid)
			}
		case <-sigchlds:
			{
				var wstatus syscall.WaitStatus
				var rusage syscall.Rusage
				waitPid, err := syscall.Wait4(-1, &wstatus, 0, &rusage)
				if err == nil {
					subscription, assigned := subscriptions[waitPid]
					if assigned {
						notification := pidChangeNotification{
							pid:     waitPid,
							wstatus: wstatus,
							rusage:  rusage,
						}
						select {
						case subscription <- notification:
							{
							}
						default:
							{
								log.Info("PID Change subscription channel full, deleting entry")
								delete(subscriptions, waitPid)
							}
						}
					} else {
						log.Info("PID notification received without actor to send it to: ", waitPid)
					}
				} else {
					log.Error("Error waiting for PID: ", err)
				}
			}
		}
	}
}
예제 #26
0
// waitPID waits for 'wpid' to exit and prints the exit status of
// 'wpid'. Returns error on failure, nil otherwise.
func waitPid(wpid int) error {
	var wstatus syscall.WaitStatus
	if *debug {
		log.Printf("Waiting for pid %d to exit", wpid)
	}
	if out, err := syscall.Wait4(wpid, &wstatus, 0, nil); out != wpid {
		return fmt.Errorf("failed while waiting for process %d to exit. error: %s", wpid, err)
	}
	return nil
}
예제 #27
0
파일: ui_unix.go 프로젝트: hail100/cli
func readPassword(pid int) string {
	rd := bufio.NewReader(os.Stdin)
	syscall.Wait4(pid, &ws, 0, nil)

	line, err := rd.ReadString('\n')
	if err == nil {
		return strings.TrimSpace(line)
	}
	return ""
}
예제 #28
0
func ShotgunZombieProcs(t *testing.T) {
	for {
		pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil)
		if err != nil {
			Fatalf(t, "Unexpected Wait4 error: %s", err)
		} else if pid == 0 {
			return
		}
	}
}
예제 #29
0
// Call calls an external command.
func (e ExternalCmd) Call(ec *EvalCtx, argVals []Value, opts map[string]Value) {
	if len(opts) > 0 {
		throw(ErrExternalCmdOpts)
	}
	if util.DontSearch(e.Name) {
		stat, err := os.Stat(e.Name)
		if err == nil && stat.IsDir() {
			// implicit cd
			if len(argVals) > 0 {
				throw(ErrCdNoArg)
			}
			cdInner(e.Name, ec)
			return
		}
	}

	files := make([]uintptr, len(ec.ports))
	for i, port := range ec.ports {
		if port == nil || port.File == nil {
			files[i] = fdNil
		} else {
			files[i] = port.File.Fd()
		}
	}

	args := make([]string, len(argVals)+1)
	for i, a := range argVals {
		// NOTE Maybe we should enfore string arguments instead of coercing all
		// args into string
		args[i+1] = ToString(a)
	}

	sys := syscall.SysProcAttr{Setpgid: ec.background}
	attr := syscall.ProcAttr{Env: os.Environ(), Files: files[:], Sys: &sys}

	path, err := ec.Search(e.Name)
	if err != nil {
		throw(err)
	}

	args[0] = path
	pid, err := syscall.ForkExec(path, args, &attr)
	if err != nil {
		throw(errors.New("forkExec: " + err.Error()))
	}

	var ws syscall.WaitStatus
	_, err = syscall.Wait4(pid, &ws, syscall.WUNTRACED, nil)

	if err != nil {
		throw(fmt.Errorf("wait: %s", err.Error()))
	} else {
		maybeThrow(NewExternalCmdExit(e.Name, ws, pid))
	}
}
예제 #30
0
func run() int {
	flag.Parse()

	runtime := flag.Args()[1] // e.g. runc
	dir := flag.Args()[2]     // bundlePath for run, processPath for exec
	containerId := flag.Args()[3]

	signals := make(chan os.Signal, 100)
	signal.Notify(signals, syscall.SIGCHLD)

	fd3 := os.NewFile(3, "/proc/self/fd/3")
	logFile := fmt.Sprintf("/proc/%d/fd/4", os.Getpid())
	logFD := os.NewFile(4, "/proc/self/fd/4")
	syncPipe := os.NewFile(5, "/proc/self/fd/5")
	pidFilePath := filepath.Join(dir, "pidfile")

	stdin, stdout, stderr, winsz := openPipes(dir)

	syncPipe.Write([]byte{0})

	var runcStartCmd *exec.Cmd
	if *tty {
		ttySlave := setupTty(stdin, stdout, pidFilePath, winsz, garden.WindowSize{Rows: *rows, Columns: *cols})
		runcStartCmd = exec.Command(runtime, "-debug", "-log", logFile, "exec", "-d", "-tty", "-console", ttySlave.Name(), "-p", fmt.Sprintf("/proc/%d/fd/0", os.Getpid()), "-pid-file", pidFilePath, containerId)
	} else {
		runcStartCmd = exec.Command(runtime, "-debug", "-log", logFile, "exec", "-p", fmt.Sprintf("/proc/%d/fd/0", os.Getpid()), "-d", "-pid-file", pidFilePath, containerId)
		runcStartCmd.Stdin = stdin
		runcStartCmd.Stdout = stdout
		runcStartCmd.Stderr = stderr
	}

	// we need to be the subreaper so we can wait on the detached container process
	system.SetSubreaper(os.Getpid())

	if err := runcStartCmd.Start(); err != nil {
		fd3.Write([]byte{2})
		return 2
	}

	var status syscall.WaitStatus
	var rusage syscall.Rusage
	_, err := syscall.Wait4(runcStartCmd.Process.Pid, &status, 0, &rusage)
	check(err)    // Start succeeded but Wait4 failed, this can only be a programmer error
	logFD.Close() // No more logs from runc so close fd

	fd3.Write([]byte{byte(status.ExitStatus())})
	if status.ExitStatus() != 0 {
		return 3 // nothing to wait for, container didn't launch
	}

	containerPid, err := parsePid(pidFilePath)
	check(err)

	return waitForContainerToExit(dir, containerPid, signals)
}