repo-sync/service_cmd.go
James Coleman 5af1dd8928
Some checks failed
Go package / build (push) Has been cancelled
first commit
2026-07-28 19:22:20 -05:00

125 lines
3.6 KiB
Go

package main
import (
"fmt"
cfg "github.com/grmrgecko/repo-sync/config"
"github.com/kardianos/service"
)
// systemdScript is the unit template used when installing the service. It
// replaces the library default to run as a notify service with automatic
// restart, so systemd only considers the mirror started once the listener
// is bound.
const systemdScript = `[Unit]
Description={{Description}}
ConditionFileIsExecutable={{Path | cmdEscape}}
{{range Dependencies}}{{.}}
{{end}}StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
Type=notify
ExecStart={{Path | cmdEscape}}{{range Arguments}} {{. | cmd}}{{end}}
{{if ChRoot}}RootDirectory={{ChRoot | cmd}}
{{end}}{{if WorkingDirectory}}WorkingDirectory={{WorkingDirectory | cmdEscape}}
{{end}}{{if UserName}}User={{UserName}}
{{end}}{{if ReloadSignal}}ExecReload=/bin/kill -{{ReloadSignal}} "$MAINPID"
{{end}}{{if PIDFile}}PIDFile={{PIDFile | cmd}}
{{end}}{{if OutputFileSupport}}StandardOutput=file:{{LogDirectory}}/{{Name}}.out
StandardError=file:{{LogDirectory}}/{{Name}}.err
{{end}}{{if LimitNOFILE}}LimitNOFILE={{LimitNOFILE}}
{{end}}{{if Restart}}Restart={{Restart}}
{{end}}{{if SuccessExitStatus}}SuccessExitStatus={{SuccessExitStatus}}
{{end}}RestartSec=5
EnvironmentFile=-/etc/sysconfig/{{Name}}
{{range EnvVars}}{{.}}
{{end}}[Install]
WantedBy=multi-user.target
`
// ServiceAction lists the accepted service actions, in the order the Run
// switch handles them.
var ServiceAction = []string{"start", "stop", "status", "restart", "install", "uninstall"}
// ServiceCmd manages the mirror server as a system service.
type ServiceCmd struct {
Action string `arg:"" enum:"${serviceActions}" help:"${serviceActions}" required:""`
}
// action returns the requested service action.
func (s *ServiceCmd) action() string {
return s.Action
}
// Run performs the requested action against the installed service.
func (s *ServiceCmd) Run() (err error) {
svc, err := s.service()
if err != nil {
return err
}
switch s.action() {
case ServiceAction[0]:
err = svc.Start()
case ServiceAction[1]:
err = svc.Stop()
case ServiceAction[2]:
var status service.Status
status, err = svc.Status()
if err == nil {
switch status {
case service.StatusRunning:
fmt.Println("Service is running.")
case service.StatusStopped:
fmt.Println("Service is stopped.")
default:
fmt.Println("Service is in an unknown state.")
}
}
case ServiceAction[3]:
err = svc.Restart()
case ServiceAction[4]:
err = svc.Install()
case ServiceAction[5]:
err = svc.Uninstall()
}
if err != nil {
return err
}
// Status already printed its own result.
if s.action() != ServiceAction[2] {
fmt.Println("Command executed successfully.")
}
return
}
// service builds the service definition shared by the management actions
// and by the server when it is started by the service manager.
func (s *ServiceCmd) service() (service.Service, error) {
svcConfig := &service.Config{
Name: cfg.Name,
DisplayName: cfg.DisplayName,
Description: cfg.Description,
Arguments: []string{"server"},
Dependencies: []string{"After=network.target"},
Option: service.KeyValue{
"SystemdScript": systemdScript,
"ReloadSignal": "HUP",
"Restart": "always",
},
}
return service.New(s, svcConfig)
}
// Start satisfies service.Interface. The server is already running in the
// foreground by the time the supervisor attaches.
func (s *ServiceCmd) Start(svc service.Service) error {
return nil
}
// Stop satisfies service.Interface, signalling the server's shutdown.
func (s *ServiceCmd) Stop(svc service.Service) error {
stopChan <- struct{}{}
return nil
}