github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/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/client/clientutil" 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, clientutil.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 }