runner

package
v2.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 52 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// TrackerKindWebsh marks an interactive Websh PTY session.
	TrackerKindWebsh = "websh"
	// TrackerKindCommand marks a non-interactive deploy shell Command execution.
	TrackerKindCommand = "command"
)

Kinds of tracked processes that may issue sudo through alpamon-pam.

View Source
const (
	IFF_UP          = 1 << 0 // Interface is up
	IFF_LOOPBACK    = 1 << 3 // Loopback interface
	IFF_POINTOPOINT = 1 << 4 // Point-to-point link
	IFF_RUNNING     = 1 << 6 // Interface is running
)
View Source
const (
	ErrPermissionDenied      = "permission denied"
	ErrOperationNotPermitted = "operation not permitted"
	ErrTooLargeDepth         = "depth has reached its limit. please try a lower depth"
	ErrInvalidArgument       = "invalid argument"
	ErrNoSuchFileOrDirectory = "no such file or directory"
	ErrFileExists            = "file exists"
	ErrDirectoryNotEmpty     = "directory not empty"
	ErrInfiniteRecursion     = "causing infinite recursion"
	ErrNotSupported          = "not supported on this platform"
)
View Source
const (

	// Client type constants for tunnel connections.
	ClientTypeCLI    = "cli"
	ClientTypeWeb    = "web"
	ClientTypeEditor = "editor"
)
View Source
const (
	ConnectionReadTimeout = 35 * time.Minute
)

Variables

This section is empty.

Functions

func CheckSystemResources

func CheckSystemResources() error

CheckSystemResources verifies that system resources are within acceptable limits for creating a new tunnel session. Returns an error if CPU or memory usage exceeds thresholds. Uses fail-open policy: if resource metrics cannot be retrieved, the session is allowed.

func CloseAllActiveFtpWorkers added in v2.2.4

func CloseAllActiveFtpWorkers()

CloseAllActiveFtpWorkers terminates all tracked WebFTP worker processes. Called during graceful shutdown so the agent reaps its own helper processes instead of relying on the systemd cgroup kill.

func CloseAllActiveTunnels

func CloseAllActiveTunnels()

CloseAllActiveTunnels closes all active tunnel connections. Called during graceful shutdown to ensure code-server and daemon processes are cleaned up.

func CloseTunnel

func CloseTunnel(sessionID string) error

CloseTunnel closes an active tunnel by session ID.

func CommitAsync

func CommitAsync(session *scheduler.Session, commissioned bool, ctxManager *agent.ContextManager)

CommitAsync commits system information asynchronously Uses ContextManager for coordinated lifecycle management

func CommitSystemInfo

func CommitSystemInfo()

func IsValidSessionID

func IsValidSessionID(sessionID string) bool

IsValidSessionID reports whether sessionID is safe to use in paths and map keys.

func RegisterCommandPID

func RegisterCommandPID(pid int, commandID, username string) func()

RegisterCommandPID is a package-level helper that registers a deploy shell Command root pid on the singleton AuthManager (if initialized) and returns an unregister closure. The closure captures the exact (pid, commandID) pair so callers cannot accidentally unregister the wrong entry, making the Register/Unregister pair leak-proof by construction. The returned closure is always safe to call — it is a no-op when the AuthManager has not been wired up yet (tests, early boot) or when the arguments would be rejected by AddPIDCommandMapping (non-positive pid, empty commandID).

func RegisterFtpWorker added in v2.2.4

func RegisterFtpWorker(sessionID string, cmd *exec.Cmd) chan struct{}

RegisterFtpWorker records a started worker process and returns a channel the caller must close once the worker exits on its own. The caller still owns the single exec.Cmd.Wait call.

func RegisterTunnel

func RegisterTunnel(sessionID string, tc *TunnelClient) bool

RegisterTunnel atomically checks for an existing tunnel and registers a new one. Returns false if a tunnel with the same session ID already exists.

func RunTunnelDaemon

func RunTunnelDaemon(socketPath string)

RunTunnelDaemon runs the tunnel daemon subprocess. It listens on a Unix domain socket and relays connections to local TCP services. This function is called by the tunnel-daemon subcommand and runs with demoted user credentials.

func SyncSystemInfo

func SyncSystemInfo(session *scheduler.Session, keys []string)

func UnregisterFtpWorker added in v2.2.4

func UnregisterFtpWorker(sessionID string, cmd *exec.Cmd)

UnregisterFtpWorker drops the worker for sessionID, but only if it still maps to the same command, so a worker that exits after a same-session worker has replaced it does not delete the newer entry.

Types

type AccessPolicy

type AccessPolicy struct {
	BlockLocalSudo bool `json:"block_local_sudo"`
}

type Address

type Address struct {
	ID            string `json:"id,omitempty"`
	Address       string `json:"address"`
	Broadcast     string `json:"broadcast"`
	InterfaceName string `json:"interface_name,omitempty"`
	Mask          string `json:"mask"`
}

func (Address) GetComparableData

func (a Address) GetComparableData() ComparableData

func (Address) GetData

func (a Address) GetData() ComparableData

func (Address) GetID

func (a Address) GetID() string

func (Address) GetKey

func (a Address) GetKey() interface{}

type AuthManager

type AuthManager struct {
	// contains filtered or unexported fields
}

func GetAuthManager

func GetAuthManager(controlClient *ControlClient, session *scheduler.Session) *AuthManager

func NewEmptyAuthManager

func NewEmptyAuthManager() *AuthManager

NewEmptyAuthManager returns an AuthManager populated just enough to exercise the PID tracker without starting the socket listener. It has no dependency on the testing package, so it is safe to keep in the shipped package; the actual test-only glue that swaps the singleton lives in the internal/runnertest package, which is firewalled by the Go compiler's internal rule and only imported from *_test.go files.

func SwapAuthManager

func SwapAuthManager(am *AuthManager) *AuthManager

SwapAuthManager installs am as the package-level singleton and returns the previously installed one so callers can restore it. It is intended to be used from internal/runnertest (which adds *testing.T cleanup); direct use in production code is not supported.

func (*AuthManager) AddPIDCommandMapping

func (am *AuthManager) AddPIDCommandMapping(pid int, commandID, username string)

AddPIDCommandMapping registers the root pid of a deploy shell Command execution so alpamon-pam can attribute a sudo call made inside the Command (or any descendant) to the originating Command.ID. It must be called before the child process can exec sudo to avoid a race where sudo arrives at the PAM module before the tracker knows about the pid.

Concurrency: multiple Commands run in parallel with distinct root pids, so entries never collide. Safe to call from any goroutine.

func (*AuthManager) AddPIDSessionMapping

func (am *AuthManager) AddPIDSessionMapping(pid int, session *SessionInfo)

AddPIDSessionMapping registers an entry for a Websh PTY session. Kind and StartedAt are filled in when unset so callers don't have to remember to populate them; CommandID is always cleared to keep the websh/command fields mutually exclusive per entry.

func (*AuthManager) HandleSudoApprovalResponse

func (am *AuthManager) HandleSudoApprovalResponse(response SudoApprovalResponse) error

HandleSudoApprovalResponse is used to handle the sudo_approval response from the alpacon-server

func (*AuthManager) LookupPID

func (am *AuthManager) LookupPID(pid int) (TrackerEntry, bool)

LookupPID returns a snapshot of the tracker entry for pid (if any). The second return value reports whether an entry was found.

func (*AuthManager) RemovePIDCommandMapping

func (am *AuthManager) RemovePIDCommandMapping(pid int, commandID string)

RemovePIDCommandMapping deletes the tracker entry for a deploy shell Command's root pid. It only removes the entry when it still has the matching command_id; callers must pass a non-empty commandID so that a pid reused by an unrelated entry (e.g. a legacy leftover from a crash) cannot be dropped accidentally.

func (*AuthManager) RemovePIDSessionMapping

func (am *AuthManager) RemovePIDSessionMapping(pid int)

func (*AuthManager) Start

func (am *AuthManager) Start(ctx context.Context)

func (*AuthManager) Stop

func (am *AuthManager) Stop()

func (*AuthManager) UpdateBlockLocalSudo

func (am *AuthManager) UpdateBlockLocalSudo(value bool)

type BaseRequest

type BaseRequest struct {
	Type string `json:"type"`
}

type CodeServerConfig

type CodeServerConfig struct {
	// Timeouts
	InstallTimeout time.Duration
	StartupTimeout time.Duration
	IdleTimeout    time.Duration

	// Paths
	UserDataDirName  string
	InstallScriptURL string

	// Daemon settings (config.yaml)
	Auth               string
	DisableTelemetry   bool
	DisableUpdateCheck bool

	// Editor settings (settings.json)
	ColorTheme                string
	WindowTitle               string
	TelemetryLevel            string
	StartupEditor             string
	RestoreWindows            string
	UpdateMode                string
	DisableWorkspaceTrust     bool
	DisableWelcomeWalkthrough bool

	// Extension gallery
	ExtensionGalleryServiceURL string
	ExtensionGalleryItemURL    string
}

CodeServerConfig holds all code-server configuration in one place.

func GetCodeServerConfig

func GetCodeServerConfig() *CodeServerConfig

GetCodeServerConfig returns the code-server configuration. On the first call, it applies the editor idle timeout from alpamon.conf.

func (*CodeServerConfig) ToArgs

func (c *CodeServerConfig) ToArgs(port int, userDataDir string) []string

ToArgs generates command line arguments for code-server.

func (*CodeServerConfig) ToConfigYAML

func (c *CodeServerConfig) ToConfigYAML() string

ToConfigYAML generates config.yaml content for code-server daemon.

func (*CodeServerConfig) ToEnv

func (c *CodeServerConfig) ToEnv(homeDir string, includeXDG bool) []string

ToEnv generates environment variables for code-server process.

func (*CodeServerConfig) ToExtensionGalleryEnv

func (c *CodeServerConfig) ToExtensionGalleryEnv() string

ToExtensionGalleryEnv generates the EXTENSIONS_GALLERY environment variable value.

func (*CodeServerConfig) ToSettingsJSON

func (c *CodeServerConfig) ToSettingsJSON() ([]byte, error)

ToSettingsJSON generates settings.json content for VS Code editor.

type CodeServerManager

type CodeServerManager struct {
	// contains filtered or unexported fields
}

CodeServerManager manages a code-server process for editor tunneling.

func NewCodeServerManager

func NewCodeServerManager(parentCtx context.Context, username, groupname string) (*CodeServerManager, error)

func (*CodeServerManager) IsRunning

func (m *CodeServerManager) IsRunning() bool

IsRunning checks if code-server is currently running.

func (*CodeServerManager) Port

func (m *CodeServerManager) Port() int

Port returns the port code-server is running on.

func (*CodeServerManager) Start

func (m *CodeServerManager) Start() error

Start installs (if needed) and starts code-server on an available port. Lock is held only for short field updates to avoid blocking Status()/Stop() during long-running install/startup operations.

func (*CodeServerManager) StartAsync

func (m *CodeServerManager) StartAsync()

StartAsync starts code-server installation/startup in background. Returns immediately. Use Status() to check progress.

func (*CodeServerManager) Status

func (m *CodeServerManager) Status() (CodeServerStatus, string)

Status returns the current status and last error message.

func (*CodeServerManager) Stop

func (m *CodeServerManager) Stop() error

Stop gracefully terminates the code-server process.

type CodeServerStatus

type CodeServerStatus string

CodeServerStatus represents the current state of code-server.

const (
	CodeServerStatusIdle       CodeServerStatus = "idle"
	CodeServerStatusInstalling CodeServerStatus = "installing"
	CodeServerStatusStarting   CodeServerStatus = "starting"
	CodeServerStatusReady      CodeServerStatus = "ready"
	CodeServerStatusError      CodeServerStatus = "error"
)

type CommandDispatcher

type CommandDispatcher interface {
	Execute(ctx context.Context, command string, args *common.CommandArgs) (int, string, error)
	HasHandler(command string) bool
}

CommandDispatcher interface to avoid circular import with executor package

type CommandResult

type CommandResult struct {
	Name             string          `json:"name,omitempty"`
	Type             string          `json:"type,omitempty"`
	Path             string          `json:"path,omitempty"`
	Dst              string          `json:"dst,omitempty"`
	Code             int             `json:"code,omitempty"`
	Size             int64           `json:"size,omitempty"`
	Children         []CommandResult `json:"children,omitempty"`
	ModTime          *time.Time      `json:"mod_time,omitempty"`
	Message          string          `json:"message,omitempty"`
	PermissionString string          `json:"permission_str,omitempty"`
	PermissionOctal  string          `json:"permission_octal,omitempty"`
	Owner            string          `json:"owner,omitempty"`
	Group            string          `json:"group,omitempty"`
	Target           string          `json:"target,omitempty"` // Symlink target path
}

func GetFtpErrorCode

func GetFtpErrorCode(command FtpCommand, result CommandResult) (CommandResult, int)

type CommandRunner

type CommandRunner struct {
	// contains filtered or unexported fields
}

CommandRunner executes commands received from the server

func NewCommandRunner

func NewCommandRunner(wsClient *WebsocketClient, apiSession *scheduler.Session, command protocol.Command, data protocol.CommandData, dispatcher CommandDispatcher) *CommandRunner

func (*CommandRunner) Run

func (cr *CommandRunner) Run(ctx context.Context) error

type ComparableData

type ComparableData interface {
	GetID() string
	GetKey() interface{}
	GetData() ComparableData           // For transmission (includes all raw data)
	GetComparableData() ComparableData // For comparison (excludes fields not stored by server)
}

Defines the ComparableData interface for comparing different types. Ensures data retrieval for each key, excluding the ID field, while minimizing the use of reflection for better performance.

type ControlClient

type ControlClient struct {
	Conn *websocket.Conn
	// contains filtered or unexported fields
}

ControlClient handles WebSocket connection for control messages (sudo_approval, etc.)

func NewControlClient

func NewControlClient() *ControlClient

NewControlClient creates a new ControlClient

func (*ControlClient) Close

func (cc *ControlClient) Close()

Close cleanly closes the WebSocket connection

func (*ControlClient) CloseAndReconnect

func (cc *ControlClient) CloseAndReconnect(ctx context.Context)

CloseAndReconnect closes current connection and reconnects

func (*ControlClient) Connect

func (cc *ControlClient) Connect()

Connect establishes WebSocket connection to control endpoint

func (*ControlClient) GetWSPath

func (cc *ControlClient) GetWSPath() string

GetWSPath returns the WebSocket URL for control endpoint

func (*ControlClient) HandleMessage

func (cc *ControlClient) HandleMessage(message []byte)

HandleMessage processes incoming control messages

func (*ControlClient) IsConnected

func (cc *ControlClient) IsConnected() bool

IsConnected returns whether the client is connected

func (*ControlClient) RunForever

func (cc *ControlClient) RunForever(ctx context.Context)

RunForever maintains the control WebSocket connection and handles messages

func (*ControlClient) WriteJSON

func (cc *ControlClient) WriteJSON(data interface{}) error

WriteJSON sends JSON data through the WebSocket connection

type ControlMessage

type ControlMessage struct {
	Query string          `json:"query"`
	Data  json.RawMessage `json:"data"`
}

ControlMessage represents the wrapper message from alpacon-server via Redis

type Disk

type Disk struct {
	ID           string `json:"id,omitempty"`
	Name         string `json:"name"`
	SerialNumber string `json:"serial_number"`
	Label        string `json:"label"`
}

func (Disk) GetComparableData

func (d Disk) GetComparableData() ComparableData

func (Disk) GetData

func (d Disk) GetData() ComparableData

func (Disk) GetID

func (d Disk) GetID() string

func (Disk) GetKey

func (d Disk) GetKey() interface{}

type FtpClient

type FtpClient struct {
	// contains filtered or unexported fields
}

func NewFtpClient

func NewFtpClient(data FtpConfigData) *FtpClient

func (*FtpClient) RunFtpBackground

func (fc *FtpClient) RunFtpBackground()

type FtpCommand

type FtpCommand string
const (
	List  FtpCommand = "list"
	Mkd   FtpCommand = "mkd"
	Cwd   FtpCommand = "cwd"
	Pwd   FtpCommand = "pwd"
	Dele  FtpCommand = "dele"
	Rmd   FtpCommand = "rmd"
	Mv    FtpCommand = "mv"
	Cp    FtpCommand = "cp"
	Chmod FtpCommand = "chmod"
	Chown FtpCommand = "chown"
)

type FtpConfigData

type FtpConfigData struct {
	URL           string
	ServerURL     string
	HomeDirectory string
	Logger        logger.FtpLogger
}

type FtpContent

type FtpContent struct {
	Command FtpCommand `json:"command"`
	Data    FtpData    `json:"data"`
}

type FtpData

type FtpData struct {
	Path           string `json:"path,omitempty"`
	Depth          int    `json:"depth,omitempty"`
	Recursive      bool   `json:"recursive,omitempty"`
	ShowHidden     bool   `json:"show_hidden,omitempty"`
	AllowOverwrite bool   `json:"allow_overwrite,omitempty"`
	Src            string `json:"src,omitempty"`
	Dst            string `json:"dst,omitempty"`
	Mode           string `json:"mode,omitempty"`
	Username       string `json:"username,omitempty"`
	Groupname      string `json:"groupname,omitempty"`
}

type FtpResult

type FtpResult struct {
	Command FtpCommand    `json:"command"`
	Success bool          `json:"success"`
	Code    int           `json:"code,omitempty"`
	Data    CommandResult `json:"data,omitempty"`
}

type GroupData

type GroupData struct {
	ID        string `json:"id,omitempty"`
	GID       int    `json:"gid"`
	GroupName string `json:"groupname"`
}

func (GroupData) GetComparableData

func (g GroupData) GetComparableData() ComparableData

func (GroupData) GetData

func (g GroupData) GetData() ComparableData

func (GroupData) GetID

func (g GroupData) GetID() string

func (GroupData) GetKey

func (g GroupData) GetKey() interface{}

type Interface

type Interface struct {
	ID        string `json:"id,omitempty"`
	Name      string `json:"name"`
	Mac       string `json:"mac"`
	Type      int    `json:"type"`
	Flags     int    `json:"flags"`
	MTU       int    `json:"mtu"`
	LinkSpeed int    `json:"link_speed"`
}

func (Interface) GetComparableData

func (i Interface) GetComparableData() ComparableData

func (Interface) GetData

func (i Interface) GetData() ComparableData

func (Interface) GetID

func (i Interface) GetID() string

func (Interface) GetKey

func (i Interface) GetKey() interface{}

type IsAlpconRequest

type IsAlpconRequest struct {
	Type      string `json:"type"`
	Username  string `json:"username"`
	Groupname string `json:"groupname"`
	PID       int    `json:"pid"`
	PPID      int    `json:"ppid"`
}

type IsAlpconResponse

type IsAlpconResponse struct {
	Type         string `json:"type"`
	Username     string `json:"username"`
	Groupname    string `json:"groupname"`
	PID          int    `json:"pid"`
	PPID         int    `json:"ppid"`
	IsAlpconUser bool   `json:"is_alpacon_user"`
}

type MFAResponse

type MFAResponse struct {
	RequestID    string `json:"request_id"`
	SessionID    string `json:"session_id"`
	Username     string `json:"username"`
	Groupname    string `json:"groupname"`
	PID          int    `json:"pid"`
	PPID         int    `json:"ppid"`
	IsAlpconUser bool   `json:"is_alpacon_user"`
	Success      bool   `json:"success"`
}

type OSData

type OSData struct {
	ID           string `json:"id,omitempty"`
	Name         string `json:"name"`
	Version      string `json:"version"`
	Major        int    `json:"major"`
	Minor        int    `json:"minor"`
	Patch        int    `json:"patch"`
	Platform     string `json:"platform"`
	PlatformLike string `json:"platform_like"`
}

func (OSData) GetComparableData

func (o OSData) GetComparableData() ComparableData

func (OSData) GetData

func (o OSData) GetData() ComparableData

func (OSData) GetID

func (o OSData) GetID() string

func (OSData) GetKey

func (o OSData) GetKey() interface{}

type Partition

type Partition struct {
	ID          string   `json:"id,omitempty"`
	MountPoints []string `json:"mount_points"`
	Name        string   `json:"name"`
	DiskName    string   `json:"disk_name"`
	Fstype      string   `json:"fs_type"`
	IsVirtual   bool     `json:"is_virtual"`
}

func (Partition) GetComparableData

func (p Partition) GetComparableData() ComparableData

func (Partition) GetData

func (p Partition) GetData() ComparableData

func (Partition) GetID

func (p Partition) GetID() string

func (Partition) GetKey

func (p Partition) GetKey() interface{}

type PtyClient

type PtyClient struct {
	// contains filtered or unexported fields
}

func NewPtyClient

func NewPtyClient(data protocol.CommandData, apiSession *scheduler.Session, manager *TerminalManager) *PtyClient

func (*PtyClient) Refresh

func (pc *PtyClient) Refresh() error

Refresh sends SIGWINCH to the PTY foreground process group to force a terminal redraw without changing the terminal size. It uses TIOCGPGRP to look up the foreground process group from the PTY fd, ensuring that full-screen programs (vim, less, top, etc.) also receive the signal.

func (*PtyClient) Resize

func (pc *PtyClient) Resize(rows, cols uint16) error

Resize is the exported version of resize for external packages

func (*PtyClient) RunPtyBackground

func (pc *PtyClient) RunPtyBackground()

type ServerData

type ServerData struct {
	Version    string  `json:"version"`
	PamVersion string  `json:"pam_version,omitempty"`
	Load       float64 `json:"load"`
}

type SessionInfo

type SessionInfo struct {
	Kind      string
	SessionID string
	CommandID string
	Username  string
	PID       int
	StartedAt time.Time
	PtyClient *PtyClient
	Requests  map[string]*SudoRequest
}

SessionInfo tracks a process (Websh PTY or deploy shell Command) that alpamon-pam may later encounter by walking the ppid chain. The same map holds both kinds of entries so the PAM lookup logic stays single-path.

Exactly one of SessionID or CommandID is populated, determined by Kind:

  • Kind == TrackerKindWebsh: SessionID set, CommandID empty.
  • Kind == TrackerKindCommand: CommandID set, SessionID empty.

Legacy entries created before the Kind field was introduced are treated as websh entries for backward compatibility (see effectiveKind).

type SudoApprovalRequest

type SudoApprovalRequest struct {
	RequestID    string `json:"request_id"`
	Type         string `json:"type"`
	Username     string `json:"username"`
	Groupname    string `json:"groupname"`
	PID          int    `json:"pid"`
	PPID         int    `json:"ppid"`
	Command      string `json:"command"`
	IsAlpconUser bool   `json:"is_alpacon_user"`
	SessionID    string `json:"session_id,omitempty"`
	CommandID    string `json:"command_id,omitempty"`
}

type SudoApprovalResponse

type SudoApprovalResponse struct {
	RequestID    string `json:"request_id"`
	Type         string `json:"type"`
	Username     string `json:"username"`
	Groupname    string `json:"groupname"`
	PID          int    `json:"pid"`
	PPID         int    `json:"ppid"`
	Command      string `json:"command"`
	IsAlpconUser bool   `json:"is_alpacon_user"`
	SessionID    string `json:"session_id,omitempty"`
	CommandID    string `json:"command_id,omitempty"`
	Approved     bool   `json:"approved"`
	Reason       string `json:"reason"`
	// ErrorCode is an optional machine-readable denial code (e.g.
	// SUDO_NO_WORKSESSION_POLICY) from alpacon-server. Its value is forwarded
	// unchanged to the auth socket so the PAM module / approval plugin can show
	// a specific reason. omitempty omits the key when the server doesn't send
	// it, keeping older socket clients unaffected.
	ErrorCode string `json:"error_code,omitempty"`
}

type SudoRequest

type SudoRequest struct {
	RequestID  string
	Connection net.Conn
}

type SyncHashes

type SyncHashes map[string]string

SyncHashes maps sync category keys to their SHA-256 content hashes.

type Syncer

type Syncer interface {
	Key() string
	Collect() (any, error)
	Def() commitDef
	ComputeHash(data any) string
}

Syncer encapsulates per-category sync logic.

type SystemData

type SystemData struct {
	ID               string `json:"id,omitempty"`
	UUID             string `json:"uuid"`
	CPUType          string `json:"cpu_type"`
	CPUBrand         string `json:"cpu_brand"`
	CPUPhysicalCores int    `json:"cpu_physical_cores"`
	CPULogicalCores  int    `json:"cpu_logical_cores"`
	PhysicalMemory   uint64 `json:"physical_memory"`
	HardwareVendor   string `json:"hardware_vendor"`
	HardwareModel    string `json:"hardware_model"`
	HardwareSerial   string `json:"hardware_serial"`
	ComputerName     string `json:"computer_name"`
	Hostname         string `json:"hostname"`
	LocalHostname    string `json:"local_hostname"`
}

func (SystemData) GetComparableData

func (s SystemData) GetComparableData() ComparableData

func (SystemData) GetData

func (s SystemData) GetData() ComparableData

func (SystemData) GetID

func (s SystemData) GetID() string

func (SystemData) GetKey

func (s SystemData) GetKey() interface{}

type TerminalManager

type TerminalManager struct {
	// contains filtered or unexported fields
}

TerminalManager manages PTY session lifecycle in a single place.

func NewTerminalManager

func NewTerminalManager() *TerminalManager

NewTerminalManager creates a new TerminalManager.

func (*TerminalManager) Get

func (m *TerminalManager) Get(sessionID string) *PtyClient

Get returns the PtyClient for the given session ID, or nil if not found.

func (*TerminalManager) Refresh

func (m *TerminalManager) Refresh(sessionID string) error

Refresh sends SIGWINCH to the terminal for the given session ID. It holds a read lock for the duration of the signal syscall so that a concurrent close/Remove cannot tear down the PTY mid-operation.

func (*TerminalManager) Register

func (m *TerminalManager) Register(sessionID string, client *PtyClient)

Register adds a PtyClient to the manager.

func (*TerminalManager) Remove

func (m *TerminalManager) Remove(sessionID string)

Remove deletes a PtyClient from the manager.

func (*TerminalManager) Resize

func (m *TerminalManager) Resize(sessionID string, rows, cols uint16) error

Resize resizes the terminal for the given session ID. It holds a read lock for the duration of the resize syscall so that a concurrent close/Remove cannot tear down the PTY mid-operation.

type TimeData

type TimeData struct {
	ID       string `json:"id,omitempty"`
	Datetime string `json:"datetime"`
	BootTime uint64 `json:"boot_time"`
	Timezone string `json:"timezone"`
	Uptime   uint64 `json:"uptime"`
}

func (TimeData) GetComparableData

func (t TimeData) GetComparableData() ComparableData

func (TimeData) GetData

func (t TimeData) GetData() ComparableData

func (TimeData) GetID

func (t TimeData) GetID() string

func (TimeData) GetKey

func (t TimeData) GetKey() interface{}

type TrackerEntry

type TrackerEntry struct {
	Kind      string
	SessionID string
	CommandID string
	Username  string
	PID       int
	StartedAt time.Time
}

TrackerEntry is a read-only snapshot of a tracker entry, returned by LookupPID for callers (e.g. tests) that need to inspect state without taking the AuthManager's lock themselves.

type TunnelClient

type TunnelClient struct {
	// contains filtered or unexported fields
}

TunnelClient manages the smux-multiplexed tunnel connection to the proxy server. It accepts streams from the server and relays them to local services.

func GetActiveTunnel

func GetActiveTunnel(sessionID string) (*TunnelClient, bool)

GetActiveTunnel returns an active tunnel by session ID.

func NewTunnelClient

func NewTunnelClient(sessionID, clientType string, targetPort int, username, groupname, url string) *TunnelClient

NewTunnelClient creates a new tunnel client for the given WebSocket URL.

func (*TunnelClient) Close

func (tc *TunnelClient) Close()

Close cleanly shuts down the tunnel connection. Safe to call multiple times; only the first call performs cleanup.

func (*TunnelClient) RunTunnelBackground

func (tc *TunnelClient) RunTunnelBackground()

RunTunnelBackground starts the tunnel connection in a goroutine. The caller must register the tunnel via RegisterTunnel before calling this method.

type UserData

type UserData struct {
	ID               string   `json:"id,omitempty"`
	UID              int      `json:"uid"`
	GID              int      `json:"gid"`
	Username         string   `json:"username"`
	Description      string   `json:"description"`
	Directory        string   `json:"directory"`
	Shell            string   `json:"shell"`
	ShadowExpireDate *int64   `json:"shadow_expire_date,omitempty"` // /etc/shadow: raw expiration date (days since epoch)
	ValidShells      []string `json:"valid_shells,omitempty"`       // /etc/shells: full list of valid login shells
}

func (UserData) GetComparableData

func (u UserData) GetComparableData() ComparableData

GetComparableData returns data for comparison, excluding fields not stored by server. ValidShells is excluded because the server doesn't store it (system-wide, rarely changes). ShadowExpireDate is included because the server stores it for real-time expiration checks.

func (UserData) GetData

func (u UserData) GetData() ComparableData

func (UserData) GetID

func (u UserData) GetID() string

func (UserData) GetKey

func (u UserData) GetKey() interface{}

type WebsocketClient

type WebsocketClient struct {
	Conn *websocket.Conn

	RestartChan          chan struct{}
	ShutDownChan         chan struct{}
	CollectorRestartChan chan struct{}
	// contains filtered or unexported fields
}

func NewWebsocketClient

func NewWebsocketClient(session *scheduler.Session, ctxManager *agent.ContextManager, workerPool *pool.Pool) *WebsocketClient

func (*WebsocketClient) Close

func (wc *WebsocketClient) Close()

Cleanly close the websocket connection by sending a close message Do not close quitChan, as the purpose here is to disconnect the WebSocket, not to terminate RunForever.

func (*WebsocketClient) CloseAndReconnect

func (wc *WebsocketClient) CloseAndReconnect(ctx context.Context)

func (*WebsocketClient) CommandRequestHandler

func (wc *WebsocketClient) CommandRequestHandler(message []byte)

func (*WebsocketClient) Connect

func (wc *WebsocketClient) Connect()

func (*WebsocketClient) ReadMessage

func (wc *WebsocketClient) ReadMessage() (messageType int, message []byte, err error)

func (*WebsocketClient) Restart

func (wc *WebsocketClient) Restart()

func (*WebsocketClient) RestartCollector

func (wc *WebsocketClient) RestartCollector()

func (*WebsocketClient) RunForever

func (wc *WebsocketClient) RunForever(ctx context.Context)

func (*WebsocketClient) SendPingQuery

func (wc *WebsocketClient) SendPingQuery() error

func (*WebsocketClient) SendPongResponse

func (wc *WebsocketClient) SendPongResponse() error

func (*WebsocketClient) SetDispatcher

func (wc *WebsocketClient) SetDispatcher(dispatcher CommandDispatcher)

SetDispatcher sets the dispatcher for handling commands with dispatcher

func (*WebsocketClient) SetOnAuthenticated

func (wc *WebsocketClient) SetOnAuthenticated(fn func())

SetOnAuthenticated registers a callback invoked from RunForever after the first successful ReadMessage of each connection. Firing here rather than at dial-time gives the migration watchdog a strong "the new workspace really did accept us" signal: an upgrade-then-immediate-close scenario (auth rejected by a downstream proxy, rate-limit, etc.) never produces a ReadMessage and so never confirms.

The callback must be idempotent — it fires on every reconnect, not only the first one. Passing nil clears it. Safe to call from any goroutine.

func (*WebsocketClient) ShutDown

func (wc *WebsocketClient) ShutDown()

func (*WebsocketClient) WriteJSON

func (wc *WebsocketClient) WriteJSON(data interface{}) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL