service icon indicating copy to clipboard operation
service copied to clipboard

`Service.Status` returns wrong error

Open EugeneOne1 opened this issue 3 years ago • 0 comments

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.

EugeneOne1 avatar May 16 '22 16:05 EugeneOne1