mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 10:02:03 -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"
|
[ "$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
|
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
|
// List of fonts for each OS
|
||||||
var FontsByOS = map[string][]string{
|
var FontsByOS = map[string][]string{
|
||||||
"windows": {
|
"windows": {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -27,10 +25,15 @@ func main() {
|
||||||
|
|
||||||
// Run the Camoufox executable
|
// Run the Camoufox executable
|
||||||
execName := getExecutableName()
|
execName := getExecutableName()
|
||||||
|
if err := setExecutablePermissions(execName); err != nil {
|
||||||
|
fmt.Printf("Error setting executable permissions: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
runCamoufox(execName, remainingArgs)
|
runCamoufox(execName, remainingArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseArgs(args []string) (string, []string) {
|
func parseArgs(args []string) (string, []string) {
|
||||||
|
// Parse the arguments
|
||||||
var configPath string
|
var configPath string
|
||||||
var remainingArgs []string
|
var remainingArgs []string
|
||||||
|
|
||||||
|
|
@ -56,6 +59,7 @@ func parseArgs(args []string) (string, []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func readAndParseConfig(configInput string) map[string]interface{} {
|
func readAndParseConfig(configInput string) map[string]interface{} {
|
||||||
|
// Unmarshal the config input into a map
|
||||||
var configData []byte
|
var configData []byte
|
||||||
|
|
||||||
// Check if the input is a file path or inline JSON
|
// 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 {
|
func determineUserAgentOS(configMap map[string]interface{}) string {
|
||||||
|
// Determine the OS from the user agent string if provided
|
||||||
defaultOS := normalizeOS(runtime.GOOS)
|
defaultOS := normalizeOS(runtime.GOOS)
|
||||||
if ua, ok := configMap["navigator.userAgent"].(string); ok {
|
if ua, ok := configMap["navigator.userAgent"].(string); ok {
|
||||||
parsedUA := useragent.Parse(ua)
|
parsedUA := useragent.Parse(ua)
|
||||||
|
|
@ -91,6 +96,7 @@ func determineUserAgentOS(configMap map[string]interface{}) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeOS(osName string) string {
|
func normalizeOS(osName string) string {
|
||||||
|
// Get the OS name as {macos, windows, linux}
|
||||||
osName = strings.ToLower(osName)
|
osName = strings.ToLower(osName)
|
||||||
switch {
|
switch {
|
||||||
case osName == "darwin" || strings.Contains(osName, "mac"):
|
case osName == "darwin" || strings.Contains(osName, "mac"):
|
||||||
|
|
@ -103,6 +109,7 @@ func normalizeOS(osName string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
||||||
|
// Add fonts associated with the OS to the config map
|
||||||
fonts, ok := configMap["fonts"].([]interface{})
|
fonts, ok := configMap["fonts"].([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
fonts = []interface{}{}
|
fonts = []interface{}{}
|
||||||
|
|
@ -122,6 +129,7 @@ func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setEnvironmentVariables(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)
|
updatedConfigData, err := json.Marshal(configMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error updating config: %v\n", err)
|
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)
|
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