aboutsummaryrefslogtreecommitdiffhomepage
path: root/main.go
blob: 4aca72f344605450e36f527ff4e5c47dce72b74f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/fs"
	"os"
	"strconv"
	"syscall"

	"git.ophivana.moe/cat/fortify/dbus"
	"git.ophivana.moe/cat/fortify/internal/acl"
	"git.ophivana.moe/cat/fortify/internal/app"
	"git.ophivana.moe/cat/fortify/internal/state"
	"git.ophivana.moe/cat/fortify/internal/system"
)

var (
	Version = "impure"

	a *app.App
	c *dbus.Config
)

func tryVersion() {
	if printVersion {
		fmt.Println(Version)
		os.Exit(0)
	}
}

func main() {
	flag.Parse()

	// launcher payload early exit
	app.Early(printVersion)

	// version/license command early exit
	tryVersion()
	tryLicense()

	system.Retrieve(flagVerbose)
	a = app.New(userName, flag.Args())
	state.Set(*a.User, a.Command(), a.UID())

	// parse D-Bus config file if applicable
	if mustDBus {
		if dbusConfig == "builtin" {
			c = dbus.NewConfig(dbusID, true, mpris)
		} else {
			if f, err := os.Open(dbusConfig); err != nil {
				state.Fatal("Error opening D-Bus proxy config file:", err)
			} else {
				if err = json.NewDecoder(f).Decode(&c); err != nil {
					state.Fatal("Error parsing D-Bus proxy config file:", err)
				}
			}
		}
	}

	// ensure RunDir (e.g. `/run/user/%d/fortify`)
	if err := os.Mkdir(system.V.RunDir, 0700); err != nil && !errors.Is(err, fs.ErrExist) {
		state.Fatal("Error creating runtime directory:", err)
	}

	// state query command early exit
	state.Early()

	// ensure Share (e.g. `/tmp/fortify.%d`)
	// acl is unnecessary as this directory is world executable
	if err := os.Mkdir(system.V.Share, 0701); err != nil && !errors.Is(err, fs.ErrExist) {
		state.Fatal("Error creating shared directory:", err)
	}

	// warn about target user home directory ownership
	if stat, err := os.Stat(a.HomeDir); err != nil {
		if system.V.Verbose {
			switch {
			case errors.Is(err, fs.ErrPermission):
				fmt.Printf("User %s home directory %s is not accessible", a.Username, a.HomeDir)
			case errors.Is(err, fs.ErrNotExist):
				fmt.Printf("User %s home directory %s does not exist", a.Username, a.HomeDir)
			default:
				fmt.Printf("Error stat user %s home directory %s: %s", a.Username, a.HomeDir, err)
			}
		}
		return
	} else {
		// FreeBSD: not cross-platform
		if u := strconv.Itoa(int(stat.Sys().(*syscall.Stat_t).Uid)); u != a.Uid {
			fmt.Printf("User %s home directory %s has incorrect ownership (expected UID %s, found %s)", a.Username, a.HomeDir, a.Uid, u)
		}
	}

	// ensure runtime directory ACL (e.g. `/run/user/%d`)
	if s, err := os.Stat(system.V.Runtime); err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			state.Fatal("Runtime directory does not exist")
		}
		state.Fatal("Error accessing runtime directory:", err)
	} else if !s.IsDir() {
		state.Fatal(fmt.Sprintf("Path '%s' is not a directory", system.V.Runtime))
	} else {
		if err = acl.UpdatePerm(system.V.Runtime, a.UID(), acl.Execute); err != nil {
			state.Fatal("Error preparing runtime dir:", err)
		} else {
			state.RegisterRevertPath(system.V.Runtime)
		}
		if system.V.Verbose {
			fmt.Printf("Runtime data dir '%s' configured\n", system.V.Runtime)
		}
	}

	if mustWayland {
		a.ShareWayland()
	}

	if mustX {
		a.ShareX()
	}

	if mustDBus {
		a.ShareDBus(c)
	}

	if mustPulse {
		a.SharePulse()
	}

	a.Run()
}