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  }