service
service copied to clipboard
`Service.Status` returns wrong error
At least in the macOS Service implementation, there are lines:
https://github.com/kardianos/service/blob/29f8c79c511bc18422bb99992779f96e6bc33921/service_darwin.go#L235-L239
So it acts on the assumption that os.Stat may only return the fs.ErrNotExists error which is not true. Here is a piece of code that meets another condition:
package main
import (
"fmt"
"os"
"os/user"
"path/filepath"
"github.com/kardianos/service"
)
// Svc is a stub service.Interface implementation.
type Svc struct{}
// type check.
var _ service.Interface = Svc{}
func (Svc) Start(s service.Service) (err error) { return nil }
func (Svc) Stop(s service.Service) (err error) { return nil }
func dieOnErr(err error) {
if err != nil {
panic(err)
}
}
func printStatus(step string, s service.Service) {
fmt.Println(step)
st, err := s.Status()
switch st {
case service.StatusUnknown:
fmt.Println("status: unknown")
case service.StatusRunning:
fmt.Println("status: running")
case service.StatusStopped:
fmt.Println("status: stopped")
default:
fmt.Printf("status: %d\n", st)
}
if err != nil {
fmt.Printf("occurred error: %v\n", err)
}
}
func main() {
user, err := user.Current()
dieOnErr(err)
conf := &service.Config{
Name: "nop",
Option: service.KeyValue{
"UserService": true,
},
}
s, err := service.New(Svc{}, conf)
dieOnErr(err)
dieOnErr(s.Install())
defer func() {
dieOnErr(s.Uninstall())
}()
homeDir := user.HomeDir
dirPath := homeDir + "/Library/LaunchAgents"
fi, err := os.Stat(dirPath)
dieOnErr(err)
fm := fi.Mode()
err = os.Chmod(dirPath, 0o000)
dieOnErr(err)
printStatus("changed permissions:", s)
plist := filepath.Join(dirPath, conf.Name+".plist")
_, err = os.Stat(plist)
fmt.Println("real error:", err)
err = os.Chmod(dirPath, fm)
dieOnErr(err)
}
It prints:
changed permissions:
status: unknown
occurred error: the service is not installed
real error: stat <MY_HOME_DIR>/Library/LaunchAgents/nop.plist: permission denied
I think it would be useful for a real user of the package to know exactly which error occurred. I'd suggest returning service.ErrNotInstalled error only when the errors.Is(err, fs.ErrNotExist) == true, where err is an error returned by os.Stat.