github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/hookstate/ctlcmd/services.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 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 ctlcmd 21 22 import ( 23 "errors" 24 "fmt" 25 "sort" 26 "text/tabwriter" 27 28 "github.com/snapcore/snapd/client/clientutil" 29 "github.com/snapcore/snapd/i18n" 30 "github.com/snapcore/snapd/overlord/servicestate" 31 "github.com/snapcore/snapd/progress" 32 "github.com/snapcore/snapd/snap" 33 ) 34 35 var ( 36 shortServicesHelp = i18n.G("Query the status of services") 37 longServicesHelp = i18n.G(` 38 The services command lists information about the services specified. 39 `) 40 ) 41 42 func init() { 43 addCommand("services", shortServicesHelp, longServicesHelp, func() command { return &servicesCommand{} }) 44 } 45 46 type servicesCommand struct { 47 baseCommand 48 Positional struct { 49 ServiceNames []string `positional-arg-name:"<service>"` 50 } `positional-args:"yes"` 51 } 52 53 var errNoContextForServices = errors.New(i18n.G("cannot query services without a context")) 54 55 type byApp []*snap.AppInfo 56 57 func (a byApp) Len() int { return len(a) } 58 func (a byApp) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 59 func (a byApp) Less(i, j int) bool { 60 return a[i].Name < a[j].Name 61 } 62 63 func (c *servicesCommand) Execute([]string) error { 64 context := c.context() 65 if context == nil { 66 return errNoContextForServices 67 } 68 69 st := context.State() 70 svcInfos, err := getServiceInfos(st, context.InstanceName(), c.Positional.ServiceNames) 71 if err != nil { 72 return err 73 } 74 sort.Sort(byApp(svcInfos)) 75 76 sd := servicestate.NewStatusDecorator(progress.Null) 77 78 services, err := clientutil.ClientAppInfosFromSnapAppInfos(svcInfos, sd) 79 if err != nil || len(services) == 0 { 80 return err 81 } 82 83 w := tabwriter.NewWriter(c.stdout, 5, 3, 2, ' ', 0) 84 defer w.Flush() 85 86 fmt.Fprintln(w, i18n.G("Service\tStartup\tCurrent\tNotes")) 87 88 for _, svc := range services { 89 startup := i18n.G("disabled") 90 if svc.Enabled { 91 startup = i18n.G("enabled") 92 } 93 current := i18n.G("inactive") 94 if svc.DaemonScope == snap.UserDaemon { 95 current = "-" 96 } else if svc.Active { 97 current = i18n.G("active") 98 } 99 fmt.Fprintf(w, "%s.%s\t%s\t%s\t%s\n", svc.Snap, svc.Name, startup, current, clientutil.ClientAppInfoNotes(&svc)) 100 } 101 102 return nil 103 }