github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/service.go (about) 1 // +build varlink,!remoteclient 2 3 package main 4 5 import ( 6 "fmt" 7 "net" 8 "os" 9 "path/filepath" 10 "strings" 11 "time" 12 13 "github.com/containers/libpod/cmd/podman/cliconfig" 14 "github.com/containers/libpod/cmd/podman/libpodruntime" 15 "github.com/containers/libpod/libpod" 16 "github.com/containers/libpod/pkg/adapter" 17 api "github.com/containers/libpod/pkg/api/server" 18 "github.com/containers/libpod/pkg/rootless" 19 "github.com/containers/libpod/pkg/systemd" 20 "github.com/containers/libpod/pkg/util" 21 iopodman "github.com/containers/libpod/pkg/varlink" 22 "github.com/containers/libpod/pkg/varlinkapi" 23 "github.com/containers/libpod/version" 24 "github.com/pkg/errors" 25 "github.com/sirupsen/logrus" 26 "github.com/spf13/cobra" 27 "github.com/varlink/go/varlink" 28 ) 29 30 var ( 31 serviceCommand cliconfig.ServiceValues 32 serviceDescription = `Run an API service 33 34 Enable a listening service for API access to Podman commands. 35 ` 36 37 _serviceCommand = &cobra.Command{ 38 Use: "service [flags] [URI]", 39 Short: "Run API service", 40 Long: serviceDescription, 41 RunE: func(cmd *cobra.Command, args []string) error { 42 serviceCommand.InputArgs = args 43 serviceCommand.GlobalFlags = MainGlobalOpts 44 return serviceCmd(&serviceCommand) 45 }, 46 } 47 ) 48 49 func init() { 50 serviceCommand.Command = _serviceCommand 51 serviceCommand.SetHelpTemplate(HelpTemplate()) 52 serviceCommand.SetUsageTemplate(UsageTemplate()) 53 flags := serviceCommand.Flags() 54 flags.Int64VarP(&serviceCommand.Timeout, "timeout", "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout") 55 flags.BoolVar(&serviceCommand.Varlink, "varlink", false, "Use legacy varlink service instead of REST") 56 } 57 58 func serviceCmd(c *cliconfig.ServiceValues) error { 59 apiURI, err := resolveApiURI(c) 60 if err != nil { 61 return err 62 } 63 64 // Create a single runtime api consumption 65 runtime, err := libpodruntime.GetRuntimeDisableFDs(getContext(), &c.PodmanCommand) 66 if err != nil { 67 return errors.Wrapf(err, "error creating libpod runtime") 68 } 69 defer func() { 70 if err := runtime.Shutdown(false); err != nil { 71 fmt.Fprintf(os.Stderr, "Failed to shutdown libpod runtime: %v", err) 72 } 73 }() 74 75 timeout := time.Duration(c.Timeout) * time.Second 76 if c.Varlink { 77 return runVarlink(runtime, apiURI, timeout, c) 78 } 79 return runREST(runtime, apiURI, timeout) 80 } 81 82 func resolveApiURI(c *cliconfig.ServiceValues) (string, error) { 83 var apiURI string 84 85 // When determining _*THE*_ listening endpoint -- 86 // 1) User input wins always 87 // 2) systemd socket activation 88 // 3) rootless honors XDG_RUNTIME_DIR 89 // 4) if varlink -- adapter.DefaultVarlinkAddress 90 // 5) lastly adapter.DefaultAPIAddress 91 92 if len(c.InputArgs) > 0 { 93 apiURI = c.InputArgs[0] 94 } else if ok := systemd.SocketActivated(); ok { // nolint: gocritic 95 apiURI = "" 96 } else if rootless.IsRootless() { 97 xdg, err := util.GetRuntimeDir() 98 if err != nil { 99 return "", err 100 } 101 socketName := "podman.sock" 102 if c.Varlink { 103 socketName = "io.podman" 104 } 105 socketDir := filepath.Join(xdg, "podman", socketName) 106 if _, err := os.Stat(filepath.Dir(socketDir)); err != nil { 107 if os.IsNotExist(err) { 108 if err := os.Mkdir(filepath.Dir(socketDir), 0755); err != nil { 109 return "", err 110 } 111 } else { 112 return "", err 113 } 114 } 115 apiURI = "unix:" + socketDir 116 } else if c.Varlink { 117 apiURI = adapter.DefaultVarlinkAddress 118 } else { 119 // For V2, default to the REST socket 120 apiURI = adapter.DefaultAPIAddress 121 } 122 123 if "" == apiURI { 124 logrus.Info("using systemd socket activation to determine API endpoint") 125 } else { 126 logrus.Infof("using API endpoint: %s", apiURI) 127 } 128 return apiURI, nil 129 } 130 131 func runREST(r *libpod.Runtime, uri string, timeout time.Duration) error { 132 logrus.Warn("This function is EXPERIMENTAL") 133 fmt.Println("This function is EXPERIMENTAL.") 134 135 var listener *net.Listener 136 if uri != "" { 137 fields := strings.Split(uri, ":") 138 if len(fields) == 1 { 139 return errors.Errorf("%s is an invalid socket destination", uri) 140 } 141 address := strings.Join(fields[1:], ":") 142 l, err := net.Listen(fields[0], address) 143 if err != nil { 144 return errors.Wrapf(err, "unable to create socket %s", uri) 145 } 146 listener = &l 147 } 148 server, err := api.NewServerWithSettings(r, timeout, listener) 149 if err != nil { 150 return err 151 } 152 defer func() { 153 if err := server.Shutdown(); err != nil { 154 fmt.Fprintf(os.Stderr, "Error when stopping service: %s", err) 155 } 156 }() 157 158 return server.Serve() 159 } 160 161 func runVarlink(r *libpod.Runtime, uri string, timeout time.Duration, c *cliconfig.ServiceValues) error { 162 var varlinkInterfaces = []*iopodman.VarlinkInterface{varlinkapi.New(c.PodmanCommand.Command, r)} 163 service, err := varlink.NewService( 164 "Atomic", 165 "podman", 166 version.Version, 167 "https://github.com/containers/libpod", 168 ) 169 if err != nil { 170 return errors.Wrapf(err, "unable to create new varlink service") 171 } 172 173 for _, i := range varlinkInterfaces { 174 if err := service.RegisterInterface(i); err != nil { 175 return errors.Errorf("unable to register varlink interface %v", i) 176 } 177 } 178 179 // Run the varlink server at the given address 180 if err = service.Listen(uri, timeout); err != nil { 181 switch err.(type) { 182 case varlink.ServiceTimeoutError: 183 logrus.Infof("varlink service expired (use --timeout to increase session time beyond %s ms, 0 means never timeout)", timeout.String()) 184 return nil 185 default: 186 return errors.Wrapf(err, "unable to start varlink service") 187 } 188 } 189 return nil 190 }