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.
150 lines
3.2 KiB
Go
150 lines
3.2 KiB
Go
// session — environment scanning from xsessions, wayland-sessions,
|
|
// and custom script directories. Includes desktop entry parser.
|
|
//
|
|
// Mirrors vendor/src/post_login/mod.rs:get_envs and parse_desktop_entry.
|
|
package session
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type DesktopEntry struct {
|
|
Name string
|
|
Exec string
|
|
DesktopNames string
|
|
Hidden bool
|
|
NoDisplay bool
|
|
}
|
|
|
|
func parseDesktopEntry(path string) (*DesktopEntry, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open %s: %w", path, err)
|
|
}
|
|
defer f.Close()
|
|
|
|
e := &DesktopEntry{}
|
|
inSection := false
|
|
sc := bufio.NewScanner(f)
|
|
for sc.Scan() {
|
|
line := strings.TrimSpace(sc.Text())
|
|
if line == "" || line[0] == '#' {
|
|
continue
|
|
}
|
|
if line[0] == '[' && line[len(line)-1] == ']' {
|
|
inSection = line == "[Desktop Entry]"
|
|
continue
|
|
}
|
|
if !inSection {
|
|
continue
|
|
}
|
|
eq := strings.IndexByte(line, '=')
|
|
if eq < 0 {
|
|
continue
|
|
}
|
|
k, v := line[:eq], line[eq+1:]
|
|
switch k {
|
|
case "Name":
|
|
e.Name = v
|
|
case "Exec":
|
|
e.Exec = v
|
|
case "DesktopNames":
|
|
e.DesktopNames = v
|
|
case "Hidden":
|
|
e.Hidden = strings.EqualFold(v, "true")
|
|
case "NoDisplay":
|
|
e.NoDisplay = strings.EqualFold(v, "true")
|
|
}
|
|
}
|
|
if sc.Err() != nil {
|
|
return nil, sc.Err()
|
|
}
|
|
if e.Exec == "" {
|
|
return nil, fmt.Errorf("no Exec in %s", path)
|
|
}
|
|
if e.Name == "" {
|
|
e.Name = e.Exec
|
|
}
|
|
return e, nil
|
|
}
|
|
|
|
func ScanEnvs(xSessions, wlSessions, xScripts, wlScripts string, includeTTY bool) []PostLoginEnv {
|
|
var envs []PostLoginEnv
|
|
|
|
scanDesktopDir(xSessions, "x11", &envs)
|
|
scanDesktopDir(wlSessions, "wayland", &envs)
|
|
scanScriptDir(xScripts, "x11", &envs)
|
|
scanScriptDir(wlScripts, "wayland", &envs)
|
|
|
|
if len(envs) == 0 || includeTTY {
|
|
envs = append(envs, PostLoginEnv{Kind: "tty", Title: "TTYSHELL"})
|
|
}
|
|
return envs
|
|
}
|
|
|
|
func scanDesktopDir(dir, kind string, envs *[]PostLoginEnv) {
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, e := range entries {
|
|
if e.IsDir() || filepath.Ext(e.Name()) != ".desktop" {
|
|
continue
|
|
}
|
|
path := filepath.Join(dir, e.Name())
|
|
de, err := parseDesktopEntry(path)
|
|
if err != nil || de.Hidden || de.NoDisplay {
|
|
continue
|
|
}
|
|
desktopNames, sessionDesktop := splitDesktopNames(de.DesktopNames, e.Name())
|
|
*envs = append(*envs, PostLoginEnv{
|
|
Kind: kind,
|
|
Title: de.Name,
|
|
XinitrcPath: de.Exec,
|
|
ScriptPath: de.Exec,
|
|
DesktopNames: desktopNames,
|
|
SessionDesktop: sessionDesktop,
|
|
})
|
|
}
|
|
}
|
|
|
|
func splitDesktopNames(raw, fallback string) (currentDesktop, sessionDesktop string) {
|
|
if raw == "" {
|
|
return fallback, fallback
|
|
}
|
|
parts := strings.SplitN(raw, ";", 2)
|
|
sessionDesktop = strings.TrimSpace(parts[0])
|
|
currentDesktop = strings.ReplaceAll(raw, ";", ":")
|
|
return
|
|
}
|
|
|
|
func scanScriptDir(dir, kind string, envs *[]PostLoginEnv) {
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
continue
|
|
}
|
|
info, err := e.Info()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if info.Mode()&0111 == 0 {
|
|
continue
|
|
}
|
|
path := filepath.Join(dir, e.Name())
|
|
*envs = append(*envs, PostLoginEnv{
|
|
Kind: kind,
|
|
Title: e.Name(),
|
|
XinitrcPath: path,
|
|
ScriptPath: path,
|
|
})
|
|
}
|
|
}
|