ipn/desktop: add a new package for managing desktop sessions on Windows

This PR adds a new package, ipn/desktop, which provides a platform-agnostic
interface for enumerating desktop sessions and registering session callbacks.
Currently, it is implemented only for Windows.

Updates #14823

Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
Nick Khyl
2025-02-10 17:47:10 -06:00
committed by Nick Khyl
parent 5eacf61844
commit bc0cd512ee
7 changed files with 994 additions and 0 deletions

58
ipn/desktop/session.go Normal file
View File

@@ -0,0 +1,58 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package desktop
import (
"fmt"
"tailscale.com/ipn/ipnauth"
)
// SessionID is a unique identifier of a desktop session.
type SessionID uint
// SessionStatus is the status of a desktop session.
type SessionStatus int
const (
// ClosedSession is a session that does not exist, is not yet initialized by the OS,
// or has been terminated.
ClosedSession SessionStatus = iota
// ForegroundSession is a session that a user can interact with,
// such as when attached to a physical console or an active,
// unlocked RDP connection.
ForegroundSession
// BackgroundSession indicates that the session is locked, disconnected,
// or otherwise running without user presence or interaction.
BackgroundSession
)
// String implements [fmt.Stringer].
func (s SessionStatus) String() string {
switch s {
case ClosedSession:
return "Closed"
case ForegroundSession:
return "Foreground"
case BackgroundSession:
return "Background"
default:
panic("unreachable")
}
}
// Session is a state of a desktop session at a given point in time.
type Session struct {
ID SessionID // Identifier of the session; can be reused after the session is closed.
Status SessionStatus // The status of the session, such as foreground or background.
User ipnauth.Actor // User logged into the session.
}
// Description returns a human-readable description of the session.
func (s *Session) Description() string {
if maybeUsername, _ := s.User.Username(); maybeUsername != "" { // best effort
return fmt.Sprintf("Session %d - %q (%s)", s.ID, maybeUsername, s.Status)
}
return fmt.Sprintf("Session %d (%s)", s.ID, s.Status)
}