diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/adapter/containers.go | 56 | ||||
-rw-r--r-- | pkg/adapter/containers_remote.go | 33 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.c | 84 | ||||
-rw-r--r-- | pkg/spec/storage.go | 2 | ||||
-rw-r--r-- | pkg/varlinkapi/attach.go | 5 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 67 | ||||
-rw-r--r-- | pkg/varlinkapi/transfers.go | 11 | ||||
-rw-r--r-- | pkg/varlinkapi/virtwriter/virtwriter.go | 106 |
8 files changed, 262 insertions, 102 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index ff7b6377a..34ee70d3d 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -6,6 +6,7 @@ import ( "bufio" "context" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -15,9 +16,12 @@ import ( "syscall" "time" + "github.com/containers/buildah" + "github.com/containers/image/manifest" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter/shortcuts" "github.com/containers/libpod/pkg/systemdgen" "github.com/containers/psgo" @@ -1030,3 +1034,55 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace { return shared.GetNamespaces(container.Pid) } + +// Commit creates a local image from a container +func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, container, imageName string) (string, error) { + var ( + writer io.Writer + mimeType string + ) + switch c.Format { + case "oci": + mimeType = buildah.OCIv1ImageManifest + if c.Flag("message").Changed { + return "", errors.Errorf("messages are only compatible with the docker image format (-f docker)") + } + case "docker": + mimeType = manifest.DockerV2Schema2MediaType + default: + return "", errors.Errorf("unrecognized image format %q", c.Format) + } + if !c.Quiet { + writer = os.Stderr + } + ctr, err := r.Runtime.LookupContainer(container) + if err != nil { + return "", errors.Wrapf(err, "error looking up container %q", container) + } + + rtc, err := r.Runtime.GetConfig() + if err != nil { + return "", err + } + + sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false) + coptions := buildah.CommitOptions{ + SignaturePolicyPath: rtc.SignaturePolicyPath, + ReportWriter: writer, + SystemContext: sc, + PreferredManifestType: mimeType, + } + options := libpod.ContainerCommitOptions{ + CommitOptions: coptions, + Pause: c.Pause, + IncludeVolumes: c.IncludeVolumes, + Message: c.Message, + Changes: c.Change, + Author: c.Author, + } + newImage, err := ctr.Commit(ctx, imageName, options) + if err != nil { + return "", err + } + return newImage.ID(), nil +} diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index c34495b3d..bc6a9cfcd 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -583,7 +583,15 @@ func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid s } // TODO add detach keys support - _, err = iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid, detachKeys, start) + reply, err := iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid, detachKeys, start) + if err != nil { + restoreTerminal(oldTermState) + return nil, err + } + + // See if the server accepts the upgraded connection or returns an error + _, err = reply() + if err != nil { restoreTerminal(oldTermState) return nil, err @@ -986,3 +994,26 @@ func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared } return &ns } + +// Commit creates a local image from a container +func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, container, imageName string) (string, error) { + var iid string + reply, err := iopodman.Commit().Send(r.Conn, varlink.More, container, imageName, c.Change, c.Author, c.Message, c.Pause, c.Format) + if err != nil { + return "", err + } + for { + responses, flags, err := reply() + if err != nil { + return "", err + } + for _, line := range responses.Logs { + fmt.Fprintln(os.Stderr, line) + } + iid = responses.Id + if flags&varlink.Continues == 0 { + break + } + } + return iid, nil +} diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 2356882e7..eb62d55e9 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -34,6 +34,15 @@ int renameat2 (int olddirfd, const char *oldpath, int newdirfd, const char *newp } #endif +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(expression) \ + (__extension__ \ + ({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif + static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces"; static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone"; @@ -113,9 +122,7 @@ get_cmd_line_args (pid_t pid) return NULL; for (;;) { - do - ret = read (fd, buffer + used, allocated - used); - while (ret < 0 && errno == EINTR); + ret = TEMP_FAILURE_RETRY (read (fd, buffer + used, allocated - used)); if (ret < 0) { free (buffer); @@ -180,7 +187,7 @@ can_use_shortcut () argv = get_cmd_line_args (0); if (argv == NULL) - return NULL; + return false; for (argc = 0; argv[argc]; argc++) { @@ -269,13 +276,15 @@ static void __attribute__((constructor)) init() return; } - r = read (fd, buf, sizeof (buf)); + r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof (buf))); close (fd); if (r < 0) { free (cwd); return; } + buf[r] = '\0'; + pid = strtol (buf, NULL, 10); if (pid == LONG_MAX) { @@ -352,10 +361,7 @@ reexec_in_user_namespace_wait (int pid, int options) pid_t p; int status; - do - p = waitpid (pid, &status, 0); - while (p < 0 && errno == EINTR); - + p = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)); if (p < 0) return -1; @@ -384,12 +390,10 @@ create_pause_process (const char *pause_pid_file_path, char **argv) close (p[1]); /* Block until we write the pid file. */ - do - r = read (p[0], &b, 1); - while (r < 0 && errno == EINTR); + r = TEMP_FAILURE_RETRY (read (p[0], &b, 1)); close (p[0]); - reexec_in_user_namespace_wait(r, 0); + reexec_in_user_namespace_wait (r, 0); return r == 1 && b == '0' ? 0 : -1; } @@ -426,9 +430,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv) _exit (EXIT_FAILURE); } - do - r = write (fd, pid_str, strlen (pid_str)); - while (r < 0 && errno == EINTR); + r = TEMP_FAILURE_RETRY (write (fd, pid_str, strlen (pid_str))); if (r < 0) { kill (pid, SIGKILL); @@ -445,9 +447,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv) _exit (EXIT_FAILURE); } - do - r = write (p[1], "0", 1); - while (r < 0 && errno == EINTR); + r = TEMP_FAILURE_RETRY (write (p[1], "0", 1)); close (p[1]); _exit (EXIT_SUCCESS); @@ -489,6 +489,7 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path) char **argv; int pid; char *cwd = getcwd (NULL, 0); + sigset_t sigset, oldsigset; if (cwd == NULL) { @@ -522,6 +523,22 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path) return pid; } + if (sigfillset (&sigset) < 0) + { + fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } + if (sigdelset (&sigset, SIGCHLD) < 0) + { + fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } + if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0) + { + fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } + setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1); setenv ("_CONTAINERS_ROOTLESS_UID", uid, 1); setenv ("_CONTAINERS_ROOTLESS_GID", gid, 1); @@ -570,6 +587,11 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path) /* We ignore errors here as we didn't create the namespace anyway. */ create_pause_process (pause_pid_file_path, argv); } + if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0) + { + fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } execvp (argv[0], argv); @@ -609,9 +631,7 @@ copy_file_to_fd (const char *file_to_read, int outfd) { ssize_t r, w, t = 0; - do - r = read (fd, buf, sizeof buf); - while (r < 0 && errno == EINTR); + r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof buf)); if (r < 0) { close (fd); @@ -623,9 +643,7 @@ copy_file_to_fd (const char *file_to_read, int outfd) while (t < r) { - do - w = write (outfd, &buf[t], r - t); - while (w < 0 && errno == EINTR); + w = TEMP_FAILURE_RETRY (write (outfd, &buf[t], r - t)); if (w < 0) { close (fd); @@ -734,9 +752,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re setenv ("_CONTAINERS_ROOTLESS_UID", uid, 1); setenv ("_CONTAINERS_ROOTLESS_GID", gid, 1); - do - ret = read (ready, &b, 1) < 0; - while (ret < 0 && errno == EINTR); + ret = TEMP_FAILURE_RETRY (read (ready, &b, 1)); if (ret < 0) { fprintf (stderr, "cannot read from sync pipe: %s\n", strerror (errno)); @@ -748,21 +764,21 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re if (syscall_setresgid (0, 0, 0) < 0) { fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); - write (ready, "1", 1); + TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } if (syscall_setresuid (0, 0, 0) < 0) { fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); - write (ready, "1", 1); + TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } if (chdir (cwd) < 0) { fprintf (stderr, "cannot chdir: %s\n", strerror (errno)); - write (ready, "1", 1); + TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } free (cwd); @@ -771,14 +787,12 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re { if (create_pause_process (pause_pid_file_path, argv) < 0) { - write (ready, "2", 1); + TEMP_FAILURE_RETRY (write (ready, "2", 1)); _exit (EXIT_FAILURE); } } - do - ret = write (ready, "0", 1) < 0; - while (ret < 0 && errno == EINTR); + ret = TEMP_FAILURE_RETRY (write (ready, "0", 1)); close (ready); if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0) diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go index dcc149b55..e221b5cb5 100644 --- a/pkg/spec/storage.go +++ b/pkg/spec/storage.go @@ -797,7 +797,7 @@ func initFSMounts(inputMounts []spec.Mount) []spec.Mount { if m.Type == TypeBind { m.Options = util.ProcessOptions(m.Options) } - if m.Type == TypeTmpfs { + if m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev" { m.Options = append(m.Options, "tmpcopyup") } mounts = append(mounts, m) diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 2234899a5..8051f07be 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -60,7 +60,10 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st if !start && state != libpod.ContainerStateRunning { return call.ReplyErrorOccurred("container must be running to attach") } - call.Reply(nil) + + // ACK the client upgrade request + call.ReplyAttach() + reader, writer, _, pw, streams := setupStreams(call) go func() { diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index fa1a0a109..1abc4f086 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -371,7 +371,6 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr done = true default: if !call.WantsMore() { - time.Sleep(1 * time.Second) break } br := iopodman.MoreResponse{ @@ -495,6 +494,9 @@ func (i *LibpodAPI) DeleteUnusedImages(call iopodman.VarlinkCall) error { // Commit ... func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, changes []string, author, message string, pause bool, manifestType string) error { + var newImage *image.Image + + output := bytes.NewBuffer([]byte{}) ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -515,7 +517,7 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch } coptions := buildah.CommitOptions{ SignaturePolicyPath: rtc.SignaturePolicyPath, - ReportWriter: nil, + ReportWriter: output, SystemContext: sc, PreferredManifestType: mimeType, } @@ -527,11 +529,61 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch Author: author, } - newImage, err := ctr.Commit(getContext(), imageName, options) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) + if call.WantsMore() { + call.Continues = true } - return call.ReplyCommit(newImage.ID()) + + c := make(chan error) + + go func() { + newImage, err = ctr.Commit(getContext(), imageName, options) + if err != nil { + c <- err + } + c <- nil + close(c) + }() + + var log []string + done := false + for { + line, err := output.ReadString('\n') + if err == nil { + log = append(log, line) + continue + } else if err == io.EOF { + select { + case err := <-c: + if err != nil { + logrus.Errorf("reading of output during commit failed for %s", name) + return call.ReplyErrorOccurred(err.Error()) + } + done = true + default: + if !call.WantsMore() { + break + } + br := iopodman.MoreResponse{ + Logs: log, + } + call.ReplyCommit(br) + log = []string{} + } + } else { + return call.ReplyErrorOccurred(err.Error()) + } + if done { + break + } + } + call.Continues = false + + br := iopodman.MoreResponse{ + Logs: log, + Id: newImage.ID(), + } + + return call.ReplyCommit(br) } // ImportImage imports an image from a tarball to the image store @@ -633,7 +685,6 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error { done = true default: if !call.WantsMore() { - time.Sleep(1 * time.Second) break } br := iopodman.MoreResponse{ @@ -764,7 +815,6 @@ func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageS done = true default: if !call.WantsMore() { - time.Sleep(1 * time.Second) break } br := iopodman.MoreResponse{ @@ -844,7 +894,6 @@ func (i *LibpodAPI) LoadImage(call iopodman.VarlinkCall, name, inputFile string, done = true default: if !call.WantsMore() { - time.Sleep(1 * time.Second) break } br := iopodman.MoreResponse{ diff --git a/pkg/varlinkapi/transfers.go b/pkg/varlinkapi/transfers.go index 96f76bcdc..24a91a86f 100644 --- a/pkg/varlinkapi/transfers.go +++ b/pkg/varlinkapi/transfers.go @@ -29,6 +29,12 @@ func (i *LibpodAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int return call.ReplyErrorOccurred(err.Error()) } + // FIXME return parameter + if err = call.ReplySendFile("FIXME_file_handle"); err != nil { + // If an error occurs while sending the reply, return the error + return err + } + writer := bufio.NewWriter(outputFile) defer writer.Flush() @@ -60,9 +66,10 @@ func (i *LibpodAPI) ReceiveFile(call iopodman.VarlinkCall, filepath string, dele } // Send the file length down to client - // Varlink connection upraded + // Varlink connection upgraded if err = call.ReplyReceiveFile(fileInfo.Size()); err != nil { - return call.ReplyErrorOccurred(err.Error()) + // If an error occurs while sending the reply, return the error + return err } reader := bufio.NewReader(fs) diff --git a/pkg/varlinkapi/virtwriter/virtwriter.go b/pkg/varlinkapi/virtwriter/virtwriter.go index 3adaf6e17..e747984c7 100644 --- a/pkg/varlinkapi/virtwriter/virtwriter.go +++ b/pkg/varlinkapi/virtwriter/virtwriter.go @@ -91,65 +91,65 @@ func (v VirtWriteCloser) Write(input []byte) (int, error) { // Reader decodes the content that comes over the wire and directs it to the proper destination. func Reader(r *bufio.Reader, output, errput *os.File, input *io.PipeWriter, resize chan remotecommand.TerminalSize) error { - var saveb []byte - var eom int + var messageSize int64 + headerBytes := make([]byte, 8) + for { - readb := make([]byte, 32*1024) - n, err := r.Read(readb) - // TODO, later may be worth checking in len of the read is 0 + n, err := io.ReadFull(r, headerBytes) if err != nil { return err } - b := append(saveb, readb[0:n]...) - // no sense in reading less than the header len - for len(b) > 7 { - eom = int(binary.BigEndian.Uint32(b[4:8])) + 8 - // The message and header are togther - if len(b) >= eom { - out := append([]byte{}, b[8:eom]...) - - switch IntToSocketDest(int(b[0])) { - case ToStdout: - n, err := output.Write(out) - if err != nil { - return err - } - if n < len(out) { - return errors.New("short write error occurred on stdout") - } - case ToStderr: - n, err := errput.Write(out) - if err != nil { - return err - } - if n < len(out) { - return errors.New("short write error occurred on stderr") - } - case ToStdin: - n, err := input.Write(out) - if err != nil { - return err - } - if n < len(out) { - return errors.New("short write error occurred on stdin") - } - case TerminalResize: - // Resize events come over in bytes, need to be reserialized - resizeEvent := remotecommand.TerminalSize{} - if err := json.Unmarshal(out, &resizeEvent); err != nil { - return err - } - resize <- resizeEvent - case Quit: - return nil + if n < 8 { + return errors.New("short read and no full header read") + } + + messageSize = int64(binary.BigEndian.Uint32(headerBytes[4:8])) + + switch IntToSocketDest(int(headerBytes[0])) { + case ToStdout: + _, err := io.CopyN(output, r, messageSize) + if err != nil { + return err + } + case ToStderr: + _, err := io.CopyN(errput, r, messageSize) + if err != nil { + return err + } + case ToStdin: + _, err := io.CopyN(input, r, messageSize) + if err != nil { + return err + } + case TerminalResize: + out := make([]byte, messageSize) + if messageSize > 0 { + _, err = io.ReadFull(r, out) + + if err != nil { + return err } - b = b[eom:] - } else { - // We do not have the header and full message, need to slurp again - saveb = b - break } + // Resize events come over in bytes, need to be reserialized + resizeEvent := remotecommand.TerminalSize{} + if err := json.Unmarshal(out, &resizeEvent); err != nil { + return err + } + resize <- resizeEvent + case Quit: + out := make([]byte, messageSize) + if messageSize > 0 { + _, err = io.ReadFull(r, out) + + if err != nil { + return err + } + } + return nil + + default: + // Something really went wrong + return errors.New("Unknown multiplex destination") } } - return nil } |