github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/snap/cmd_services.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package main
    21  
    22  import (
    23  	"fmt"
    24  	"strconv"
    25  
    26  	"github.com/jessevdk/go-flags"
    27  
    28  	"github.com/snapcore/snapd/client"
    29  	"github.com/snapcore/snapd/cmd"
    30  	"github.com/snapcore/snapd/i18n"
    31  )
    32  
    33  type svcStatus struct {
    34  	clientMixin
    35  	Positional struct {
    36  		ServiceNames []serviceName
    37  	} `positional-args:"yes"`
    38  }
    39  
    40  type svcLogs struct {
    41  	clientMixin
    42  	N          string `short:"n" default:"10"`
    43  	Follow     bool   `short:"f"`
    44  	Positional struct {
    45  		ServiceNames []serviceName `required:"1"`
    46  	} `positional-args:"yes" required:"yes"`
    47  }
    48  
    49  var (
    50  	shortServicesHelp = i18n.G("Query the status of services")
    51  	longServicesHelp  = i18n.G(`
    52  The services command lists information about the services specified, or about
    53  the services in all currently installed snaps.
    54  `)
    55  	shortLogsHelp = i18n.G("Retrieve logs for services")
    56  	longLogsHelp  = i18n.G(`
    57  The logs command fetches logs of the given services and displays them in
    58  chronological order.
    59  `)
    60  	shortStartHelp = i18n.G("Start services")
    61  	longStartHelp  = i18n.G(`
    62  The start command starts, and optionally enables, the given services.
    63  `)
    64  	shortStopHelp = i18n.G("Stop services")
    65  	longStopHelp  = i18n.G(`
    66  The stop command stops, and optionally disables, the given services.
    67  `)
    68  	shortRestartHelp = i18n.G("Restart services")
    69  	longRestartHelp  = i18n.G(`
    70  The restart command restarts the given services.
    71  
    72  If the --reload option is given, for each service whose app has a reload
    73  command, a reload is performed instead of a restart.
    74  `)
    75  )
    76  
    77  func init() {
    78  	argdescs := []argDesc{{
    79  		// TRANSLATORS: This needs to begin with < and end with >
    80  		name: i18n.G("<service>"),
    81  		// TRANSLATORS: This should not start with a lowercase letter.
    82  		desc: i18n.G("A service specification, which can be just a snap name (for all services in the snap), or <snap>.<app> for a single service."),
    83  	}}
    84  	addCommand("services", shortServicesHelp, longServicesHelp, func() flags.Commander { return &svcStatus{} }, nil, argdescs)
    85  	addCommand("logs", shortLogsHelp, longLogsHelp, func() flags.Commander { return &svcLogs{} },
    86  		map[string]string{
    87  			// TRANSLATORS: This should not start with a lowercase letter.
    88  			"n": i18n.G("Show only the given number of lines, or 'all'."),
    89  			// TRANSLATORS: This should not start with a lowercase letter.
    90  			"f": i18n.G("Wait for new lines and print them as they come in."),
    91  		}, argdescs)
    92  
    93  	addCommand("start", shortStartHelp, longStartHelp, func() flags.Commander { return &svcStart{} },
    94  		waitDescs.also(map[string]string{
    95  			// TRANSLATORS: This should not start with a lowercase letter.
    96  			"enable": i18n.G("As well as starting the service now, arrange for it to be started on boot."),
    97  		}), argdescs)
    98  	addCommand("stop", shortStopHelp, longStopHelp, func() flags.Commander { return &svcStop{} },
    99  		waitDescs.also(map[string]string{
   100  			// TRANSLATORS: This should not start with a lowercase letter.
   101  			"disable": i18n.G("As well as stopping the service now, arrange for it to no longer be started on boot."),
   102  		}), argdescs)
   103  	addCommand("restart", shortRestartHelp, longRestartHelp, func() flags.Commander { return &svcRestart{} },
   104  		waitDescs.also(map[string]string{
   105  			// TRANSLATORS: This should not start with a lowercase letter.
   106  			"reload": i18n.G("If the service has a reload command, use it instead of restarting."),
   107  		}), argdescs)
   108  }
   109  
   110  func svcNames(s []serviceName) []string {
   111  	svcNames := make([]string, len(s))
   112  	for i, svcName := range s {
   113  		svcNames[i] = string(svcName)
   114  	}
   115  	return svcNames
   116  }
   117  
   118  func (s *svcStatus) Execute(args []string) error {
   119  	if len(args) > 0 {
   120  		return ErrExtraArgs
   121  	}
   122  
   123  	services, err := s.client.Apps(svcNames(s.Positional.ServiceNames), client.AppOptions{Service: true})
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	if len(services) == 0 {
   129  		fmt.Fprintln(Stderr, i18n.G("There are no services provided by installed snaps."))
   130  		return nil
   131  	}
   132  
   133  	w := tabWriter()
   134  	defer w.Flush()
   135  
   136  	fmt.Fprintln(w, i18n.G("Service\tStartup\tCurrent\tNotes"))
   137  
   138  	for _, svc := range services {
   139  		startup := i18n.G("disabled")
   140  		if svc.Enabled {
   141  			startup = i18n.G("enabled")
   142  		}
   143  		current := i18n.G("inactive")
   144  		if svc.Active {
   145  			current = i18n.G("active")
   146  		}
   147  		fmt.Fprintf(w, "%s.%s\t%s\t%s\t%s\n", svc.Snap, svc.Name, startup, current, cmd.ClientAppInfoNotes(svc))
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  func (s *svcLogs) Execute(args []string) error {
   154  	if len(args) > 0 {
   155  		return ErrExtraArgs
   156  	}
   157  
   158  	sN := -1
   159  	if s.N != "all" {
   160  		n, err := strconv.ParseInt(s.N, 0, 32)
   161  		if n < 0 || err != nil {
   162  			return fmt.Errorf(i18n.G("invalid argument for flag ‘-n’: expected a non-negative integer argument, or “all”."))
   163  		}
   164  		sN = int(n)
   165  	}
   166  
   167  	logs, err := s.client.Logs(svcNames(s.Positional.ServiceNames), client.LogOptions{N: sN, Follow: s.Follow})
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	for log := range logs {
   173  		fmt.Fprintln(Stdout, log)
   174  	}
   175  
   176  	return nil
   177  }
   178  
   179  type svcStart struct {
   180  	waitMixin
   181  	Positional struct {
   182  		ServiceNames []serviceName `required:"1"`
   183  	} `positional-args:"yes" required:"yes"`
   184  	Enable bool `long:"enable"`
   185  }
   186  
   187  func (s *svcStart) Execute(args []string) error {
   188  	if len(args) > 0 {
   189  		return ErrExtraArgs
   190  	}
   191  	names := svcNames(s.Positional.ServiceNames)
   192  	changeID, err := s.client.Start(names, client.StartOptions{Enable: s.Enable})
   193  	if err != nil {
   194  		return err
   195  	}
   196  	if _, err := s.wait(changeID); err != nil {
   197  		if err == noWait {
   198  			return nil
   199  		}
   200  		return err
   201  	}
   202  
   203  	fmt.Fprintf(Stdout, i18n.G("Started.\n"))
   204  
   205  	return nil
   206  }
   207  
   208  type svcStop struct {
   209  	waitMixin
   210  	Positional struct {
   211  		ServiceNames []serviceName `required:"1"`
   212  	} `positional-args:"yes" required:"yes"`
   213  	Disable bool `long:"disable"`
   214  }
   215  
   216  func (s *svcStop) Execute(args []string) error {
   217  	if len(args) > 0 {
   218  		return ErrExtraArgs
   219  	}
   220  	names := svcNames(s.Positional.ServiceNames)
   221  	changeID, err := s.client.Stop(names, client.StopOptions{Disable: s.Disable})
   222  	if err != nil {
   223  		return err
   224  	}
   225  	if _, err := s.wait(changeID); err != nil {
   226  		if err == noWait {
   227  			return nil
   228  		}
   229  		return err
   230  	}
   231  
   232  	fmt.Fprintf(Stdout, i18n.G("Stopped.\n"))
   233  
   234  	return nil
   235  }
   236  
   237  type svcRestart struct {
   238  	waitMixin
   239  	Positional struct {
   240  		ServiceNames []serviceName `required:"1"`
   241  	} `positional-args:"yes" required:"yes"`
   242  	Reload bool `long:"reload"`
   243  }
   244  
   245  func (s *svcRestart) Execute(args []string) error {
   246  	if len(args) > 0 {
   247  		return ErrExtraArgs
   248  	}
   249  	names := svcNames(s.Positional.ServiceNames)
   250  	changeID, err := s.client.Restart(names, client.RestartOptions{Reload: s.Reload})
   251  	if err != nil {
   252  		return err
   253  	}
   254  	if _, err := s.wait(changeID); err != nil {
   255  		if err == noWait {
   256  			return nil
   257  		}
   258  		return err
   259  	}
   260  
   261  	fmt.Fprintf(Stdout, i18n.G("Restarted.\n"))
   262  
   263  	return nil
   264  }