diff --git a/main.go b/main.go index 82e0cff..067e8d1 100644 --- a/main.go +++ b/main.go @@ -575,6 +575,87 @@ func (m *SessionManager) PeerSubmit(sessionID, peerID, prompt string) (bool, *db return true, nil } +// --- Detached Process Management --- + +func (m *SessionManager) DetachProcess(sessionID string) (bool, *dbus.Error) { + m.mu.Lock() + sess, ok := m.sessions[sessionID] + m.mu.Unlock() + if !ok { + return false, dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID)) + } + if !sess.core.Detach() { + return false, dbus.MakeFailedError(fmt.Errorf("no running process to detach")) + } + m.conn.Emit(busPath, busIface+".ProcessDetached", sessionID) + return true, nil +} + +func (m *SessionManager) ListDetached(sessionID string) ([]string, *dbus.Error) { + m.mu.Lock() + sess, ok := m.sessions[sessionID] + m.mu.Unlock() + if !ok { + return nil, dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID)) + } + infos := sess.core.ListDetached() + out := make([]string, len(infos)) + for i, info := range infos { + status := "running" + if info.Exited { + status = fmt.Sprintf("exited(%d)", info.ExitCode) + } + out[i] = fmt.Sprintf("%d\t%s\t%d\t%s", info.PID, info.Command, info.Started, status) + } + return out, nil +} + +func (m *SessionManager) SignalDetached(sessionID string, pid int32, signal string) (bool, *dbus.Error) { + m.mu.Lock() + sess, ok := m.sessions[sessionID] + m.mu.Unlock() + if !ok { + return false, dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID)) + } + var sig int + switch strings.ToUpper(signal) { + case "TERM", "SIGTERM", "15": + sig = 15 + case "KILL", "SIGKILL", "9": + sig = 9 + default: + return false, dbus.MakeFailedError(fmt.Errorf("unsupported signal: %s (use TERM or KILL)", signal)) + } + if err := sess.core.SignalDetached(int(pid), sig); err != nil { + return false, dbus.MakeFailedError(err) + } + return true, nil +} + +func (m *SessionManager) GetDetachedOutput(sessionID string, pid int32) (string, *dbus.Error) { + m.mu.Lock() + sess, ok := m.sessions[sessionID] + m.mu.Unlock() + if !ok { + return "", dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID)) + } + output, err := sess.core.GetDetachedOutput(int(pid)) + if err != nil { + return "", dbus.MakeFailedError(err) + } + return output, nil +} + +func (m *SessionManager) DismissDetached(sessionID string, pid int32) (bool, *dbus.Error) { + m.mu.Lock() + sess, ok := m.sessions[sessionID] + m.mu.Unlock() + if !ok { + return false, dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID)) + } + return sess.core.DismissDetached(int(pid)), nil +} + // --- Notifications --- func (m *SessionManager) sendNotification(title, body string) {