-
-
Notifications
You must be signed in to change notification settings - Fork 47
/
Systemd_linux.go
160 lines (133 loc) · 4.37 KB
/
Systemd_linux.go
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//go:build linux
package systemd
import (
"fmt"
"os"
"os/exec"
"os/user"
"path/filepath"
"syscall"
)
// InstallService installs Gokapi as a systemd service
func InstallService() {
checkRunAsRoot()
checkSystemdOs()
fmt.Println("Installing Gokapi as a service...")
// Check if the service file already exists
if _, err := os.Stat("/usr/lib/systemd/system/gokapi.service"); err == nil {
fmt.Println("Service file already exists. Reinstalling it")
}
// Find the path to the current executable and it's directory
executablePath, err := os.Executable()
if err != nil {
fmt.Println("Error getting executable path: ", err)
os.Exit(6)
}
executableDir := filepath.Dir(executablePath)
username := getUserInvokingSudo(executablePath)
fmt.Println("Running service as user", username)
// Create the service file
serviceFileContents := createSystemdFileContent(executablePath, executableDir, username)
err = os.WriteFile("/usr/lib/systemd/system/gokapi.service", serviceFileContents, 0644)
if err != nil {
fmt.Println("Error writing service data to file: ", err)
os.Exit(3)
}
systemctlCmd("daemon-reload")
systemctlCmd("enable", "gokapi.service")
systemctlCmd("start", "gokapi.service")
fmt.Println("Service installed and started successfully.")
fmt.Println("The Gokapi executable found at " + executablePath + " will now run on startup in the background.")
fmt.Println("Please do not remove the executable file from that location or the service will not start.")
// Exit the program
os.Exit(0)
}
// UninstallService uninstalls Gokapi as a systemd service
func UninstallService() {
checkRunAsRoot()
checkSystemdOs()
fmt.Println("Uninstalling Gokapi systemd service...")
// Check if the service file exists
if _, err := os.Stat("/usr/lib/systemd/system/gokapi.service"); os.IsNotExist(err) {
fmt.Println("Service does not exist in systemd. Nothing to uninstall.")
os.Exit(3)
}
systemctlCmd("stop", "gokapi.service")
systemctlCmd("disable", "gokapi.service")
// Remove the service file
fmt.Println("Removing the service file...")
err := os.Remove("/usr/lib/systemd/system/gokapi.service")
if err != nil {
fmt.Println("Error removing service file: ", err)
os.Exit(4)
}
systemctlCmd("daemon-reload")
fmt.Println("Service uninstalled successfully.")
// Exit the program
os.Exit(0)
}
// checkRunAsRoot displays an error message and exits the program if not run as root
func checkRunAsRoot() {
if os.Geteuid() != 0 {
fmt.Println("This feature requires root privileges.")
os.Exit(1)
}
}
// checkSystemdOs displays an error message and exits the program if the OS is not systemd based
func checkSystemdOs() {
if _, err := os.Stat("/usr/lib/systemd/system"); os.IsNotExist(err) {
fmt.Println("This feature is only supported on systems using systemd.")
os.Exit(2)
}
}
// systemctlCmd runs the command systemctl with the provided arguments. It displays an error message and exits the program
// if an error is encountered
func systemctlCmd(arg ...string) {
err := exec.Command("systemctl", arg...).Run()
if err != nil {
fmt.Println("Error executing systemctl "+arg[0]+": ", err)
os.Exit(4)
}
}
func getUserInvokingSudo(executablePath string) string {
username := os.Getenv("SUDO_USER")
if username == "root" || username == "" {
fmt.Println("WARNING! Could not determine user invoking sudo.")
usernameFromExecutable, err := getUsernameOfFileOwner(executablePath)
if err != nil {
fmt.Println("Could not determine username from file owner:", err)
os.Exit(6)
}
username = usernameFromExecutable
}
if username == "root" {
fmt.Println("Could not determine username other than root. Not running service as root.")
os.Exit(6)
}
return username
}
func getUsernameOfFileOwner(filename string) (string, error) {
fileInfo, err := os.Stat(filename)
if err != nil {
return "", err
}
fileUid := fileInfo.Sys().(*syscall.Stat_t).Uid
fileUser, err := user.LookupId(fmt.Sprintf("%d", fileUid))
if err != nil {
return "", err
}
return fileUser.Username, nil
}
// createSystemdFileContent returns a byte array with the content of the systemd file to be written
func createSystemdFileContent(executablePath, executableDir, username string) []byte {
return []byte(`[Unit]
Description=Gokapi
After=network.target
[Service]
ExecStart=` + executablePath + `
WorkingDirectory=` + executableDir + `
User=` + username + `
Restart=always
[Install]
WantedBy=multi-user.target`)
}