mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 06:32:05 -08:00
Launcher: Better process handling
- Automatically run chmod on linux & macos - Manage camoufox with process group on unix - Closing camoufox kills launcher, & vice versa - Exclude useless warnings from output - Make return code of launcher match camoufox process
This commit is contained in:
parent
22cbdb2126
commit
8e5144abe0
6 changed files with 210 additions and 67 deletions
|
|
@ -26,6 +26,9 @@ esac
|
|||
|
||||
[ "$OS" = "windows" ] && OUTPUT="launch.exe" || OUTPUT="launch"
|
||||
|
||||
GOOS=$GOOS GOARCH=$GOARCH go build -o dist/$OUTPUT
|
||||
rm -rf ./dist launch launch.exe
|
||||
|
||||
echo "Built: $OUTPUT"
|
||||
echo Building launcher...
|
||||
GOOS=$GOOS GOARCH=$GOARCH go build -o dist/$OUTPUT || exit 1
|
||||
|
||||
echo "Complete: launcher/dist/$OUTPUT"
|
||||
|
|
@ -1,5 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Exclude lines from output
|
||||
var ExclusionRules = []string{
|
||||
// Ignore search related warnings
|
||||
"^console\\.error:\\ Search",
|
||||
"SearchService",
|
||||
"SearchEngineSelector",
|
||||
// Ignore glxtest errors
|
||||
"\\[GFX1\\-\\]:",
|
||||
// Ignore meaningless lines
|
||||
"^console\\.error:\\ \\(\\{\\}\\)$",
|
||||
"^console\\.error:\\ \"Could\\ not\\ record\\ event:\\ \"\\ \\(\\{\\}\\)$",
|
||||
"^\\s*?$",
|
||||
// Ignore missing urlbar provider errors
|
||||
"^JavaScript\\ error:\\ resource:///modules/UrlbarProvider",
|
||||
}
|
||||
|
||||
// Convert ExclusionRules into a regex command
|
||||
var ExclusionRegex = regexp.MustCompile(".*(" + strings.Join(ExclusionRules, "|") + ").*")
|
||||
|
||||
// List of fonts for each OS
|
||||
var FontsByOS = map[string][]string{
|
||||
"windows": {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
|
@ -27,10 +25,15 @@ func main() {
|
|||
|
||||
// Run the Camoufox executable
|
||||
execName := getExecutableName()
|
||||
if err := setExecutablePermissions(execName); err != nil {
|
||||
fmt.Printf("Error setting executable permissions: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
runCamoufox(execName, remainingArgs)
|
||||
}
|
||||
|
||||
func parseArgs(args []string) (string, []string) {
|
||||
// Parse the arguments
|
||||
var configPath string
|
||||
var remainingArgs []string
|
||||
|
||||
|
|
@ -56,6 +59,7 @@ func parseArgs(args []string) (string, []string) {
|
|||
}
|
||||
|
||||
func readAndParseConfig(configInput string) map[string]interface{} {
|
||||
// Unmarshal the config input into a map
|
||||
var configData []byte
|
||||
|
||||
// Check if the input is a file path or inline JSON
|
||||
|
|
@ -80,6 +84,7 @@ func readAndParseConfig(configInput string) map[string]interface{} {
|
|||
}
|
||||
|
||||
func determineUserAgentOS(configMap map[string]interface{}) string {
|
||||
// Determine the OS from the user agent string if provided
|
||||
defaultOS := normalizeOS(runtime.GOOS)
|
||||
if ua, ok := configMap["navigator.userAgent"].(string); ok {
|
||||
parsedUA := useragent.Parse(ua)
|
||||
|
|
@ -91,6 +96,7 @@ func determineUserAgentOS(configMap map[string]interface{}) string {
|
|||
}
|
||||
|
||||
func normalizeOS(osName string) string {
|
||||
// Get the OS name as {macos, windows, linux}
|
||||
osName = strings.ToLower(osName)
|
||||
switch {
|
||||
case osName == "darwin" || strings.Contains(osName, "mac"):
|
||||
|
|
@ -103,6 +109,7 @@ func normalizeOS(osName string) string {
|
|||
}
|
||||
|
||||
func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
||||
// Add fonts associated with the OS to the config map
|
||||
fonts, ok := configMap["fonts"].([]interface{})
|
||||
if !ok {
|
||||
fonts = []interface{}{}
|
||||
|
|
@ -122,6 +129,7 @@ func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
|||
}
|
||||
|
||||
func setEnvironmentVariables(configMap map[string]interface{}, userAgentOS string) {
|
||||
// Update the config map with the fonts and environment variables
|
||||
updatedConfigData, err := json.Marshal(configMap)
|
||||
if err != nil {
|
||||
fmt.Printf("Error updating config: %v\n", err)
|
||||
|
|
@ -162,66 +170,3 @@ func setEnvironmentVariables(configMap map[string]interface{}, userAgentOS strin
|
|||
os.Setenv("FONTCONFIG_PATH", fontconfigPath)
|
||||
}
|
||||
}
|
||||
|
||||
func getExecutableName() string {
|
||||
switch normalizeOS(runtime.GOOS) {
|
||||
case "linux":
|
||||
return "./camoufox-bin"
|
||||
case "macos":
|
||||
return "./Camoufox.app"
|
||||
case "windows":
|
||||
return "./camoufox.exe"
|
||||
default:
|
||||
// This should never be reached due to the check in normalizeOS
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func runCamoufox(execName string, args []string) {
|
||||
cmd := exec.Command(execName, args...)
|
||||
|
||||
// Create pipes for stdout and stderr
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating stdout pipe: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating stderr pipe: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Start the command
|
||||
if err := cmd.Start(); err != nil {
|
||||
fmt.Printf("Error starting %s: %v\n", execName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create a channel to signal when we're done copying output
|
||||
done := make(chan bool)
|
||||
|
||||
// Copy stdout and stderr to the console
|
||||
go func() {
|
||||
io.Copy(os.Stdout, stdout)
|
||||
done <- true
|
||||
}()
|
||||
go func() {
|
||||
io.Copy(os.Stderr, stderr)
|
||||
done <- true
|
||||
}()
|
||||
|
||||
// Wait for both stdout and stderr to finish
|
||||
<-done
|
||||
<-done
|
||||
|
||||
// Wait for the command to finish
|
||||
if err := cmd.Wait(); err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
os.Exit(exitErr.ExitCode())
|
||||
} else {
|
||||
fmt.Printf("Error running %s: %v\n", execName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
launcher/procgroup_unix.go
Normal file
17
launcher/procgroup_unix.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func setProcessGroupID(cmd *exec.Cmd) {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
}
|
||||
|
||||
func killProcessGroup(cmd *exec.Cmd) {
|
||||
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
16
launcher/procgroup_win.go
Normal file
16
launcher/procgroup_win.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func setProcessGroupID(cmd *exec.Cmd) {
|
||||
// Windows doesn't support process groups in the same way
|
||||
}
|
||||
|
||||
func killProcessGroup(cmd *exec.Cmd) {
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
138
launcher/runner.go
Normal file
138
launcher/runner.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func getExecutableName() string {
|
||||
// Get the executable name based on the OS
|
||||
switch normalizeOS(runtime.GOOS) {
|
||||
case "linux":
|
||||
return "./camoufox-bin"
|
||||
case "macos":
|
||||
return "./Camoufox.app"
|
||||
case "windows":
|
||||
return "./camoufox.exe"
|
||||
default:
|
||||
// This should never be reached due to the check in normalizeOS
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func setExecutablePermissions(execPath string) error {
|
||||
// Set executable permissions if needed
|
||||
switch normalizeOS(runtime.GOOS) {
|
||||
case "macos":
|
||||
return filepath.Walk(execPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return maybeSetPermission(path, 0755)
|
||||
})
|
||||
case "linux":
|
||||
return maybeSetPermission(execPath, 0755)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func maybeSetPermission(path string, mode os.FileMode) error {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentMode := info.Mode().Perm()
|
||||
if currentMode != mode {
|
||||
return os.Chmod(path, mode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterOutput(r io.Reader, w io.Writer) {
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if !ExclusionRegex.MatchString(line) {
|
||||
fmt.Fprintln(w, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runCamoufox(execName string, args []string) {
|
||||
cmd := exec.Command(execName, args...)
|
||||
|
||||
setProcessGroupID(cmd)
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating stdout pipe: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating stderr pipe: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Set up signal handling
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
fmt.Printf("Error starting %s: %v\n", execName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Channel to signal when the subprocess has finished
|
||||
subprocessDone := make(chan struct{})
|
||||
|
||||
// Start a goroutine to handle signals
|
||||
go func() {
|
||||
select {
|
||||
case <-sigChan:
|
||||
killProcessGroup(cmd)
|
||||
case <-subprocessDone:
|
||||
// Subprocess has finished, exit the Go process
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
go func() {
|
||||
filterOutput(stdout, os.Stdout)
|
||||
done <- true
|
||||
}()
|
||||
go func() {
|
||||
filterOutput(stderr, os.Stderr)
|
||||
done <- true
|
||||
}()
|
||||
|
||||
<-done
|
||||
<-done
|
||||
|
||||
// Wait for the command to finish
|
||||
if err := cmd.Wait(); err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
// If the subprocess exited with an error, use its exit code
|
||||
os.Exit(exitErr.ExitCode())
|
||||
} else {
|
||||
fmt.Printf("Error running %s: %v\n", execName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Signal that the subprocess has finished
|
||||
close(subprocessDone)
|
||||
|
||||
// Wait here to allow the signal handling goroutine to exit the process
|
||||
select {}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue