2023-05-25 23:18:16 -06:00
|
|
|
// Package service provides a toolkit for creating Hnakra services.
|
2023-05-25 16:08:56 -06:00
|
|
|
package service
|
2023-05-26 18:01:27 -06:00
|
|
|
|
|
|
|
import "os"
|
|
|
|
import "log"
|
|
|
|
import "time"
|
|
|
|
import "hnakra/rotate"
|
2023-05-31 16:08:29 -06:00
|
|
|
import "hnakra/daemon"
|
2023-05-26 18:01:27 -06:00
|
|
|
import "hnakra/routines"
|
|
|
|
|
|
|
|
// Service is capable of managing multiple mounts. It also sets up logging
|
|
|
|
// automatically.
|
2023-05-29 15:03:27 -06:00
|
|
|
type Service struct {
|
|
|
|
ServiceInfo
|
|
|
|
Mounts []Mount
|
2023-05-31 16:08:29 -06:00
|
|
|
|
|
|
|
manager routines.Manager
|
2023-05-29 15:03:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewService provides a shorthand for creating a new service, leaving most
|
|
|
|
// values to their default.
|
|
|
|
func NewService (name, description string, mounts ...Mount) *Service {
|
|
|
|
return &Service {
|
|
|
|
ServiceInfo: ServiceInfo {
|
|
|
|
Name: name,
|
|
|
|
Description: description,
|
|
|
|
},
|
|
|
|
Mounts: mounts,
|
|
|
|
}
|
|
|
|
}
|
2023-05-26 18:01:27 -06:00
|
|
|
|
|
|
|
// Run runs the mounts within the service, and only exits when all of them have
|
|
|
|
// exited. It will automatically start logging to the directory specified by
|
|
|
|
// $HNAKRA_LOG_DIR. If that variable is unset, it will just log to stdout.
|
2023-05-31 16:08:29 -06:00
|
|
|
// Additionally, if $HNAKRA_PIDFILE is set, it will write the process PID to the
|
|
|
|
// file specified by it.
|
2023-05-29 15:03:27 -06:00
|
|
|
func (service *Service) Run () error {
|
2023-05-26 18:01:27 -06:00
|
|
|
// set up logging
|
|
|
|
logDir := os.Getenv("HNAKRA_LOG_DIR")
|
|
|
|
if logDir != "" {
|
|
|
|
logger, err := rotate.New(logDir)
|
|
|
|
if err != nil { log.Fatal("cannot access log dir:", err) }
|
|
|
|
log.SetOutput(logger)
|
|
|
|
}
|
2023-05-29 15:03:27 -06:00
|
|
|
|
|
|
|
log.Println("... starting service", service.Name)
|
2023-05-26 18:01:27 -06:00
|
|
|
|
|
|
|
// set up routine manager
|
2023-05-31 16:08:29 -06:00
|
|
|
service.manager = routines.Manager { RestartDeadline: time.Second * 8 }
|
|
|
|
service.manager.Routines = make([]routines.Routine, len(service.Mounts))
|
2023-05-29 15:03:27 -06:00
|
|
|
for index, mount := range service.Mounts {
|
2023-05-31 16:08:29 -06:00
|
|
|
service.manager.Routines[index] = routines.From (func () error {
|
2023-05-29 15:03:27 -06:00
|
|
|
return mount.Run(service.ServiceInfo)
|
2023-05-31 16:08:29 -06:00
|
|
|
}, mount.Shutdown)
|
|
|
|
}
|
|
|
|
|
|
|
|
// be a daemon
|
|
|
|
daemon.ShutdownOnSigint(service)
|
|
|
|
pidfile := daemon.PidFile(os.Getenv("HNAKRA_PIDFILE"))
|
|
|
|
if !pidfile.Empty() {
|
|
|
|
err := pidfile.Start()
|
|
|
|
if err != nil { log.Println("!!! could not write pid:", err) }
|
|
|
|
defer func () {
|
|
|
|
err := pidfile.Close()
|
|
|
|
if err != nil { log.Println("!!! could not delete pidfile:", err) }
|
|
|
|
} ()
|
2023-05-26 18:01:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// send it
|
2023-05-31 16:08:29 -06:00
|
|
|
err := service.manager.Run()
|
2023-05-26 18:01:27 -06:00
|
|
|
if err != nil { log.Println("XXX", err) }
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close abruptly closes all mounts in the service. This will cause Run() to
|
|
|
|
// exit.
|
2023-05-29 15:03:27 -06:00
|
|
|
func (service *Service) Close () (err error) {
|
|
|
|
for _, mount := range service.Mounts {
|
2023-05-26 18:01:27 -06:00
|
|
|
singleErr := mount.Close()
|
|
|
|
if singleErr != nil {
|
|
|
|
err = singleErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown gracefully shuts down each mount in the service. This will cause
|
|
|
|
// Run() to exit.
|
2023-05-29 15:03:27 -06:00
|
|
|
func (service *Service) Shutdown () (err error) {
|
2023-05-31 16:08:29 -06:00
|
|
|
return service.manager.Shutdown()
|
2023-05-26 18:01:27 -06:00
|
|
|
}
|