github.com/containers/podman/v4@v4.9.4/pkg/machine/machine_common.go (about)

     1  //go:build amd64 || arm64
     2  // +build amd64 arm64
     3  
     4  package machine
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/url"
    10  	"os"
    11  	"strconv"
    12  
    13  	"github.com/containers/storage/pkg/ioutils"
    14  )
    15  
    16  // GetDevNullFiles returns pointers to Read-only and Write-only DevNull files
    17  func GetDevNullFiles() (*os.File, *os.File, error) {
    18  	dnr, err := os.OpenFile(os.DevNull, os.O_RDONLY, 0755)
    19  	if err != nil {
    20  		return nil, nil, err
    21  	}
    22  
    23  	dnw, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0755)
    24  	if err != nil {
    25  		if e := dnr.Close(); e != nil {
    26  			err = e
    27  		}
    28  		return nil, nil, err
    29  	}
    30  
    31  	return dnr, dnw, nil
    32  }
    33  
    34  // AddSSHConnectionsToPodmanSocket adds SSH connections to the podman socket if
    35  // no ignition path is provided
    36  func AddSSHConnectionsToPodmanSocket(uid, port int, identityPath, name, remoteUsername string, opts InitOptions) error {
    37  	if len(opts.IgnitionPath) > 0 {
    38  		fmt.Println("An ignition path was provided.  No SSH connection was added to Podman")
    39  		return nil
    40  	}
    41  	uri := SSHRemoteConnection.MakeSSHURL(LocalhostIP, fmt.Sprintf("/run/user/%d/podman/podman.sock", uid), strconv.Itoa(port), remoteUsername)
    42  	uriRoot := SSHRemoteConnection.MakeSSHURL(LocalhostIP, "/run/podman/podman.sock", strconv.Itoa(port), "root")
    43  
    44  	uris := []url.URL{uri, uriRoot}
    45  	names := []string{name, name + "-root"}
    46  
    47  	// The first connection defined when connections is empty will become the default
    48  	// regardless of IsDefault, so order according to rootful
    49  	if opts.Rootful {
    50  		uris[0], names[0], uris[1], names[1] = uris[1], names[1], uris[0], names[0]
    51  	}
    52  
    53  	for i := 0; i < 2; i++ {
    54  		if err := AddConnection(&uris[i], names[i], identityPath, opts.IsDefault && i == 0); err != nil {
    55  			return err
    56  		}
    57  	}
    58  	return nil
    59  }
    60  
    61  // WaitAPIAndPrintInfo prints info about the machine and does a ping test on the
    62  // API socket
    63  func WaitAPIAndPrintInfo(forwardState APIForwardingState, name, helper, forwardSock string, noInfo, isIncompatible, rootful bool) {
    64  	suffix := ""
    65  	var fmtString string
    66  
    67  	if name != DefaultMachineName {
    68  		suffix = " " + name
    69  	}
    70  
    71  	if isIncompatible {
    72  		fmtString = `
    73  !!! ACTION REQUIRED: INCOMPATIBLE MACHINE !!!
    74  
    75  This machine was created by an older podman release that is incompatible
    76  with this release of podman. It has been started in a limited operational
    77  mode to allow you to copy any necessary files before recreating it. This
    78  can be accomplished with the following commands:
    79  
    80          # Login and copy desired files (Optional)
    81          # podman machine ssh%[1]s tar cvPf - /path/to/files > backup.tar
    82  
    83          # Recreate machine (DESTRUCTIVE!)
    84          podman machine stop%[1]s
    85          podman machine rm -f%[1]s
    86          podman machine init --now%[1]s
    87  
    88          # Copy back files (Optional)
    89          # cat backup.tar | podman machine ssh%[1]s tar xvPf -
    90  
    91  `
    92  
    93  		fmt.Fprintf(os.Stderr, fmtString, suffix)
    94  	}
    95  
    96  	if forwardState == NoForwarding {
    97  		return
    98  	}
    99  
   100  	WaitAndPingAPI(forwardSock)
   101  
   102  	if !noInfo {
   103  		if !rootful {
   104  			fmtString = `
   105  This machine is currently configured in rootless mode. If your containers
   106  require root permissions (e.g. ports < 1024), or if you run into compatibility
   107  issues with non-podman clients, you can switch using the following command:
   108  
   109          podman machine set --rootful%s
   110  
   111  `
   112  
   113  			fmt.Printf(fmtString, suffix)
   114  		}
   115  
   116  		fmt.Printf("API forwarding listening on: %s\n", forwardSock)
   117  		if forwardState == DockerGlobal {
   118  			fmt.Printf("Docker API clients default to this address. You do not need to set DOCKER_HOST.\n\n")
   119  		} else {
   120  			stillString := "still "
   121  			switch forwardState {
   122  			case NotInstalled:
   123  
   124  				fmtString = `
   125  The system helper service is not installed; the default Docker API socket
   126  address can't be used by podman. `
   127  
   128  				if len(helper) < 1 {
   129  					fmt.Print(fmtString)
   130  				} else {
   131  					fmtString += `If you would like to install it, run the following commands:
   132  
   133          sudo %s install
   134          podman machine stop%[2]s; podman machine start%[2]s
   135  
   136                  `
   137  					fmt.Printf(fmtString, helper, suffix)
   138  				}
   139  			case MachineLocal:
   140  				fmt.Printf("\nAnother process was listening on the default Docker API socket address.\n")
   141  			case ClaimUnsupported:
   142  				fallthrough
   143  			default:
   144  				stillString = ""
   145  			}
   146  
   147  			fmtString = `You can %sconnect Docker API clients by setting DOCKER_HOST using the
   148  following command in your terminal session:
   149  
   150          export DOCKER_HOST='unix://%s'
   151  
   152  `
   153  
   154  			fmt.Printf(fmtString, stillString, forwardSock)
   155  		}
   156  	}
   157  }
   158  
   159  // SetRootful modifies the machine's default connection to be either rootful or
   160  // rootless
   161  func SetRootful(rootful bool, name, rootfulName string) error {
   162  	changeCon, err := AnyConnectionDefault(name, rootfulName)
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	if changeCon {
   168  		newDefault := name
   169  		if rootful {
   170  			newDefault += "-root"
   171  		}
   172  		err := ChangeDefault(newDefault)
   173  		if err != nil {
   174  			return err
   175  		}
   176  	}
   177  	return nil
   178  }
   179  
   180  // WriteConfig writes the machine's JSON config file
   181  func WriteConfig(configPath string, v VM) error {
   182  	opts := &ioutils.AtomicFileWriterOptions{ExplicitCommit: true}
   183  	w, err := ioutils.NewAtomicFileWriterWithOpts(configPath, 0644, opts)
   184  	if err != nil {
   185  		return err
   186  	}
   187  	defer w.Close()
   188  
   189  	enc := json.NewEncoder(w)
   190  	enc.SetIndent("", " ")
   191  
   192  	if err := enc.Encode(v); err != nil {
   193  		return err
   194  	}
   195  
   196  	// Commit the changes to disk if no errors
   197  	return w.Commit()
   198  }