github.com/Axway/agent-sdk@v1.1.101/pkg/cmd/service/daemon/daemonlinuxsystemd.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"regexp"
     7  	"strings"
     8  	"text/template"
     9  
    10  	"github.com/Axway/agent-sdk/pkg/cmd"
    11  )
    12  
    13  const (
    14  	systemctl     = "systemctl"
    15  	journalctl    = "journalctl"
    16  	serviceSuffix = ".service"
    17  )
    18  
    19  // systemDRecord - standard record (struct) for linux systemD version of daemon package
    20  type systemDRecord struct {
    21  	name         string
    22  	description  string
    23  	dependencies []string
    24  	user         string
    25  	group        string
    26  	envFile      string
    27  	workingDir   string
    28  }
    29  
    30  // Standard service path for systemD daemons
    31  func (s *systemDRecord) servicePath() string {
    32  	return "/etc/systemd/system/" + s.serviceName()
    33  }
    34  
    35  // Is a service installed
    36  func (s *systemDRecord) isInstalled() bool {
    37  
    38  	if _, err := fs.Stat(s.servicePath()); err == nil {
    39  		return true
    40  	}
    41  
    42  	return false
    43  }
    44  
    45  // Check service is running
    46  func (s *systemDRecord) checkRunning() (string, bool) {
    47  	output, err := execCmd(systemctl, "status", s.serviceName())
    48  	if err == nil {
    49  		if matched, err := regexp.MatchString("Active: active", string(output)); err == nil && matched {
    50  			reg := regexp.MustCompile("Main PID: ([0-9]+)")
    51  			data := reg.FindStringSubmatch(string(output))
    52  			if len(data) > 1 {
    53  				return "Service (pid  " + data[1] + ") is running...", true
    54  			}
    55  			return "Service is running...", true
    56  		}
    57  	}
    58  
    59  	return "Service is stopped", false
    60  }
    61  
    62  func (s *systemDRecord) serviceName() string {
    63  	return s.name + serviceSuffix
    64  }
    65  
    66  // Install the service
    67  func (s *systemDRecord) install(args ...string) (string, error) {
    68  	if ok, err := checkPrivileges(); !ok {
    69  		return failed, err
    70  	}
    71  
    72  	srvPath := s.servicePath()
    73  
    74  	if s.isInstalled() {
    75  		return failed, ErrAlreadyInstalled.FormatError(s.serviceName())
    76  	}
    77  
    78  	file, err := fs.Create(srvPath)
    79  	if err != nil {
    80  		return failed, err
    81  	}
    82  
    83  	execPath, err := executablePath(s.name)
    84  	if err != nil {
    85  		file.Close()
    86  		return failed, err
    87  	}
    88  
    89  	s.workingDir = filepath.Dir(execPath)
    90  
    91  	templ, err := template.New("systemDConfig").Parse(systemDConfig)
    92  	if err != nil {
    93  		file.Close()
    94  		return failed, err
    95  	}
    96  
    97  	if s.envFile != "" {
    98  		args = append(args, fmt.Sprintf("--%s", cmd.EnvFileFlag), s.envFile)
    99  	}
   100  
   101  	if err := templ.Execute(
   102  		file,
   103  		&struct {
   104  			Name, Description, Dependencies, User, Group, Path, WorkingDir, Args string
   105  		}{
   106  			s.name,
   107  			s.description,
   108  			strings.Join(s.dependencies, " "),
   109  			s.user,
   110  			s.group,
   111  			execPath,
   112  			s.workingDir,
   113  			strings.Join(args, " "),
   114  		},
   115  	); err != nil {
   116  		file.Close()
   117  		return failed, err
   118  	}
   119  
   120  	if _, err := execCmd(systemctl, "daemon-reload"); err != nil {
   121  		file.Close()
   122  		return failed, err
   123  	}
   124  
   125  	file.Close()
   126  	return success, nil
   127  }
   128  
   129  // Install the service
   130  func (s *systemDRecord) Install(args ...string) (string, error) {
   131  	installAction := "Install " + s.description + ":"
   132  
   133  	msg, err := s.install(args...)
   134  	return installAction + msg, err
   135  }
   136  
   137  // Update the service
   138  func (s *systemDRecord) Update(args ...string) (string, error) {
   139  	updateAction := "Updating " + s.description + ":"
   140  
   141  	msg, err := s.remove()
   142  	if err != nil {
   143  		return updateAction + msg, err
   144  	}
   145  
   146  	msg, err = s.install(args...)
   147  	return updateAction + msg, err
   148  }
   149  
   150  // Remove the service
   151  func (s *systemDRecord) remove() (string, error) {
   152  	if ok, err := checkPrivileges(); !ok {
   153  		return failed, err
   154  	}
   155  
   156  	if !s.isInstalled() {
   157  		return failed, ErrNotInstalled.FormatError(s.serviceName())
   158  	}
   159  
   160  	if _, ok := s.checkRunning(); ok {
   161  		return failed, ErrCurrentlyRunning.FormatError(s.serviceName())
   162  	}
   163  
   164  	if _, err := execCmd(systemctl, "disable", s.serviceName()); err != nil {
   165  		return failed, err
   166  	}
   167  
   168  	if err := fs.Remove(s.servicePath()); err != nil {
   169  		return failed, err
   170  	}
   171  
   172  	return success, nil
   173  
   174  }
   175  
   176  // Remove the service
   177  func (s *systemDRecord) Remove() (string, error) {
   178  	removeAction := "Removing " + s.description + ":"
   179  
   180  	msg, err := s.remove()
   181  	return removeAction + msg, err
   182  }
   183  
   184  // Start the service
   185  func (s *systemDRecord) Start() (string, error) {
   186  	startAction := "Starting " + s.description + ":"
   187  
   188  	if ok, err := checkPrivileges(); !ok {
   189  		return startAction + failed, err
   190  	}
   191  
   192  	if !s.isInstalled() {
   193  		return startAction + failed, ErrNotInstalled.FormatError(s.serviceName())
   194  	}
   195  
   196  	if _, ok := s.checkRunning(); ok {
   197  		return startAction + failed, ErrAlreadyRunning.FormatError(s.serviceName())
   198  	}
   199  
   200  	if _, err := execCmd(systemctl, "start", s.serviceName()); err != nil {
   201  		return startAction + failed, err
   202  	}
   203  
   204  	return startAction + success, nil
   205  }
   206  
   207  // Stop the service
   208  func (s *systemDRecord) Stop() (string, error) {
   209  	stopAction := "Stopping " + s.description + ":"
   210  
   211  	if ok, err := checkPrivileges(); !ok {
   212  		return stopAction + failed, err
   213  	}
   214  
   215  	if !s.isInstalled() {
   216  		return stopAction + failed, ErrNotInstalled.FormatError(s.serviceName())
   217  	}
   218  
   219  	if _, ok := s.checkRunning(); !ok {
   220  		return stopAction + failed, ErrAlreadyStopped.FormatError(s.serviceName())
   221  	}
   222  
   223  	if _, err := execCmd(systemctl, "stop", s.serviceName()); err != nil {
   224  		return stopAction + failed, err
   225  	}
   226  
   227  	return stopAction + success, nil
   228  }
   229  
   230  // Status - Get service status
   231  func (s *systemDRecord) Status() (string, error) {
   232  
   233  	if ok, err := checkPrivileges(); !ok {
   234  		return "", err
   235  	}
   236  
   237  	if !s.isInstalled() {
   238  		return statNotInstalled, ErrNotInstalled.FormatError(s.serviceName())
   239  	}
   240  
   241  	statusAction, _ := s.checkRunning()
   242  
   243  	return statusAction, nil
   244  }
   245  
   246  // Logs - Get service logs
   247  func (s *systemDRecord) Logs() (string, error) {
   248  
   249  	if !s.isInstalled() {
   250  		return statNotInstalled, ErrNotInstalled.FormatError(s.serviceName())
   251  	}
   252  
   253  	var data []byte
   254  	var err error
   255  
   256  	// run journalctl with --no-pager (get akk output), -b (logs on current boot only), -u service_name
   257  	if data, err = execCmd(journalctl, "--no-pager", "-b", "-u", s.serviceName()); err != nil {
   258  		return "", err
   259  	}
   260  
   261  	dataOutput := fmt.Sprintf("%s\nSee `journalctl -h` for alternative options to the `journalctl -u %s` command", string(data), s.serviceName())
   262  
   263  	return dataOutput, nil
   264  }
   265  
   266  // Run - Run service
   267  func (s *systemDRecord) Run(e Executable) (string, error) {
   268  	runAction := "Running " + s.description + ":"
   269  	e.Run()
   270  	return runAction + " completed.", nil
   271  }
   272  
   273  // Status - Get service status
   274  func (s *systemDRecord) Enable() (string, error) {
   275  	enableAction := "Enabling " + s.description + ":"
   276  
   277  	if ok, err := checkPrivileges(); !ok {
   278  		return enableAction + failed, err
   279  	}
   280  
   281  	if !s.isInstalled() {
   282  		return enableAction + failed, ErrNotInstalled.FormatError(s.serviceName())
   283  	}
   284  
   285  	if _, err := execCmd(systemctl, "enable", s.serviceName()); err != nil {
   286  		return enableAction + failed, err
   287  	}
   288  
   289  	return enableAction + success, nil
   290  }
   291  
   292  // GetTemplate - gets service config template
   293  func (s *systemDRecord) GetTemplate() string {
   294  	return systemDConfig
   295  }
   296  
   297  // GetServiceName - gets service name
   298  func (s *systemDRecord) GetServiceName() string {
   299  	return s.serviceName()
   300  }
   301  
   302  // SetTemplate - sets service config template
   303  func (s *systemDRecord) SetTemplate(tplStr string) error {
   304  	systemDConfig = tplStr
   305  	return nil
   306  }
   307  
   308  // SetEnvFile - sets the envFile that will be used by the service
   309  func (s *systemDRecord) SetEnvFile(envFile string) error {
   310  	// set the absolute path, incase it is relative
   311  	envFileAbsolute, _ := filepath.Abs(envFile)
   312  	s.envFile = envFileAbsolute
   313  	return nil
   314  }
   315  
   316  // SetUser - sets the user that will execute the service
   317  func (s *systemDRecord) SetUser(user string) error {
   318  	s.user = user
   319  	return nil
   320  }
   321  
   322  // SetGroup - sets the group that will execute the service
   323  func (s *systemDRecord) SetGroup(group string) error {
   324  	s.group = group
   325  	return nil
   326  }
   327  
   328  var systemDConfig = `[Unit]
   329  Description={{.Description}}
   330  Requires={{.Dependencies}}
   331  After={{.Dependencies}}
   332  
   333  [Service]
   334  ExecStart={{.Path}} {{.Args}}
   335  User={{.User}}
   336  Group={{.Group}}
   337  WorkingDirectory={{.WorkingDir}}
   338  Restart=on-failure
   339  RestartSec=60s
   340  
   341  [Install]
   342  WantedBy=multi-user.target
   343  `