diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/containers_archive.go | 11 | ||||
-rw-r--r-- | pkg/bindings/containers/archive.go | 12 | ||||
-rw-r--r-- | pkg/bindings/containers/types.go | 8 | ||||
-rw-r--r-- | pkg/bindings/containers/types_copy_options.go | 37 | ||||
-rw-r--r-- | pkg/bindings/internal/util/util.go | 10 | ||||
-rw-r--r-- | pkg/domain/entities/containers.go | 7 | ||||
-rw-r--r-- | pkg/domain/entities/engine_container.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/archive.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 4 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 22 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 8 | ||||
-rw-r--r-- | pkg/servicereaper/service.go | 64 |
12 files changed, 169 insertions, 20 deletions
diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index e119dc7cb..a9d74e5f4 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -9,6 +9,7 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/copy" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -92,11 +93,13 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { query := struct { - Path string `schema:"path"` + Path string `schema:"path"` + Chown bool `schema:"copyUIDGID"` // TODO handle params below NoOverwriteDirNonDir bool `schema:"noOverwriteDirNonDir"` - CopyUIDGID bool `schema:"copyUIDGID"` - }{} + }{ + Chown: utils.IsLibpodRequest(r), // backward compatibility + } err := decoder.Decode(&query, r.URL.Query()) if err != nil { @@ -107,7 +110,7 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, containerName := utils.GetName(r) containerEngine := abi.ContainerEngine{Libpod: runtime} - copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body) + copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body, entities.CopyOptions{Chown: query.Chown}) if errors.Cause(err) == define.ErrNoSuchCtr || os.IsNotExist(err) { // 404 is returned for an absent container and path. The // clients must deal with it accordingly. diff --git a/pkg/bindings/containers/archive.go b/pkg/bindings/containers/archive.go index 0178f63c3..52b73662b 100644 --- a/pkg/bindings/containers/archive.go +++ b/pkg/bindings/containers/archive.go @@ -50,11 +50,21 @@ func Stat(ctx context.Context, nameOrID string, path string) (*entities.Containe } func CopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) { + return CopyFromArchiveWithOptions(ctx, nameOrID, path, reader, nil) +} + +// CopyFromArchiveWithOptions FIXME: remove this function and make CopyFromArchive accept the option as the last parameter in podman 4.0 +func CopyFromArchiveWithOptions(ctx context.Context, nameOrID string, path string, reader io.Reader, options *CopyOptions) (entities.ContainerCopyFunc, error) { conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} + + params, err := options.ToParams() + if err != nil { + return nil, err + } + params.Set("path", path) return func() error { diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 80f382926..1058c7a48 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -256,3 +256,11 @@ type ExistsOptions struct { // External checks for containers created outside of Podman External *bool } + +//go:generate go run ../generator/generator.go CopyOptions +// CopyOptions are options for copying to containers. +type CopyOptions struct { + // If used with CopyFromArchive and set to true it will change ownership of files from the source tar archive + // to the primary uid/gid of the target container. + Chown *bool `schema:"copyUIDGID"` +} diff --git a/pkg/bindings/containers/types_copy_options.go b/pkg/bindings/containers/types_copy_options.go new file mode 100644 index 000000000..12ad085fd --- /dev/null +++ b/pkg/bindings/containers/types_copy_options.go @@ -0,0 +1,37 @@ +package containers + +import ( + "net/url" + + "github.com/containers/podman/v3/pkg/bindings/internal/util" +) + +/* +This file is generated automatically by go generate. Do not edit. +*/ + +// Changed +func (o *CopyOptions) Changed(fieldName string) bool { + return util.Changed(o, fieldName) +} + +// ToParams +func (o *CopyOptions) ToParams() (url.Values, error) { + return util.ToParams(o) +} + +// WithChown +func (o *CopyOptions) WithChown(value bool) *CopyOptions { + v := &value + o.Chown = v + return o +} + +// GetChown +func (o *CopyOptions) GetChown() bool { + var chown bool + if o.Chown == nil { + return chown + } + return *o.Chown +} diff --git a/pkg/bindings/internal/util/util.go b/pkg/bindings/internal/util/util.go index ef93d6e25..bcf6959f2 100644 --- a/pkg/bindings/internal/util/util.go +++ b/pkg/bindings/internal/util/util.go @@ -72,14 +72,18 @@ func ToParams(o interface{}) (url.Values, error) { if reflect.Ptr == f.Kind() { f = f.Elem() } + paramName := fieldName + if pn, ok := sType.Field(i).Tag.Lookup("schema"); ok { + paramName = pn + } switch { case IsSimpleType(f): - params.Set(fieldName, SimpleTypeToParam(f)) + params.Set(paramName, SimpleTypeToParam(f)) case f.Kind() == reflect.Slice: for i := 0; i < f.Len(); i++ { elem := f.Index(i) if IsSimpleType(elem) { - params.Add(fieldName, SimpleTypeToParam(elem)) + params.Add(paramName, SimpleTypeToParam(elem)) } else { return nil, errors.New("slices must contain only simple types") } @@ -95,7 +99,7 @@ func ToParams(o interface{}) (url.Values, error) { return nil, err } - params.Set(fieldName, s) + params.Set(paramName, s) } } return params, nil diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 8ed9b9b61..302b35a47 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -160,6 +160,13 @@ type CommitOptions struct { Writer io.Writer } +type CopyOptions struct { + // If used with ContainerCopyFromArchive and set to true + // it will change ownership of files from the source tar archive + // to the primary uid/gid of the destination container. + Chown bool +} + type CommitReport struct { Id string //nolint } diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index ec17d7ce3..28e5160db 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -20,7 +20,7 @@ type ContainerEngine interface { ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error) ContainerCommit(ctx context.Context, nameOrID string, options CommitOptions) (*CommitReport, error) - ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (ContainerCopyFunc, error) + ContainerCopyFromArchive(ctx context.Context, nameOrID, path string, reader io.Reader, options CopyOptions) (ContainerCopyFunc, error) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (ContainerCopyFunc, error) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error) ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error) diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go index 2ea63aa5e..1a5bb6dc4 100644 --- a/pkg/domain/infra/abi/archive.go +++ b/pkg/domain/infra/abi/archive.go @@ -7,12 +7,12 @@ import ( "github.com/containers/podman/v3/pkg/domain/entities" ) -func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, containerPath string, reader io.Reader) (entities.ContainerCopyFunc, error) { +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID, containerPath string, reader io.Reader, options entities.CopyOptions) (entities.ContainerCopyFunc, error) { container, err := ic.Libpod.LookupContainer(nameOrID) if err != nil { return nil, err } - return container.CopyFromArchive(ctx, containerPath, reader) + return container.CopyFromArchive(ctx, containerPath, options.Chown, reader) } func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, containerPath string, writer io.Writer) (entities.ContainerCopyFunc, error) { diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 1bee521cf..56315f46f 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -843,8 +843,8 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o return reports, nil } -func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) { - return containers.CopyFromArchive(ic.ClientCtx, nameOrID, path, reader) +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID, path string, reader io.Reader, options entities.CopyOptions) (entities.ContainerCopyFunc, error) { + return containers.CopyFromArchiveWithOptions(ic.ClientCtx, nameOrID, path, reader, new(containers.CopyOptions).WithChown(options.Chown)) } func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) { diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 22fb78a5c..42ae23c43 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -15,9 +15,8 @@ import ( "strings" "time" - "github.com/containers/podman/v3/pkg/rootless" - "github.com/containers/podman/v3/pkg/machine" + "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/utils" "github.com/containers/storage/pkg/homedir" "github.com/digitalocean/go-qemu/qmp" @@ -248,7 +247,23 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { if err := v.startHostNetworking(); err != nil { return errors.Errorf("unable to start host networking: %q", err) } + + rtPath, err := getRuntimeDir() + if err != nil { + return err + } + + // If the temporary podman dir is not created, create it + podmanTempDir := filepath.Join(rtPath, "podman") + if _, err := os.Stat(podmanTempDir); os.IsNotExist(err) { + if mkdirErr := os.MkdirAll(podmanTempDir, 0755); mkdirErr != nil { + return err + } + } qemuSocketPath, _, err := v.getSocketandPid() + if err != nil { + return err + } for i := 0; i < 6; i++ { qemuSocketConn, err = net.Dial("unix", qemuSocketPath) @@ -258,9 +273,6 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { time.Sleep(wait) wait++ } - if err != nil { - return err - } fd, err := qemuSocketConn.(*net.UnixConn).File() if err != nil { diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 6eff25eb9..f76eab0e3 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -268,7 +268,9 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo } if retErr != nil && pid > 0 { if err := unix.Kill(pid, unix.SIGKILL); err != nil { - logrus.Errorf("failed to kill %d", pid) + if err != unix.ESRCH { + logrus.Errorf("failed to cleanup process %d: %v", pid, err) + } } C.reexec_in_user_namespace_wait(C.int(pid), 0) } @@ -394,7 +396,9 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo } if err := unix.Kill(int(pidC), s.(unix.Signal)); err != nil { - logrus.Errorf("failed to kill %d", int(pidC)) + if err != unix.ESRCH { + logrus.Errorf("failed to propagate signal to child process %d: %v", int(pidC), err) + } } } }() diff --git a/pkg/servicereaper/service.go b/pkg/servicereaper/service.go new file mode 100644 index 000000000..e9c4fe908 --- /dev/null +++ b/pkg/servicereaper/service.go @@ -0,0 +1,64 @@ +//+build linux + +package servicereaper + +import ( + "os" + "os/signal" + "sync" + "syscall" + + "github.com/sirupsen/logrus" +) + +type service struct { + pidMap map[int]bool + mutex *sync.Mutex +} + +var s = service{ + pidMap: map[int]bool{}, + mutex: &sync.Mutex{}, +} + +func AddPID(pid int) { + s.mutex.Lock() + s.pidMap[pid] = true + s.mutex.Unlock() +} + +func Start() { + // create signal channel and only wait for SIGCHLD + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, syscall.SIGCHLD) + // wait and reap in an extra goroutine + go reaper(sigc) +} + +func reaper(sigc chan os.Signal) { + for { + // block until we receive SIGCHLD + <-sigc + s.mutex.Lock() + for pid := range s.pidMap { + var status syscall.WaitStatus + waitpid, err := syscall.Wait4(pid, &status, syscall.WNOHANG, nil) + if err != nil { + // do not log error for ECHILD + if err != syscall.ECHILD { + logrus.Warnf("wait for pid %d failed: %v ", pid, err) + } + delete(s.pidMap, pid) + continue + } + // if pid == 0 nothing happened + if waitpid == 0 { + continue + } + if status.Exited() { + delete(s.pidMap, pid) + } + } + s.mutex.Unlock() + } +} |