Beispiel #1
0
func (t *tether) initializeSessions() error {
	// Iterate over the Sessions and initialize them if needed
	for id, session := range t.config.Sessions {
		// make it a func so that we can use defer
		err := func() error {
			session.Lock()
			defer session.Unlock()

			if session.RunBlock {
				log.Infof("Session %s wants attach capabilities. Creating its channel", id)
				session.ClearToLaunch = make(chan struct{})
			}

			stdout, stderr, err := t.ops.SessionLog(session)
			if err != nil {
				detail := fmt.Errorf("failed to get log writer for session: %s", err)
				session.Started = detail.Error()

				return detail
			}
			session.Outwriter = stdout
			session.Errwriter = stderr
			session.Reader = dio.MultiReader()

			return nil
		}()
		if err != nil {
			return err
		}
	}
	return nil
}
Beispiel #2
0
// launch will launch the command defined in the session.
// This will return an error if the session fails to launch
func (t *tether) launch(session *SessionConfig) error {
	defer trace.End(trace.Begin("launching session " + session.ID))

	// encode the result whether success or error
	defer func() {
		extraconfig.EncodeWithPrefix(t.sink, session.Started, fmt.Sprintf("guestinfo..sessions|%s.started", session.ID))
	}()

	logwriter, err := t.ops.SessionLog(session)
	if err != nil {
		detail := fmt.Sprintf("failed to get log writer for session: %s", err)
		log.Error(detail)
		session.Started = detail

		return errors.New(detail)
	}

	// we store these outside of the session.Cmd struct so that there's consistent
	// handling between tty & non-tty paths
	session.Outwriter = logwriter
	session.Errwriter = logwriter
	session.Reader = dio.MultiReader()

	session.Cmd.Env = t.ops.ProcessEnv(session.Cmd.Env)
	session.Cmd.Stdout = session.Outwriter
	session.Cmd.Stderr = session.Errwriter
	session.Cmd.Stdin = session.Reader

	resolved, err := lookPath(session.Cmd.Path, session.Cmd.Env, session.Cmd.Dir)
	if err != nil {
		log.Errorf("Path lookup failed for %s: %s", session.Cmd.Path, err)
		session.Started = err.Error()
		return err
	}
	log.Debugf("Resolved %s to %s", session.Cmd.Path, resolved)
	session.Cmd.Path = resolved

	// Use the mutex to make creating a child and adding the child pid into the
	// childPidTable appear atomic to the reaper function. Use a anonymous function
	// so we can defer unlocking locally
	// logging is done after the function to keep the locked time as low as possible
	err = func() error {
		t.config.pidMutex.Lock()
		defer t.config.pidMutex.Unlock()

		log.Infof("Launching command %#v", session.Cmd.Args)
		if !session.Tty {
			err = session.Cmd.Start()
		} else {
			err = establishPty(session)
		}

		if err != nil {
			return err
		}

		// ChildReaper will use this channel to inform us the wait status of the child.
		t.config.pids[session.Cmd.Process.Pid] = session

		return nil
	}()

	if err != nil {
		detail := fmt.Sprintf("failed to start container process: %s", err)
		log.Error(detail)

		// Set the Started key to the undecorated error message
		session.Started = err.Error()

		return errors.New(detail)
	}

	// Set the Started key to "true" - this indicates a successful launch
	session.Started = "true"
	log.Debugf("Launched command with pid %d", session.Cmd.Process.Pid)

	return nil
}