github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zcli/service.go (about)

     1  package zcli
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"os"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/sohaha/zlsgo/zerror"
    12  	"github.com/sohaha/zlsgo/zfile"
    13  	"github.com/sohaha/zlsgo/zshell"
    14  	"github.com/sohaha/zlsgo/zutil"
    15  	"github.com/sohaha/zlsgo/zutil/daemon"
    16  )
    17  
    18  type (
    19  	app struct {
    20  		run    func()
    21  		status bool
    22  	}
    23  	serviceStop struct {
    24  	}
    25  	serviceStart struct {
    26  	}
    27  	serviceRestart struct {
    28  	}
    29  	serviceInstall struct {
    30  	}
    31  	serviceUnInstall struct {
    32  	}
    33  	serviceStatus struct {
    34  	}
    35  )
    36  
    37  var (
    38  	service    daemon.ServiceIface
    39  	serviceErr error
    40  	once       sync.Once
    41  )
    42  
    43  var s = make(chan struct{})
    44  
    45  func (a *app) Start(daemon.ServiceIface) error {
    46  	a.status = true
    47  	err := make(chan error, 1)
    48  	go func() {
    49  		err <- zerror.TryCatch(func() error {
    50  			a.run()
    51  			return nil
    52  		})
    53  		s <- struct{}{}
    54  	}()
    55  	return <-err
    56  }
    57  
    58  func (a *app) Stop(daemon.ServiceIface) error {
    59  	if !a.status {
    60  		return nil
    61  	}
    62  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    63  	defer cancel()
    64  	select {
    65  	case <-s:
    66  	case <-ctx.Done():
    67  		// return errors.New("forced timeout")
    68  	}
    69  	return nil
    70  }
    71  
    72  func (*serviceStatus) Flags(_ *Subcommand) {
    73  	CheckErr(serviceErr, true)
    74  }
    75  
    76  func (*serviceStatus) Run(_ []string) {
    77  	log.Printf("%s: %s\n", service.String(), service.Status())
    78  }
    79  
    80  func (*serviceInstall) Flags(_ *Subcommand) {
    81  	CheckErr(serviceErr, true)
    82  }
    83  
    84  func (*serviceInstall) Run(_ []string) {
    85  	CheckErr(service.Install(), true)
    86  	CheckErr(service.Start(), true)
    87  }
    88  
    89  func (*serviceUnInstall) Flags(_ *Subcommand) {
    90  	CheckErr(serviceErr, true)
    91  }
    92  
    93  func (*serviceUnInstall) Run(_ []string) {
    94  	CheckErr(service.Uninstall(), true)
    95  }
    96  
    97  func (*serviceStart) Flags(_ *Subcommand) {
    98  	CheckErr(serviceErr, true)
    99  }
   100  
   101  func (*serviceStart) Run(_ []string) {
   102  	CheckErr(service.Start(), true)
   103  }
   104  
   105  func (*serviceStop) Flags(_ *Subcommand) {
   106  	CheckErr(serviceErr, true)
   107  }
   108  
   109  func (*serviceStop) Run(_ []string) {
   110  	CheckErr(service.Stop(), true)
   111  }
   112  
   113  func (*serviceRestart) Flags(_ *Subcommand) {
   114  	CheckErr(serviceErr, true)
   115  }
   116  
   117  func (*serviceRestart) Run(_ []string) {
   118  	CheckErr(service.Restart(), true)
   119  }
   120  
   121  // LaunchServiceRun Launch Service and run
   122  func LaunchServiceRun(name string, description string, fn func(), config ...*daemon.Config) error {
   123  	_, _ = LaunchService(name, description, fn, config...)
   124  	Parse()
   125  	if *flagDetach {
   126  		return zshell.BgRun(strings.Join(runCmd, " "))
   127  	}
   128  	if serviceErr != nil && (serviceErr != daemon.ErrNoServiceSystemDetected && !daemon.IsPermissionError(serviceErr)) {
   129  		return serviceErr
   130  	}
   131  	if service == nil {
   132  		fn()
   133  		return nil
   134  	}
   135  	return service.Run()
   136  }
   137  
   138  // LaunchService Launch Service
   139  func LaunchService(name string, description string, fn func(), config ...*daemon.Config) (daemon.ServiceIface, error) {
   140  	once.Do(func() {
   141  		userService := false
   142  		if zutil.IsMac() {
   143  			userService = true
   144  		}
   145  		daemonConfig := &daemon.Config{
   146  			Name:        name,
   147  			Description: description,
   148  			Options: map[string]interface{}{
   149  				"UserService": userService,
   150  			},
   151  		}
   152  		if len(os.Args) > 2 {
   153  			daemonConfig.Arguments = os.Args[2:]
   154  		}
   155  
   156  		if len(config) > 0 {
   157  			nconf := config[0]
   158  			if nconf.Name == "" {
   159  				nconf.Name = name
   160  			}
   161  			if nconf.Description == "" {
   162  				nconf.Description = description
   163  			}
   164  			if len(nconf.Options) == 0 {
   165  				nconf.Options = daemonConfig.Options
   166  			}
   167  			if len(nconf.Arguments) == 0 {
   168  				nconf.Arguments = daemonConfig.Arguments
   169  			}
   170  			daemonConfig = nconf
   171  		}
   172  
   173  		// The file path is redirected to the current execution file path
   174  		_, gogccflags, _, _ := zshell.Run("go env GOGCCFLAGS")
   175  		if !strings.Contains(
   176  			gogccflags, zfile.RealPath(zfile.ProgramPath()+"../../../..")) {
   177  			zfile.ProjectPath = zfile.ProgramPath()
   178  		}
   179  
   180  		service, serviceErr = daemon.New(&app{
   181  			run: fn,
   182  		}, daemonConfig)
   183  
   184  		Add("install", GetLangText("install"), &serviceInstall{})
   185  		Add("uninstall", GetLangText("uninstall"), &serviceUnInstall{})
   186  		Add("status", GetLangText("status"), &serviceStatus{})
   187  		Add("start", GetLangText("start"), &serviceStart{})
   188  		Add("stop", GetLangText("stop"), &serviceStop{})
   189  		Add("restart", GetLangText("restart"), &serviceRestart{})
   190  	})
   191  
   192  	return service, serviceErr
   193  }