// Package service provides a toolkit for creating Hnakra services. package service import "os" import "log" import "time" import "hnakra/rotate" import "hnakra/daemon" import "hnakra/routines" // Service is capable of managing multiple mounts. It also sets up logging // automatically. type Service struct { ServiceInfo Mounts []Mount manager routines.Manager } // 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, } } // 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. // Additionally, if $HNAKRA_PIDFILE is set, it will write the process PID to the // file specified by it. func (service *Service) Run () error { // 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) } log.Println("... starting service", service.Name) // set up routine manager service.manager = routines.Manager { RestartDeadline: time.Second * 8 } service.manager.Routines = make([]routines.Routine, len(service.Mounts)) for index, mount := range service.Mounts { service.manager.Routines[index] = routines.From (func () error { return mount.Run(service.ServiceInfo) }, 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) } } () } // send it err := service.manager.Run() if err != nil { log.Println("XXX", err) } return err } // Close abruptly closes all mounts in the service. This will cause Run() to // exit. func (service *Service) Close () (err error) { for _, mount := range service.Mounts { singleErr := mount.Close() if singleErr != nil { err = singleErr } } return } // Shutdown gracefully shuts down each mount in the service. This will cause // Run() to exit. func (service *Service) Shutdown () (err error) { return service.manager.Shutdown() }