|
9 | 9 | "encoding/hex"
|
10 | 10 | "fmt"
|
11 | 11 | "os"
|
| 12 | + "os/user" |
| 13 | + "path/filepath" |
12 | 14 | "strings"
|
13 | 15 |
|
14 | 16 | uuid "github.com/satori/go.uuid"
|
@@ -107,3 +109,169 @@ func Ttyname(fd uintptr) (string, error) {
|
107 | 109 |
|
108 | 110 | return ttyPath, nil
|
109 | 111 | }
|
| 112 | + |
| 113 | +// getCurrentUserHomeDir gets the home directory for the current user on Linux |
| 114 | +func getCurrentUserHomeDir() string { |
| 115 | + // Use os/user package to get current user's home |
| 116 | + if currentUser, err := user.Current(); err == nil { |
| 117 | + return currentUser.HomeDir |
| 118 | + } |
| 119 | + |
| 120 | + // Fallback to HOME environment variable |
| 121 | + if homeDir := os.Getenv("HOME"); homeDir != "" { |
| 122 | + return homeDir |
| 123 | + } |
| 124 | + |
| 125 | + // Linux-specific fallback using /etc/passwd |
| 126 | + if username := getCurrentUser(); username != "unknown" { |
| 127 | + // Try to read from /etc/passwd |
| 128 | + if file, err := os.Open("/etc/passwd"); err == nil { |
| 129 | + defer file.Close() |
| 130 | + scanner := bufio.NewScanner(file) |
| 131 | + for scanner.Scan() { |
| 132 | + line := scanner.Text() |
| 133 | + fields := strings.Split(line, ":") |
| 134 | + if len(fields) >= 6 && fields[0] == username { |
| 135 | + return fields[5] // Home directory is the 6th field |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + return "/home/" + getCurrentUser() |
| 142 | +} |
| 143 | + |
| 144 | +// Install installs and configures the ghost service for automatic startup on Linux |
| 145 | +func Install() error { |
| 146 | + execPath, err := os.Executable() |
| 147 | + if err != nil { |
| 148 | + return fmt.Errorf("failed to get executable path: %v", err) |
| 149 | + } |
| 150 | + |
| 151 | + targetPath := "/opt/bin/ghost" |
| 152 | + |
| 153 | + err = runWithSudo("mkdir", "-p", "/opt/bin") |
| 154 | + if err != nil { |
| 155 | + return fmt.Errorf("failed to create /opt/bin directory: %v", err) |
| 156 | + } |
| 157 | + |
| 158 | + err = cpWithSudo(execPath, targetPath) |
| 159 | + if err != nil { |
| 160 | + return fmt.Errorf("failed to copy ghost binary: %v", err) |
| 161 | + } |
| 162 | + err = chmodWithSudo("755", targetPath) |
| 163 | + if err != nil { |
| 164 | + return fmt.Errorf("failed to set executable permissions: %v", err) |
| 165 | + } |
| 166 | + |
| 167 | + homeDir := getCurrentUserHomeDir() |
| 168 | + currentUser := getCurrentUser() |
| 169 | + |
| 170 | + cmdParts := getServiceCommand() |
| 171 | + cmdArgs := strings.Join(cmdParts, " ") |
| 172 | + |
| 173 | + serviceContent := fmt.Sprintf(`[Unit] |
| 174 | +Description=Overlord Ghost Client |
| 175 | +After=network-online.target local-fs.target |
| 176 | +Wants=network-online.target |
| 177 | +RequiresMountsFor=%s |
| 178 | +
|
| 179 | +[Service] |
| 180 | +Type=simple |
| 181 | +User=%s |
| 182 | +Environment=SHELL=/bin/bash |
| 183 | +Environment=HOME=%s |
| 184 | +Environment=TERM=xterm-256color |
| 185 | +ExecStart=%s %s |
| 186 | +Restart=always |
| 187 | +RestartSec=5 |
| 188 | +
|
| 189 | +[Install] |
| 190 | +WantedBy=multi-user.target |
| 191 | +`, homeDir, currentUser, homeDir, targetPath, cmdArgs) |
| 192 | + |
| 193 | + serviceFilePath, err := getSystemdServicePath() |
| 194 | + if err != nil { |
| 195 | + return fmt.Errorf("failed to determine systemd directory: %v", err) |
| 196 | + } |
| 197 | + |
| 198 | + tempServiceFile, err := os.CreateTemp("", "ghost-*.service") |
| 199 | + if err != nil { |
| 200 | + return fmt.Errorf("failed to create temp service file: %v", err) |
| 201 | + } |
| 202 | + defer os.Remove(tempServiceFile.Name()) |
| 203 | + |
| 204 | + _, err = tempServiceFile.WriteString(serviceContent) |
| 205 | + if err != nil { |
| 206 | + return fmt.Errorf("failed to write temp service file: %v", err) |
| 207 | + } |
| 208 | + tempServiceFile.Close() |
| 209 | + |
| 210 | + err = cpWithSudo(tempServiceFile.Name(), serviceFilePath) |
| 211 | + if err != nil { |
| 212 | + return fmt.Errorf("failed to install systemd service file: %v", err) |
| 213 | + } |
| 214 | + |
| 215 | + fmt.Printf("Systemd service installed at %s\n", serviceFilePath) |
| 216 | + |
| 217 | + err = runWithSudo("systemctl", "daemon-reload") |
| 218 | + if err != nil { |
| 219 | + return fmt.Errorf("failed to reload systemd daemon: %v", err) |
| 220 | + } |
| 221 | + fmt.Printf("Systemd daemon reloaded\n") |
| 222 | + |
| 223 | + err = runWithSudo("systemctl", "enable", "ghost") |
| 224 | + if err != nil { |
| 225 | + return fmt.Errorf("failed to enable ghost service: %v", err) |
| 226 | + } |
| 227 | + fmt.Printf("Ghost service enabled for automatic startup\n") |
| 228 | + |
| 229 | + if !isGhostRunning() { |
| 230 | + fmt.Printf("Starting ghost service...\n") |
| 231 | + err = runWithSudo("systemctl", "start", "ghost") |
| 232 | + if err != nil { |
| 233 | + fmt.Printf("Warning: failed to start ghost service: %v\n", err) |
| 234 | + fmt.Printf("You can start it manually with: sudo systemctl start ghost\n") |
| 235 | + } else { |
| 236 | + fmt.Printf("Ghost service started successfully\n") |
| 237 | + } |
| 238 | + } else { |
| 239 | + fmt.Printf("Ghost service is already running\n") |
| 240 | + } |
| 241 | + |
| 242 | + fmt.Printf("Ghost service installation completed successfully!\n") |
| 243 | + fmt.Printf("The ghost service will start automatically on system boot.\n") |
| 244 | + fmt.Printf("To check service status, run: sudo systemctl status ghost\n") |
| 245 | + |
| 246 | + return nil |
| 247 | +} |
| 248 | + |
| 249 | +// getSystemdServicePath determines the best systemd directory for service installation |
| 250 | +func getSystemdServicePath() (string, error) { |
| 251 | + // Systemd service directories in order of preference: |
| 252 | + // 1. /etc/systemd/system - Local configuration (highest priority, for admin-installed services) |
| 253 | + // 2. /usr/lib/systemd/system - Package manager installed services (RHEL/CentOS/Fedora) |
| 254 | + // 3. /lib/systemd/system - Package manager installed services (Debian/Ubuntu) |
| 255 | + |
| 256 | + serviceDirs := []string{ |
| 257 | + "/usr/lib/systemd/system", |
| 258 | + "/etc/systemd/system", |
| 259 | + "/lib/systemd/system", |
| 260 | + } |
| 261 | + |
| 262 | + // Try to find an existing systemd directory |
| 263 | + for _, dir := range serviceDirs { |
| 264 | + if _, err := os.Stat(dir); err == nil { |
| 265 | + return filepath.Join(dir, "ghost.service"), nil |
| 266 | + } |
| 267 | + } |
| 268 | + |
| 269 | + // If no existing directory found, try to create /etc/systemd/system (preferred for admin installs) |
| 270 | + preferredDir := "/etc/systemd/system" |
| 271 | + err := runWithSudo("mkdir", "-p", preferredDir) |
| 272 | + if err != nil { |
| 273 | + return "", fmt.Errorf("failed to create systemd directory %s: %v", preferredDir, err) |
| 274 | + } |
| 275 | + |
| 276 | + return filepath.Join(preferredDir, "ghost.service"), nil |
| 277 | +} |
0 commit comments