예제 #1
0
파일: zeusclient.go 프로젝트: burke/zeus
func sendCommandLineArguments(usock *unixsocket.Usock, args []string) error {
	master, slave, err := unixsocket.Socketpair(syscall.SOCK_STREAM)
	if err != nil {
		return err
	}
	usock.WriteFD(int(slave.Fd()))
	if err != nil {
		return err
	}
	slave.Close()

	go func() {
		defer master.Close()
		argAsBytes := []byte{}
		for _, arg := range args[1:] {
			argAsBytes = append(argAsBytes, []byte(arg)...)
			argAsBytes = append(argAsBytes, byte(0))
		}
		_, err = master.Write(argAsBytes)
		if err != nil {
			slog.ErrorString("Could not send arguments across: " +
				err.Error() + "\r")
			return
		}
	}()

	return nil
}
예제 #2
0
func Run(color bool) {
	if !color {
		slog.DisableColor()
		DisableErrorColor()
	}
	slog.StartingZeus()

	var tree *ProcessTree = BuildProcessTree()

	exitNow = make(chan int)

	localMasterFile, remoteMasterFile, err := unixsocket.Socketpair(syscall.SOCK_DGRAM)
	if err != nil {
		panic(err)
	}

	localMasterSocket, err := unixsocket.NewUsockFromFile(localMasterFile)
	if err != nil {
		panic(err)
	}

	quit1 := make(chan bool)
	quit2 := make(chan bool)
	quit3 := make(chan bool)

	go StartSlaveMonitor(tree, localMasterSocket, remoteMasterFile, quit1)
	go StartClientHandler(tree, quit2)
	go StartFileMonitor(tree, quit3)

	quit := make(chan bool, 1)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		// FIXME: Unprecedented levels of jank, right here.
		terminateComponents(quit1, quit2, quit3, quit)
	}()

	go func() {
		exitStatus = <-exitNow
		terminateComponents(quit1, quit2, quit3, quit)
	}()

	<-quit
	<-quit
	<-quit

	os.Exit(exitStatus)
}
예제 #3
0
파일: slavemonitor.go 프로젝트: burke/zeus
func StartSlaveMonitor(tree *ProcessTree, fileChanges <-chan []string, done chan bool) chan bool {
	quit := make(chan bool)
	go func() {
		localMasterFile, remoteMasterFile, err := unixsocket.Socketpair(syscall.SOCK_DGRAM)
		if err != nil {
			Error("Couldn't create socketpair")
		}

		monitor := &SlaveMonitor{tree, remoteMasterFile}
		defer monitor.cleanupChildren()

		localMasterSocket, err := unixsocket.NewFromFile(localMasterFile)
		if err != nil {
			Error("Couldn't Open UNIXSocket")
		}

		// We just want this unix socket to be a channel so we can select on it...
		registeringFds := make(chan int, 3)
		go func() {
			for {
				if fd, err := localMasterSocket.ReadFD(); err != nil {
					slog.Error(err)
				} else {
					registeringFds <- fd
				}
			}
		}()

		for _, slave := range monitor.tree.SlavesByName {
			go slave.Run(monitor)
		}

		for {
			select {
			case <-quit:
				done <- true
				return
			case fd := <-registeringFds:
				go monitor.slaveDidBeginRegistration(fd)
			case files := <-fileChanges:
				if len(files) > 0 {
					tree.RestartNodesWithFeatures(files)
				}
			}
		}
	}()
	return quit
}
예제 #4
0
파일: slavemonitor.go 프로젝트: jcf/zeus
func StartSlaveMonitor(tree *ProcessTree, quit chan bool) {
	monitor := &SlaveMonitor{tree}

	localMasterFile, remoteMasterFile, err := unixsocket.Socketpair(syscall.SOCK_DGRAM)
	if err != nil {
		panic(err)
	}

	localMasterSocket, err := unixsocket.NewUsockFromFile(localMasterFile)
	if err != nil {
		panic(err)
	}

	// We just want this unix socket to be a channel so we can select on it...
	registeringFds := make(chan int, 3)
	go func() {
		for {
			fd, err := localMasterSocket.ReadFD()
			if err != nil {
				fmt.Println(err)
			}
			registeringFds <- fd
		}
	}()

	for _, slave := range monitor.tree.SlavesByName {
		if slave.Parent == nil {
			go monitor.startInitialProcess(remoteMasterFile)
		} else {
			go monitor.bootSlave(slave)
		}
	}

	for {
		select {
		case <-quit:
			quit <- true
			monitor.cleanupChildren()
			return
		case fd := <-registeringFds:
			monitor.slaveDidBeginRegistration(fd)
		}
	}
}
예제 #5
0
func StartSlaveMonitor(tree *ProcessTree, quit chan bool) {
	localMasterFile, remoteMasterFile, err := unixsocket.Socketpair(syscall.SOCK_DGRAM)
	if err != nil {
		Error("Couldn't create socketpair")
	}

	monitor := &SlaveMonitor{tree, remoteMasterFile}

	localMasterSocket, err := unixsocket.NewUsockFromFile(localMasterFile)
	if err != nil {
		Error("Couldn't Open UNIXSocket")
	}

	// We just want this unix socket to be a channel so we can select on it...
	registeringFds := make(chan int, 3)
	go func() {
		for {
			fd, err := localMasterSocket.ReadFD()
			if err != nil {
				slog.Error(err)
			}
			registeringFds <- fd
		}
	}()

	for _, slave := range monitor.tree.SlavesByName {
		go slave.Run(monitor)
	}

	for {
		select {
		case <-quit:
			monitor.cleanupChildren()
			quit <- true
			return
		case fd := <-registeringFds:
			go monitor.slaveDidBeginRegistration(fd)
		}
	}
}
예제 #6
0
파일: zeusclient.go 프로젝트: burke/zeus
func Run(args []string, input io.Reader, output *os.File) int {
	if os.Getenv("RAILS_ENV") != "" {
		println("Warning: Specifying a Rails environment via RAILS_ENV has no effect for commands run with zeus.")
		println("As a safety precaution to protect you from nuking your development database,")
		println("Zeus will now cowardly refuse to proceed. Please unset RAILS_ENV and try again.")
		return 1
	}

	isTerminal := ttyutils.IsTerminal(output.Fd())

	var master, slave *os.File
	var err error
	if isTerminal {
		master, slave, err = pty.Open()
	} else {
		master, slave, err = unixsocket.Socketpair(syscall.SOCK_STREAM)
	}
	if err != nil {
		slog.ErrorString(err.Error() + "\r")
		return 1
	}

	defer master.Close()
	var oldState *ttyutils.Termios
	if isTerminal {
		oldState, err = ttyutils.MakeTerminalRaw(output.Fd())
		if err != nil {
			slog.ErrorString(err.Error() + "\r")
			return 1
		}
		defer ttyutils.RestoreTerminalState(output.Fd(), oldState)
	}

	// should this happen if we're running over a pipe? I think maybe not?
	ttyutils.MirrorWinsize(output, master)

	addr, err := net.ResolveUnixAddr("unixgram", unixsocket.ZeusSockName())
	if err != nil {
		slog.ErrorString(err.Error() + "\r")
		return 1
	}

	conn, err := net.DialUnix("unix", nil, addr)
	if err != nil {
		zerror.ErrorCantConnectToMaster()
		return 1
	}
	usock := unixsocket.New(conn)

	msg := messages.CreateCommandAndArgumentsMessage(args, os.Getpid())
	usock.WriteMessage(msg)
	err = sendCommandLineArguments(usock, args)
	if err != nil {
		slog.ErrorString(err.Error() + "\r")
		return 1
	}

	usock.WriteFD(int(slave.Fd()))
	slave.Close()

	msg, err = usock.ReadMessage()
	if err != nil {
		slog.ErrorString(err.Error() + "\r")
		return 1
	}

	parts := strings.Split(msg, "\000")
	commandPid, err := strconv.Atoi(parts[0])
	defer func() {
		if commandPid > 0 {
			// Just in case.
			syscall.Kill(commandPid, 9)
		}
	}()

	if err != nil {
		slog.ErrorString(err.Error() + "\r")
		return 1
	}

	if isTerminal {
		c := make(chan os.Signal, 1)
		handledSignals := append(append(terminatingSignals, syscall.SIGWINCH), syscall.SIGCONT)
		signal.Notify(c, handledSignals...)
		go func() {
			for sig := range c {
				if sig == syscall.SIGCONT {
					syscall.Kill(commandPid, syscall.SIGCONT)
				} else if sig == syscall.SIGWINCH {
					ttyutils.MirrorWinsize(output, master)
					syscall.Kill(commandPid, syscall.SIGWINCH)
				} else { // member of terminatingSignals
					ttyutils.RestoreTerminalState(output.Fd(), oldState)
					print("\r")
					syscall.Kill(commandPid, sig.(syscall.Signal))
					os.Exit(1)
				}
			}
		}()
	}

	var exitStatus int = -1
	if len(parts) > 2 {
		exitStatus, err = strconv.Atoi(parts[0])
		if err != nil {
			slog.ErrorString(err.Error() + "\r")
			return 1
		}
	}

	eof := make(chan bool)
	go func() {
		for {
			buf := make([]byte, 1024)
			n, err := master.Read(buf)

			if err == nil || (err == io.EOF && n > 0) {
				output.Write(buf[:n])
			} else {
				eof <- true
				break
			}

		}
	}()

	go func() {
		buf := make([]byte, 8192)
		for {
			n, err := input.Read(buf)
			if err != nil {
				eof <- true
				break
			}
			if isTerminal {
				for i := 0; i < n; i++ {
					switch buf[i] {
					case sigInt:
						syscall.Kill(commandPid, syscall.SIGINT)
					case sigQuit:
						syscall.Kill(commandPid, syscall.SIGQUIT)
					case sigTstp:
						syscall.Kill(commandPid, syscall.SIGTSTP)
						syscall.Kill(os.Getpid(), syscall.SIGTSTP)
					}
				}
			}
			master.Write(buf[:n])
		}
	}()

	<-eof

	if exitStatus == -1 {
		msg, err = usock.ReadMessage()
		if err != nil {
			slog.ErrorString(err.Error() + "\r")
			return 1
		}
		parts := strings.Split(msg, "\000")
		exitStatus, err = strconv.Atoi(parts[0])
		if err != nil {
			slog.ErrorString(err.Error() + "\r")
			return 1
		}
	}

	return exitStatus
}
예제 #7
0
파일: zeusclient.go 프로젝트: andyw8/zeus
func doRun(color bool) int {
	if !color {
		slog.DisableColor()
	}

	if os.Getenv("RAILS_ENV") != "" {
		println("Warning: Specifying a Rails environment via RAILS_ENV has no effect for commands run with zeus.")
	}

	isTerminal := ttyutils.IsTerminal(os.Stdout.Fd())

	var master, slave *os.File
	var err error
	if isTerminal {
		master, slave, err = pty.Open()
	} else {
		master, slave, err = unixsocket.Socketpair(syscall.SOCK_STREAM)
	}
	if err != nil {
		panic(err)
	}

	defer master.Close()
	if isTerminal {
		oldState, err := ttyutils.MakeTerminalRaw(os.Stdout.Fd())
		if err != nil {
			panic(err)
		}
		defer ttyutils.RestoreTerminalState(os.Stdout.Fd(), oldState)

	}

	// should this happen if we're running over a pipe? I think maybe not?
	ttyutils.MirrorWinsize(os.Stdout, master)

	addr, err := net.ResolveUnixAddr("unixgram", zeusSockName)
	if err != nil {
		panic("Can't resolve server address")
	}

	conn, err := net.DialUnix("unix", nil, addr)
	if err != nil {
		ErrorCantConnectToMaster()
	}
	usock := unixsocket.NewUsock(conn)

	msg := CreateCommandAndArgumentsMessage(os.Args[1], os.Args[2:])
	usock.WriteMessage(msg)
	usock.WriteFD(int(slave.Fd()))
	slave.Close()

	msg, err = usock.ReadMessage()
	if err != nil {
		panic(err)
	}

	parts := strings.Split(msg, "\000")
	commandPid, err := strconv.Atoi(parts[0])
	defer func() {
		if commandPid > 0 {
			// Just in case.
			syscall.Kill(commandPid, 9)
		}
	}()

	if err != nil {
		panic(err)
	}

	if isTerminal {
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGWINCH, syscall.SIGCONT)
		go func() {
			for sig := range c {
				if sig == syscall.SIGCONT {
					syscall.Kill(commandPid, syscall.SIGCONT)
				} else if sig == syscall.SIGWINCH {
					ttyutils.MirrorWinsize(os.Stdout, master)
					syscall.Kill(commandPid, syscall.SIGWINCH)
				}
			}
		}()
	}

	var exitStatus int = -1
	if len(parts) > 2 {
		exitStatus, err = strconv.Atoi(parts[0])
		if err != nil {
			panic(err)
		}
	}

	eof := make(chan bool)
	go func() {
		for {
			buf := make([]byte, 1024)
			n, err := master.Read(buf)
			if err != nil {
				eof <- true
				break
			}
			os.Stdout.Write(buf[:n])
		}
	}()

	go func() {
		buf := make([]byte, 8192)
		for {
			n, err := os.Stdin.Read(buf)
			if err != nil {
				eof <- true
				break
			}
			if isTerminal {
				for i := 0; i < n; i++ {
					switch buf[i] {
					case sigInt:
						syscall.Kill(commandPid, syscall.SIGINT)
					case sigQuit:
						syscall.Kill(commandPid, syscall.SIGQUIT)
					case sigTstp:
						syscall.Kill(commandPid, syscall.SIGTSTP)
						syscall.Kill(os.Getpid(), syscall.SIGTSTP)
					}
				}
			}
			master.Write(buf[:n])
		}
	}()

	<-eof

	if exitStatus == -1 {
		msg, err = usock.ReadMessage()
		if err != nil {
			panic(err)
		}
		parts := strings.Split(msg, "\000")
		exitStatus, err = strconv.Atoi(parts[0])
		if err != nil {
			panic(err)
		}
	}

	return exitStatus
}