1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-04 21:02:06 -04:00

dms-greeter: Update dankinstall greeter automation w/distro packages

This commit is contained in:
purian23
2026-02-23 15:36:17 -05:00
committed by bbedward
parent 93ed96a789
commit 7276f295fc
10 changed files with 289 additions and 25 deletions

View File

@@ -17,7 +17,6 @@ import (
"github.com/sblinch/kdl-go/document"
)
// DetectDMSPath checks for DMS installation following XDG Base Directory specification
func DetectDMSPath() (string, error) {
return config.LocateDMSConfig()
}
@@ -37,7 +36,6 @@ func DetectGreeterGroup() string {
return "greeter"
}
// DetectCompositors checks which compositors are installed
func DetectCompositors() []string {
var compositors []string
@@ -51,7 +49,6 @@ func DetectCompositors() []string {
return compositors
}
// PromptCompositorChoice asks user to choose between compositors
func PromptCompositorChoice(compositors []string) (string, error) {
fmt.Println("\nMultiple compositors detected:")
for i, comp := range compositors {
@@ -292,11 +289,9 @@ func TryInstallGreeterPackage(logFunc func(string), sudoPassword string) bool {
// CopyGreeterFiles installs the dms-greeter wrapper and sets up cache directory
func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPassword string) error {
// Check if dms-greeter is already in PATH
if utils.CommandExists("dms-greeter") {
logFunc("✓ dms-greeter wrapper already installed")
} else {
// Install the wrapper script
assetsDir := filepath.Join(dmsPath, "Modules", "Greetd", "assets")
wrapperSrc := filepath.Join(assetsDir, "dms-greeter")
@@ -333,7 +328,6 @@ func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPass
}
}
// Create cache directory with proper permissions
cacheDir := "/var/cache/dms-greeter"
if err := runSudoCmd(sudoPassword, "mkdir", "-p", cacheDir); err != nil {
return fmt.Errorf("failed to create cache directory: %w", err)
@@ -354,14 +348,90 @@ func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPass
return nil
}
// EnsureACLInstalled installs the acl package (setfacl/getfacl) if not already present
func EnsureACLInstalled(logFunc func(string), sudoPassword string) error {
if utils.CommandExists("setfacl") {
return nil
}
logFunc("setfacl not found installing acl package...")
osInfo, err := distros.GetOSInfo()
if err != nil {
return fmt.Errorf("failed to detect OS: %w", err)
}
config, exists := distros.Registry[osInfo.Distribution.ID]
if !exists {
return fmt.Errorf("unsupported distribution for automatic acl installation: %s", osInfo.Distribution.ID)
}
ctx := context.Background()
var installCmd *exec.Cmd
switch config.Family {
case distros.FamilyArch:
if sudoPassword != "" {
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "pacman -S --needed --noconfirm acl")
} else {
installCmd = exec.CommandContext(ctx, "sudo", "pacman", "-S", "--needed", "--noconfirm", "acl")
}
case distros.FamilyFedora:
if sudoPassword != "" {
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "dnf install -y acl")
} else {
installCmd = exec.CommandContext(ctx, "sudo", "dnf", "install", "-y", "acl")
}
case distros.FamilySUSE:
if sudoPassword != "" {
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "zypper install -y acl")
} else {
installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "acl")
}
case distros.FamilyUbuntu, distros.FamilyDebian:
if sudoPassword != "" {
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "apt-get install -y acl")
} else {
installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "acl")
}
case distros.FamilyGentoo:
if sudoPassword != "" {
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "emerge --ask n sys-fs/acl")
} else {
installCmd = exec.CommandContext(ctx, "sudo", "emerge", "--ask", "n", "sys-fs/acl")
}
case distros.FamilyNix:
return fmt.Errorf("on NixOS, please add pkgs.acl to your configuration.nix")
default:
return fmt.Errorf("unsupported distribution family for automatic acl installation: %s", config.Family)
}
installCmd.Stdout = os.Stdout
installCmd.Stderr = os.Stderr
if err := installCmd.Run(); err != nil {
return fmt.Errorf("failed to install acl: %w", err)
}
logFunc("✓ acl package installed")
return nil
}
// SetupParentDirectoryACLs sets ACLs on parent directories to allow traversal
func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error {
if err := EnsureACLInstalled(logFunc, sudoPassword); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: could not install acl package: %v", err))
logFunc(" ACL permissions will be skipped; theme sync may not work correctly.")
return nil
}
if !utils.CommandExists("setfacl") {
logFunc("⚠ Warning: setfacl command not found. ACL support may not be available on this filesystem.")
logFunc(" If theme sync doesn't work, you may need to install acl package:")
logFunc(" - Fedora/RHEL: sudo dnf install acl")
logFunc(" - Debian/Ubuntu: sudo apt-get install acl")
logFunc(" - Arch: sudo pacman -S acl")
// setfacl still not found after install attempt (e.g. unsupported filesystem)
logFunc("⚠ Warning: setfacl still not available after install attempt; skipping ACL setup.")
return nil
}
@@ -394,7 +464,6 @@ func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error {
}
}
// Set ACL to allow greeter user read+execute permission (for session discovery)
if err := runSudoCmd(sudoPassword, "setfacl", "-m", fmt.Sprintf("u:%s:rx", owner), dir.path); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: Failed to set ACL on %s: %v", dir.desc, err))
logFunc(fmt.Sprintf(" You may need to run manually: setfacl -m u:%s:x %s", owner, dir.path))
@@ -429,7 +498,6 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error {
if err == nil && strings.Contains(string(groupsOutput), group) {
logFunc(fmt.Sprintf("✓ %s is already in %s group", currentUser, group))
} else {
// Add current user to greeter group for file access permissions
if err := runSudoCmd(sudoPassword, "usermod", "-aG", group, currentUser); err != nil {
return fmt.Errorf("failed to add %s to %s group: %w", currentUser, group, err)
}
@@ -469,7 +537,6 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error {
logFunc(fmt.Sprintf("✓ Set group permissions for %s", dir.desc))
}
// Set up ACLs on parent directories to allow greeter user traversal
if err := SetupParentDirectoryACLs(logFunc, sudoPassword); err != nil {
return fmt.Errorf("failed to setup parent directory ACLs: %w", err)
}
@@ -917,14 +984,12 @@ user = "%s"
}
}
// Determine wrapper command path
// If dmsPath is empty (packaged greeter), omit -p; wrapper finds /usr/share/quickshell/dms-greeter
wrapperCmd := "dms-greeter"
if !utils.CommandExists("dms-greeter") {
wrapperCmd = "/usr/local/bin/dms-greeter"
}
// Build command based on compositor and dms path
// When dmsPath is empty (packaged greeter), omit -p; wrapper finds /usr/share/quickshell/dms-greeter
compositorLower := strings.ToLower(compositor)
var command string
if dmsPath == "" {
@@ -1068,3 +1133,140 @@ func runSudoCmd(sudoPassword string, command string, args ...string) error {
cmd.Stderr = os.Stderr
return cmd.Run()
}
func checkSystemdEnabled(service string) (string, error) {
cmd := exec.Command("systemctl", "is-enabled", service)
output, _ := cmd.Output()
return strings.TrimSpace(string(output)), nil
}
func DisableConflictingDisplayManagers(sudoPassword string, logFunc func(string)) error {
conflictingDMs := []string{"gdm", "gdm3", "lightdm", "sddm", "lxdm", "xdm", "cosmic-greeter"}
for _, dm := range conflictingDMs {
state, err := checkSystemdEnabled(dm)
if err != nil || state == "" || state == "not-found" {
continue
}
switch state {
case "enabled", "enabled-runtime", "static", "indirect", "alias":
logFunc(fmt.Sprintf("Disabling conflicting display manager: %s", dm))
if err := runSudoCmd(sudoPassword, "systemctl", "disable", "--now", dm); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: Failed to disable %s: %v", dm, err))
} else {
logFunc(fmt.Sprintf("✓ Disabled %s", dm))
}
}
}
return nil
}
// EnableGreetd unmasks and enables greetd, forcing it over any other DM.
func EnableGreetd(sudoPassword string, logFunc func(string)) error {
state, err := checkSystemdEnabled("greetd")
if err != nil {
return fmt.Errorf("failed to check greetd state: %w", err)
}
if state == "not-found" {
return fmt.Errorf("greetd service not found; ensure greetd is installed")
}
if state == "masked" || state == "masked-runtime" {
logFunc(" Unmasking greetd...")
if err := runSudoCmd(sudoPassword, "systemctl", "unmask", "greetd"); err != nil {
return fmt.Errorf("failed to unmask greetd: %w", err)
}
logFunc(" ✓ Unmasked greetd")
}
logFunc(" Enabling greetd service (--force)...")
if err := runSudoCmd(sudoPassword, "systemctl", "enable", "--force", "greetd"); err != nil {
return fmt.Errorf("failed to enable greetd: %w", err)
}
logFunc("✓ greetd enabled")
return nil
}
func EnsureGraphicalTarget(sudoPassword string, logFunc func(string)) error {
cmd := exec.Command("systemctl", "get-default")
output, err := cmd.Output()
if err != nil {
logFunc(fmt.Sprintf("⚠ Warning: could not get default systemd target: %v", err))
return nil
}
current := strings.TrimSpace(string(output))
if current == "graphical.target" {
logFunc("✓ Default target is already graphical.target")
return nil
}
logFunc(fmt.Sprintf(" Setting default target to graphical.target (was: %s)...", current))
if err := runSudoCmd(sudoPassword, "systemctl", "set-default", "graphical.target"); err != nil {
return fmt.Errorf("failed to set graphical target: %w", err)
}
logFunc("✓ Default target set to graphical.target")
return nil
}
// AutoSetupGreeter performs the full non-interactive greeter setup
func AutoSetupGreeter(compositor, sudoPassword string, logFunc func(string)) error {
if IsGreeterPackaged() && HasLegacyLocalGreeterWrapper() {
return fmt.Errorf("legacy manual wrapper detected at /usr/local/bin/dms-greeter; " +
"remove it before using packaged dms-greeter: sudo rm -f /usr/local/bin/dms-greeter")
}
logFunc("Ensuring greetd is installed...")
if err := EnsureGreetdInstalled(logFunc, sudoPassword); err != nil {
return fmt.Errorf("greetd install failed: %w", err)
}
dmsPath := ""
if !IsGreeterPackaged() {
detected, err := DetectDMSPath()
if err != nil {
return fmt.Errorf("DMS installation not found: %w", err)
}
dmsPath = detected
logFunc(fmt.Sprintf("✓ Found DMS at: %s", dmsPath))
} else {
logFunc("✓ Using packaged dms-greeter (/usr/share/quickshell/dms-greeter)")
}
logFunc("Setting up dms-greeter group and permissions...")
if err := SetupDMSGroup(logFunc, sudoPassword); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: group/permissions setup error: %v", err))
}
logFunc("Copying greeter files...")
if err := CopyGreeterFiles(dmsPath, compositor, logFunc, sudoPassword); err != nil {
return fmt.Errorf("failed to copy greeter files: %w", err)
}
logFunc("Configuring greetd...")
greeterPathForConfig := ""
if !IsGreeterPackaged() {
greeterPathForConfig = dmsPath
}
if err := ConfigureGreetd(greeterPathForConfig, compositor, logFunc, sudoPassword); err != nil {
return fmt.Errorf("failed to configure greetd: %w", err)
}
logFunc("Synchronizing DMS configurations...")
if err := SyncDMSConfigs(dmsPath, compositor, logFunc, sudoPassword); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: config sync error: %v", err))
}
logFunc("Checking for conflicting display managers...")
if err := DisableConflictingDisplayManagers(sudoPassword, logFunc); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: %v", err))
}
logFunc("Enabling greetd service...")
if err := EnableGreetd(sudoPassword, logFunc); err != nil {
return fmt.Errorf("failed to enable greetd: %w", err)
}
logFunc("Ensuring graphical.target as default...")
if err := EnsureGraphicalTarget(sudoPassword, logFunc); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: %v", err))
}
logFunc("✓ DMS greeter setup complete")
return nil
}