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 }
// 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 }