gitee.com/quant1x/gox@v1.21.2/daemon/daemon.go (about)

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6  Package daemon v1.0.0 for use with Go (golang) services.
     7  
     8  Package daemon provides primitives for daemonization of golang services. In the
     9  current implementation the only supported operating systems are macOS, FreeBSD,
    10  Linux and Windows. Also to note, for global daemons one must have root rights to
    11  install or remove the service. The only exception is macOS where there is an
    12  implementation of a user daemon that can installed or removed by the current
    13  user.
    14  
    15  Example:
    16  
    17  		// Example of a daemon with echo service
    18  		package main
    19  
    20  		import (
    21  			"fmt"
    22  			"log"
    23  			"net"
    24  			"os"
    25  			"os/signal"
    26  			"syscall"
    27  
    28  			"github.com/takama/daemon"
    29  		)
    30  
    31  		const (
    32  
    33  			// name of the service
    34  			name        = "myservice"
    35  			description = "My Echo Service"
    36  
    37  			// port which daemon should be listen
    38  			port = ":9977"
    39  		)
    40  
    41  	  // dependencies that are NOT required by the service, but might be used
    42  	  var dependencies = []string{"dummy.service"}
    43  
    44  		var stdlog, errlog *log.Logger
    45  
    46  		// Service has embedded daemon
    47  		type Service struct {
    48  			daemon.Daemon
    49  		}
    50  
    51  		// Manage by daemon commands or run the daemon
    52  		func (service *Service) Manage() (string, error) {
    53  
    54  			usage := "Usage: myservice install | remove | start | stop | status"
    55  
    56  			// if received any kind of command, do it
    57  			if len(os.Args) > 1 {
    58  				command := os.Args[1]
    59  				switch command {
    60  				case "install":
    61  					return service.Install()
    62  				case "remove":
    63  					return service.Remove()
    64  				case "start":
    65  					return service.Start()
    66  				case "stop":
    67  					return service.Stop()
    68  				case "status":
    69  					return service.Status()
    70  				default:
    71  					return usage, nil
    72  				}
    73  			}
    74  
    75  			// Do something, call your goroutines, etc
    76  
    77  			// Set up channel on which to send signal notifications.
    78  			// We must use a buffered channel or risk missing the signal
    79  			// if we're not ready to receive when the signal is sent.
    80  			interrupt := make(chan os.Signal, 1)
    81  			signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)
    82  
    83  			// Set up listener for defined host and port
    84  			listener, err := net.Listen("tcp", port)
    85  			if err != nil {
    86  				return "Possibly was a problem with the port binding", err
    87  			}
    88  
    89  			// set up channel on which to send accepted connections
    90  			listen := make(chan net.Conn, 100)
    91  			go acceptConnection(listener, listen)
    92  
    93  			// loop work cycle with accept connections or interrupt
    94  			// by system signal
    95  			for {
    96  				select {
    97  				case conn := <-listen:
    98  					go handleClient(conn)
    99  				case killSignal := <-interrupt:
   100  					stdlog.Println("Got signal:", killSignal)
   101  					stdlog.Println("Stoping listening on ", listener.Addr())
   102  					listener.Close()
   103  					if killSignal == os.Interrupt {
   104  						return "Daemon was interrupted by system signal", nil
   105  					}
   106  					return "Daemon was killed", nil
   107  				}
   108  			}
   109  
   110  			// never happen, but need to complete code
   111  			return usage, nil
   112  		}
   113  
   114  		// Accept a client connection and collect it in a channel
   115  		func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
   116  			for {
   117  				conn, err := listener.Accept()
   118  				if err != nil {
   119  					continue
   120  				}
   121  				listen <- conn
   122  			}
   123  		}
   124  
   125  		func handleClient(client net.Conn) {
   126  			for {
   127  				buf := make([]byte, 4096)
   128  				numbytes, err := client.Read(buf)
   129  				if numbytes == 0 || err != nil {
   130  					return
   131  				}
   132  				client.Write(buf[:numbytes])
   133  			}
   134  		}
   135  
   136  		func init() {
   137  			stdlog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
   138  			errlog = log.New(os.Stderr, "", log.Ldate|log.Ltime)
   139  		}
   140  
   141  		func main() {
   142  			srv, err := daemon.New(name, description, daemon.SystemDaemon, dependencies...)
   143  			if err != nil {
   144  				errlog.Println("Error: ", err)
   145  				os.Exit(1)
   146  			}
   147  			service := &Service{srv}
   148  			status, err := service.Manage()
   149  			if err != nil {
   150  				errlog.Println(status, "\nError: ", err)
   151  				os.Exit(1)
   152  			}
   153  			fmt.Println(status)
   154  		}
   155  
   156  Go daemon
   157  */
   158  package daemon
   159  
   160  import (
   161  	"errors"
   162  	"runtime"
   163  	"strings"
   164  )
   165  
   166  // Status constants.
   167  const (
   168  	statNotInstalled = "Service not installed"
   169  )
   170  
   171  // Daemon interface has a standard set of methods/commands
   172  type Daemon interface {
   173  	// GetTemplate - gets service config template
   174  	GetTemplate() string
   175  
   176  	// SetTemplate - sets service config template
   177  	SetTemplate(string) error
   178  
   179  	// Install the service into the system
   180  	Install(args ...string) (string, error)
   181  
   182  	// Remove the service and all corresponding files from the system
   183  	Remove() (string, error)
   184  
   185  	// Start the service
   186  	Start() (string, error)
   187  
   188  	// Stop the service
   189  	Stop() (string, error)
   190  
   191  	// Status - check the service status
   192  	Status() (string, error)
   193  
   194  	// Run - run executable service
   195  	Run(e Executable) (string, error)
   196  }
   197  
   198  // Executable interface defines controlling methods of executable service
   199  type Executable interface {
   200  	// Start - non-blocking start service
   201  	Start()
   202  	// Stop - non-blocking stop service
   203  	Stop()
   204  	// Run - blocking run service
   205  	Run()
   206  }
   207  
   208  // Kind is type of the daemon
   209  type Kind string
   210  
   211  const (
   212  	// UserAgent is a user daemon that runs as the currently logged in user and
   213  	// stores its property list in the user’s individual LaunchAgents directory.
   214  	// In other words, per-user agents provided by the user. Valid for macOS only.
   215  	UserAgent Kind = "UserAgent"
   216  
   217  	// GlobalAgent is a user daemon that runs as the currently logged in user and
   218  	// stores its property list in the users' global LaunchAgents directory. In
   219  	// other words, per-user agents provided by the administrator. Valid for macOS
   220  	// only.
   221  	GlobalAgent Kind = "GlobalAgent"
   222  
   223  	// GlobalDaemon is a system daemon that runs as the root user and stores its
   224  	// property list in the global LaunchDaemons directory. In other words,
   225  	// system-wide daemons provided by the administrator. Valid for macOS only.
   226  	GlobalDaemon Kind = "GlobalDaemon"
   227  
   228  	// SystemDaemon is a system daemon that runs as the root user. In other words,
   229  	// system-wide daemons provided by the administrator. Valid for FreeBSD, Linux
   230  	// and Windows only.
   231  	SystemDaemon Kind = "SystemDaemon"
   232  )
   233  
   234  // New - Create a new daemon
   235  //
   236  // name: name of the service
   237  //
   238  // description: any explanation, what is the service, its purpose
   239  //
   240  // kind: what kind of daemon to create
   241  func New(name, description string, kind Kind, dependencies ...string) (Daemon, error) {
   242  	switch runtime.GOOS {
   243  	case "darwin":
   244  		if kind == SystemDaemon {
   245  			return nil, errors.New("Invalid daemon kind specified")
   246  		}
   247  	case "freebsd":
   248  		if kind != SystemDaemon {
   249  			return nil, errors.New("Invalid daemon kind specified")
   250  		}
   251  	case "linux":
   252  		if kind != SystemDaemon {
   253  			return nil, errors.New("Invalid daemon kind specified")
   254  		}
   255  	case "windows":
   256  		if kind != SystemDaemon {
   257  			return nil, errors.New("Invalid daemon kind specified")
   258  		}
   259  	}
   260  
   261  	return newDaemon(strings.Join(strings.Fields(name), "_"), description, kind, dependencies)
   262  }