- .desktop files now parse DesktopNames: first ;-entry → XDG_SESSION_DESKTOP, full list with : separators → XDG_CURRENT_DESKTOP - AddUtmpx/Close wired into spawnSession (logins appear in who/w/last) - Credentials.Shell reads from /etc/passwd instead of hardcoded /bin/sh - Added SetDesktopVars to env.go
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,
|
|
})
|
|
}
|
|
}
|