aboutsummaryrefslogtreecommitdiffhomepage
path: root/main.go
blob: a15dc0656a2d34b0af9a737da9763ff26a0fd006 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
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

	dbusSession *dbus.Config
	dbusSystem  *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 dbusConfigSession == "builtin" {
			dbusSession = dbus.NewConfig(dbusID, true, mpris)
		} else {
			if f, err := os.Open(dbusConfigSession); err != nil {
				state.Fatal("Error opening D-Bus proxy config file:", err)
			} else {
				if err = json.NewDecoder(f).Decode(&dbusSession); err != nil {
					state.Fatal("Error parsing D-Bus proxy config file:", err)
				}
			}
		}

		// system bus proxy is optional
		if dbusConfigSystem != "nil" {
			if f, err := os.Open(dbusConfigSystem); err != nil {
				state.Fatal("Error opening D-Bus proxy config file:", err)
			} else {
				if err = json.NewDecoder(f).Decode(&dbusSystem); 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(dbusSession, dbusSystem, dbusVerbose)
	}

	if mustPulse {
		a.SharePulse()
	}

	a.Run()
}