feat: ship Rust login manager and GitLab release pipeline

Replace the legacy Go implementation as the production path, quarantine the reference tree, add deterministic release packaging and version metadata, and run verification through GitLab CI.
This commit is contained in:
2026-09-25 21:33:46 +05:30
parent 1723ab53b8
commit faf5b27328
49 changed files with 6795 additions and 747 deletions

263
legacy/go/main.go Normal file
View File

@@ -0,0 +1,263 @@
// latchd — TUI display/login manager for GNU/Linux and BSD.
//
// Supports TTY, X11, and Wayland sessions with PAM authentication.
// Go rewrite of Lemurs (Rust source under vendor/).
package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"os/signal"
"syscall"
"unsafe"
tea "github.com/charmbracelet/bubbletea"
"latchd/auth"
"latchd/config"
"latchd/login"
"latchd/session"
"latchd/tui/bg"
)
var (
Version = "0.1.0"
Commit = "unknown"
BuildTime = "unknown"
)
const defaultConfig = "/etc/latchd/config.toml"
const defaultVars = "/etc/latchd/variables.toml"
func main() {
var (
configPath string
varsPath string
ttyNum int
preview bool
noLog bool
showConf bool
printVer bool
xsessions string
wlsessions string
initPath string
)
flag.StringVar(&configPath, "config", defaultConfig, "")
flag.StringVar(&configPath, "c", defaultConfig, "")
flag.StringVar(&varsPath, "variables", defaultVars, "")
flag.StringVar(&varsPath, "v", defaultVars, "")
flag.IntVar(&ttyNum, "tty", 0, "")
flag.BoolVar(&preview, "preview", false, "")
flag.BoolVar(&noLog, "no-log", false, "")
flag.BoolVar(&showConf, "show-config", false, "")
flag.BoolVar(&printVer, "version", false, "")
flag.BoolVar(&printVer, "V", false, "")
flag.StringVar(&xsessions, "xsessions", "", "")
flag.StringVar(&wlsessions, "wlsessions", "", "")
flag.StringVar(&initPath, "initial-path", "", "")
flag.Parse()
if printVer {
fmt.Printf("latchd %s commit %s built %s\n", Version, Commit, BuildTime)
os.Exit(0)
}
cfg := config.Default()
if initPath != "" {
cfg.InitialPath = initPath
}
if xsessions != "" {
cfg.X11.SessionsPath = xsessions
}
if wlsessions != "" {
cfg.Wayland.SessionsPath = wlsessions
}
vars, _ := config.LoadVariables(varsPath)
if err := config.LoadPartial(configPath, vars, &cfg); err != nil {
if configPath != defaultConfig {
fmt.Fprintf(os.Stderr, "config: %v\n", err)
os.Exit(1)
}
}
if showConf {
fmt.Printf("tty = %d\npam_service = %q\nshell = %q\nfocus = %q\n",
cfg.TTY, cfg.PAMService, cfg.SystemShell, cfg.FocusBehaviour)
os.Exit(0)
}
if !noLog && !preview {
setupLog(cfg.MainLogPath)
}
if !preview {
if _, ok := os.LookupEnv("XDG_SESSION_TYPE"); ok {
fmt.Fprintln(os.Stderr, "latchd: already in a session — use --preview")
os.Exit(1)
}
if os.Getuid() != 0 {
fmt.Fprintf(os.Stderr, "latchd: must be root (uid %d)\n", os.Getuid())
os.Exit(1)
}
if ttyNum != 0 {
cfg.TTY = uint8(ttyNum)
}
if err := chvt(int(cfg.TTY)); err != nil {
log.Printf("chvt %d: %v", cfg.TTY, err)
}
}
for {
res := launchUI(cfg, preview)
if res == nil {
break
}
if err := spawnSession(res); err != nil {
log.Printf("session: %v", err)
}
if !preview {
chvt(int(cfg.TTY))
}
}
}
func launchUI(cfg config.Config, preview bool) *login.Result {
m := login.New(cfg, preview)
if cfg.Background.ShowBackground {
m.SetAutomata(bg.NewAutomata(80, 24))
}
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
final, err := p.Run()
if err != nil {
log.Printf("tui: %v", err)
os.Exit(1)
}
lm, ok := final.(*login.Model)
if !ok {
return nil
}
return lm.Result()
}
func spawnSession(res *login.Result) error {
if res.Preview {
log.Println("preview — skipping real session")
return nil
}
log.Printf("spawning %s → %s (%s)", res.Username, res.Env.Title, res.Env.Kind)
creds, err := auth.Validate(res.Username, res.Password, res.Config.PAMService)
if err != nil {
return fmt.Errorf("auth: %w", err)
}
defer creds.CloseSession()
if err := creds.OpenSession(); err != nil {
log.Printf("pam session warning: %v", err)
}
session.SetBasicVariables(creds.Username, creds.HomeDir, creds.Shell, res.Config.InitialPath)
if res.Env.Kind == "x11" {
session.SetDisplay(res.Config.X11.Display)
}
session.RemoveXDG()
session.SetSessionParams(res.Env.Kind)
session.SetSeatVars(res.Config.TTY)
session.SetSessionVars(creds.UID)
session.SetXDGCommonPaths(creds.HomeDir)
session.SetDesktopVars(res.Env.DesktopNames, res.Env.SessionDesktop)
sess, err := session.Spawn(&res.Env, creds.UID, creds.PrimaryGID, creds.AllGIDs, &res.Config)
if err != nil {
return fmt.Errorf("spawn: %w", err)
}
utmpx := auth.AddUtmpx(creds.Username, res.Config.TTY, uint32(sess.PID()))
defer utmpx.Close()
err = sess.Wait()
if err != nil {
log.Printf("session exited: %v", err)
}
log.Printf("session pid %d ended", sess.PID())
return nil
}
func setupLog(path string) {
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "log %s: %v\n", path, err)
os.Exit(1)
}
log.SetOutput(io.MultiWriter(f, os.Stderr))
log.SetFlags(log.LstdFlags)
}
// ── chvt ────────────────────────────────────────────────────────────────
const vtA = 0x5606
const vtW = 0x5607
const kbt = 0x4B33
const kb1 = 0x02
const kb2 = 0x01
func isCon(fd uintptr) bool {
var a int32
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, kbt, uintptr(unsafe.Pointer(&a)))
return e == 0 && (a == kb1 || a == kb2)
}
func openCon(fn string) (int, error) {
for _, fl := range []int{os.O_RDWR, os.O_RDONLY, os.O_WRONLY} {
fd, err := syscall.Open(fn, fl, 0)
if err != nil {
if err == syscall.EACCES {
continue
}
break
}
if isCon(uintptr(fd)) {
return fd, nil
}
syscall.Close(fd)
}
return 0, fmt.Errorf("openCon: %s", fn)
}
func getCon() (int, error) {
for _, p := range []string{"/dev/tty", "/dev/tty0", "/dev/vc/0", "/dev/console"} {
if fd, err := openCon(p); err == nil {
return fd, nil
}
}
for fd := 0; fd < 3; fd++ {
if isCon(uintptr(fd)) {
return fd, nil
}
}
return 0, fmt.Errorf("no console")
}
func chvt(n int) error {
fd, err := getCon()
if err != nil {
return err
}
defer syscall.Close(fd)
if _, _, e := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), vtA, uintptr(n)); e != 0 {
return fmt.Errorf("VT_ACTIVATE")
}
if _, _, e := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), vtW, uintptr(n)); e != 0 {
return fmt.Errorf("VT_WAITACTIVE")
}
return nil
}
func init() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT)
go func() {
<-ch
os.Exit(0)
}()
}