//go:build linux package main import ( "os" "os/exec" "path/filepath" "strconv" "strings" "syscall" "hamlog/internal/applog" wruntime "github.com/wailsapp/wails/v2/pkg/runtime" ) // hideConsole has nothing to hide on Linux: a child started from a GUI process // inherits no console, so no window can flash. func hideConsole(cmd *exec.Cmd) {} // runningProcessNames returns the set of lowercase executable names currently // running, read straight from /proc rather than by shelling out to `ps` — the // output format of `ps` varies between distributions and busybox, /proc does // not. // // Two names are recorded per process: the kernel's comm (truncated to 15 // characters, which is why it cannot be the only one — "gridtracker-bin" and // "wsjtx-improved" both hit the limit) and the basename of the real executable // behind /proc//exe. Either may be what the operator configured. func runningProcessNames() map[string]bool { out := map[string]bool{} entries, err := os.ReadDir("/proc") if err != nil { applog.Printf("autostart: cannot read /proc: %v", err) return out } for _, e := range entries { if !e.IsDir() { continue } pid := e.Name() if _, err := strconv.Atoi(pid); err != nil { continue // not a process directory } if comm, err := os.ReadFile("/proc/" + pid + "/comm"); err == nil { if n := strings.ToLower(strings.TrimSpace(string(comm))); n != "" { out[n] = true } } // The exe symlink is unreadable for processes owned by another user; // that is expected and not worth a log line. if exe, err := os.Readlink("/proc/" + pid + "/exe"); err == nil { if n := strings.ToLower(filepath.Base(strings.TrimSuffix(exe, " (deleted)"))); n != "" { out[n] = true } } } return out } // closeProcess asks the process to close. SIGTERM is the Unix equivalent of the // polite WM_CLOSE used on Windows: WSJT-X and friends get to save their state // instead of being shot with SIGKILL. func closeProcess(pid int) ([]byte, error) { p, err := os.FindProcess(pid) if err != nil { return nil, err } return nil, p.Signal(syscall.SIGTERM) } // executableFilters is what the "choose a program" dialog offers. Nothing to // filter on here: a Linux program is a file with the executable bit, and its // name carries no extension — wsjtx, gridtracker, flrig. An *.exe filter would // show the operator an empty folder. func executableFilters() []wruntime.FileFilter { return []wruntime.FileFilter{{DisplayName: "All files", Pattern: "*"}} }