From c344fe61c11beaf687da284f71bde2311b91371d Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 22 Nov 2017 07:56:46 -0500 Subject: Update vendoring Update version of docker to pull in lates code Remove kubernetes since libpod is not tied to it. Remove a few other packages that we don't seem to use. Left in the networking stuff, since we will hopefully be wiring that together. Signed-off-by: Daniel J Walsh Closes: #60 Approved by: umohnani8 --- .../github.com/kubernetes-incubator/cri-o/LICENSE | 201 +++ .../kubernetes-incubator/cri-o/README.md | 263 ++++ .../kubernetes-incubator/cri-o/conmon/cmsg.c | 149 ++ .../kubernetes-incubator/cri-o/conmon/cmsg.h | 38 + .../kubernetes-incubator/cri-o/conmon/conmon.c | 1474 ++++++++++++++++++++ .../kubernetes-incubator/cri-o/vendor.conf | 113 ++ 6 files changed, 2238 insertions(+) create mode 100644 vendor/github.com/kubernetes-incubator/cri-o/LICENSE create mode 100644 vendor/github.com/kubernetes-incubator/cri-o/README.md create mode 100644 vendor/github.com/kubernetes-incubator/cri-o/conmon/cmsg.c create mode 100644 vendor/github.com/kubernetes-incubator/cri-o/conmon/cmsg.h create mode 100644 vendor/github.com/kubernetes-incubator/cri-o/conmon/conmon.c create mode 100644 vendor/github.com/kubernetes-incubator/cri-o/vendor.conf (limited to 'vendor/github.com/kubernetes-incubator/cri-o') diff --git a/vendor/github.com/kubernetes-incubator/cri-o/LICENSE b/vendor/github.com/kubernetes-incubator/cri-o/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/vendor/github.com/kubernetes-incubator/cri-o/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/kubernetes-incubator/cri-o/README.md b/vendor/github.com/kubernetes-incubator/cri-o/README.md new file mode 100644 index 000000000..dd2881142 --- /dev/null +++ b/vendor/github.com/kubernetes-incubator/cri-o/README.md @@ -0,0 +1,263 @@ +![CRI-O logo](https://cdn.rawgit.com/kubernetes-incubator/cri-o/master/logo/crio-logo.svg) +# CRI-O - OCI-based implementation of Kubernetes Container Runtime Interface + +[![Build Status](https://img.shields.io/travis/kubernetes-incubator/cri-o.svg?maxAge=2592000&style=flat-square)](https://travis-ci.org/kubernetes-incubator/cri-o) +[![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-incubator/cri-o?style=flat-square)](https://goreportcard.com/report/github.com/kubernetes-incubator/cri-o) + +### Status: Stable + +## What is the scope of this project? + +CRI-O is meant to provide an integration path between OCI conformant runtimes and the kubelet. +Specifically, it implements the Kubelet [Container Runtime Interface (CRI)](https://github.com/kubernetes/community/blob/master/contributors/devel/container-runtime-interface.md) using OCI conformant runtimes. +The scope of CRI-O is tied to the scope of the CRI. + +At a high level, we expect the scope of CRI-O to be restricted to the following functionalities: + +* Support multiple image formats including the existing Docker image format +* Support for multiple means to download images including trust & image verification +* Container image management (managing image layers, overlay filesystems, etc) +* Container process lifecycle management +* Monitoring and logging required to satisfy the CRI +* Resource isolation as required by the CRI + +## What is not in scope for this project? + +* Building, signing and pushing images to various image storages +* A CLI utility for interacting with CRI-O. Any CLIs built as part of this project are only meant for testing this project and there will be no guarantees on the backward compatibility with it. + +This is an implementation of the Kubernetes Container Runtime Interface (CRI) that will allow Kubernetes to directly launch and manage Open Container Initiative (OCI) containers. + +The plan is to use OCI projects and best of breed libraries for different aspects: +- Runtime: [runc](https://github.com/opencontainers/runc) (or any OCI runtime-spec implementation) and [oci runtime tools](https://github.com/opencontainers/runtime-tools) +- Images: Image management using [containers/image](https://github.com/containers/image) +- Storage: Storage and management of image layers using [containers/storage](https://github.com/containers/storage) +- Networking: Networking support through use of [CNI](https://github.com/containernetworking/cni) + +It is currently in active development in the Kubernetes community through the [design proposal](https://github.com/kubernetes/kubernetes/pull/26788). Questions and issues should be raised in the Kubernetes [sig-node Slack channel](https://kubernetes.slack.com/archives/sig-node). + +## Commands +| Command | Description | Demo| +| ---------------------------------------------------- | --------------------------------------------------------------------------|-----| +| [crio(8)](/docs/crio.8.md) | OCI Kubernetes Container Runtime daemon || +| [kpod(1)](/docs/kpod.1.md) | Simple management tool for pods and images || +| [kpod-attach(1)](/docs/kpod-attach.1.md) | Instead of providing a `kpod attach` command, the man page `kpod-attach` describes how to use the `kpod logs` and `kpod exec` commands to achieve the same goals as `kpod attach`.|| +| [kpod-cp(1)](/docs/kpod-cp.1.md) | Instead of providing a `kpod cp` command, the man page `kpod-cp` describes how to use the `kpod mount` command to have even more flexibility and functionality.|| +| [kpod-create(1)](/docs/kpod-create.1.md) | Create a new container || +| [kpod-diff(1)](/docs/kpod-diff.1.md) | Inspect changes on a container or image's filesystem || +| [kpod-export(1)](/docs/kpod-export.1.md) | Export container's filesystem contents as a tar archive |[![...](/docs/play.png)](https://asciinema.org/a/913lBIRAg5hK8asyIhhkQVLtV)| +| [kpod-history(1)](/docs/kpod-history.1.md) | Shows the history of an image |[![...](/docs/play.png)](https://asciinema.org/a/bCvUQJ6DkxInMELZdc5DinNSx)| +| [kpod-images(1)](/docs/kpod-images.1.md) | List images in local storage |[![...](/docs/play.png)](https://asciinema.org/a/133649)| +| [kpod-info(1)](/docs/kpod-info.1.md) | Display system information || +| [kpod-inspect(1)](/docs/kpod-inspect.1.md) | Display the configuration of a container or image |[![...](/docs/play.png)](https://asciinema.org/a/133418)| +| [kpod-kill(1)](/docs/kpod-kill.1.md) | Kill the main process in one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/3jNos0A5yzO4hChu7ddKkUPw7)| +| [kpod-load(1)](/docs/kpod-load.1.md) | Load an image from docker archive or oci |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)| +| [kpod-login(1)](/docs/kpod-login.1.md) | Login to a container registry |[![...](/docs/play.png)](https://asciinema.org/a/oNiPgmfo1FjV2YdesiLpvihtV)| +| [kpod-logout(1)](/docs/kpod-logout.1.md) | Logout of a container registry |[![...](/docs/play.png)](https://asciinema.org/a/oNiPgmfo1FjV2YdesiLpvihtV)| +| [kpod-logs(1)](/docs/kpod-logs.1.md) | Display the logs of a container || +| [kpod-mount(1)](/docs/kpod-mount.1.md) | Mount a working container's root filesystem || +| [kpod-pause(1)](/docs/kpod-pause.1.md) | Pause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)| +| [kpod-ps(1)](/docs/kpod-ps.1.md) | Prints out information about containers |[![...](/docs/play.png)](https://asciinema.org/a/bbT41kac6CwZ5giESmZLIaTLR)| +| [kpod-pull(1)](/docs/kpod-pull.1.md) | Pull an image from a registry |[![...](/docs/play.png)](https://asciinema.org/a/lr4zfoynHJOUNu1KaXa1dwG2X)| +| [kpod-push(1)](/docs/kpod-push.1.md) | Push an image to a specified destination |[![...](/docs/play.png)](https://asciinema.org/a/133276)| +| [kpod-rename(1)](/docs/kpod-rename.1.md) | Rename a container || +| [kpod-rm(1)](/docs/kpod-rm.1.md) | Removes one or more containers |[![...](/docs/play.png)](https://asciinema.org/a/7EMk22WrfGtKWmgHJX9Nze1Qp)| +| [kpod-rmi(1)](/docs/kpod-rmi.1.md) | Removes one or more images |[![...](/docs/play.png)](https://asciinema.org/a/133799)| +| [kpod-run(1)](/docs/kpod-run.1.md) | Run a command in a new container || +| [kpod-save(1)](/docs/kpod-save.1.md) | Saves an image to an archive |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)| +| [kpod-stats(1)](/docs/kpod-stats.1.md) | Display a live stream of one or more containers' resource usage statistics|| +| [kpod-stop(1)](/docs/kpod-stop.1.md) | Stops one or more running containers || +| [kpod-tag(1)](/docs/kpod-tag.1.md) | Add an additional name to a local image |[![...](/docs/play.png)](https://asciinema.org/a/133803)| +| [kpod-umount(1)](/docs/kpod-umount.1.md) | Unmount a working container's root filesystem || +| [kpod-unpause(1)](/docs/kpod-unpause.1.md) | Unpause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)| +| [kpod-version(1)](/docs/kpod-version.1.md) | Display the version information |[![...](/docs/play.png)](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)| +| [kpod-wait(1)](/docs/kpod-wait.1.md) | Wait on one or more containers to stop and print their exit codes|| + +## Configuration +| File | Description | +| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| [crio.conf(5)](/docs/crio.conf.5.md) | CRI-O Configuation file | + +## OCI Hooks Support + +[CRI-O configures OCI Hooks to run when launching a container](./hooks.md) + +## CRI-O Usage Transfer + +[Useful information for ops and dev transfer as it relates to infrastructure that utilizes CRI-O](/transfer.md) + +## Communication + +For async communication and long running discussions please use issues and pull requests on the github repo. This will be the best place to discuss design and implementation. + +For sync communication we have an IRC channel #CRI-O, on chat.freenode.net, that everyone is welcome to join and chat about development. + +## Getting started + +### Prerequisites + +Latest version of `runc` is expected to be installed on the system. It is picked up as the default runtime by CRI-O. + +### Build and Run Dependencies + +**Required** + +Fedora, CentOS, RHEL, and related distributions: + +```bash +yum install -y \ + btrfs-progs-devel \ + device-mapper-devel \ + git \ + glib2-devel \ + glibc-devel \ + glibc-static \ + go \ + golang-github-cpuguy83-go-md2man \ + gpgme-devel \ + libassuan-devel \ + libgpg-error-devel \ + libseccomp-devel \ + libselinux-devel \ + ostree-devel \ + pkgconfig \ + runc \ + skopeo-containers +``` + +Debian, Ubuntu, and related distributions: + +```bash +apt-get install -y \ + btrfs-tools \ + git \ + golang-go \ + libassuan-dev \ + libdevmapper-dev \ + libglib2.0-dev \ + libc6-dev \ + libgpgme11-dev \ + libgpg-error-dev \ + libseccomp-dev \ + libselinux1-dev \ + pkg-config \ + go-md2man \ + runc \ + skopeo-containers +``` + +Debian, Ubuntu, and related distributions will also need a copy of the development libraries for `ostree`, either in the form of the `libostree-dev` package from the [flatpak](https://launchpad.net/~alexlarsson/+archive/ubuntu/flatpak) PPA, or built [from source](https://github.com/ostreedev/ostree) (more on that [here](https://ostree.readthedocs.io/en/latest/#building)). + +If using an older release or a long-term support release, be careful to double-check that the version of `runc` is new enough (running `runc --version` should produce `spec: 1.0.0`), or else build your own. + +**NOTE** + +Be careful to double-check that the version of golang is new enough, version 1.8.x or higher is required. If needed, golang kits are avaliable at https://golang.org/dl/ + +**Optional** + +Fedora, CentOS, RHEL, and related distributions: + +(no optional packages) + +Debian, Ubuntu, and related distributions: + +```bash +apt-get install -y \ + libapparmor-dev +``` + +### Get Source Code + +As with other Go projects, CRI-O must be cloned into a directory structure like: + +``` +GOPATH +└── src + └── github.com + └── kubernetes-incubator + └── cri-o +``` + +First, configure a `GOPATH` (if you are using go1.8 or later, this defaults to `~/go`). + +```bash +export GOPATH=~/go +mkdir -p $GOPATH +``` + +Next, clone the source code using: + +```bash +mkdir -p $GOPATH/src/github.com/kubernetes-incubator +cd $_ # or cd $GOPATH/src/github.com/kubernetes-incubator +git clone https://github.com/kubernetes-incubator/cri-o # or your fork +cd cri-o +``` + +### Build + +```bash +make install.tools +make +sudo make install +``` + +Otherwise, if you do not want to build `CRI-O` with seccomp support you can add `BUILDTAGS=""` when running make. + +```bash +make BUILDTAGS="" +sudo make install +``` + +#### Build Tags + +`CRI-O` supports optional build tags for compiling support of various features. +To add build tags to the make option the `BUILDTAGS` variable must be set. + +```bash +make BUILDTAGS='seccomp apparmor' +``` + +| Build Tag | Feature | Dependency | +|-----------|------------------------------------|-------------| +| seccomp | syscall filtering | libseccomp | +| selinux | selinux process and mount labeling | libselinux | +| apparmor | apparmor profile support | libapparmor | + +### Running pods and containers + +Follow this [tutorial](tutorial.md) to get started with CRI-O. + +### Setup CNI networking + +A proper description of setting up CNI networking is given in the +[`contrib/cni` README](contrib/cni/README.md). But the gist is that you need to +have some basic network configurations enabled and CNI plugins installed on +your system. + +### Running with kubernetes + +You can run a local version of kubernetes with CRI-O using `local-up-cluster.sh`: + +1. Clone the [kubernetes repository](https://github.com/kubernetes/kubernetes) +1. Start the CRI-O daemon (`crio`) +1. From the kubernetes project directory, run: +```shell +CGROUP_DRIVER=systemd \ +CONTAINER_RUNTIME=remote \ +CONTAINER_RUNTIME_ENDPOINT='/var/run/crio.sock --runtime-request-timeout=15m' \ +./hack/local-up-cluster.sh +``` + +To run a full cluster, see [the instructions](kubernetes.md). + +### Current Roadmap + +1. Basic pod/container lifecycle, basic image pull (done) +1. Support for tty handling and state management (done) +1. Basic integration with kubelet once client side changes are ready (done) +1. Support for log management, networking integration using CNI, pluggable image/storage management (done) +1. Support for exec/attach (done) +1. Target fully automated kubernetes testing without failures [e2e status](https://github.com/kubernetes-incubator/cri-o/issues/533) +1. Track upstream k8s releases diff --git a/vendor/github.com/kubernetes-incubator/cri-o/conmon/cmsg.c b/vendor/github.com/kubernetes-incubator/cri-o/conmon/cmsg.c new file mode 100644 index 000000000..c44db2ef1 --- /dev/null +++ b/vendor/github.com/kubernetes-incubator/cri-o/conmon/cmsg.c @@ -0,0 +1,149 @@ +/* + * Copyright 2016 SUSE LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTE: This code comes directly from runc/libcontainer/utils/cmsg.c. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cmsg.h" + +#define error(fmt, ...) \ + ({ \ + fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__); \ + errno = ECOMM; \ + goto err; /* return value */ \ + }) + +/* + * Sends a file descriptor along the sockfd provided. Returns the return + * value of sendmsg(2). Any synchronisation and preparation of state + * should be done external to this (we expect the other side to be in + * recvfd() in the code). + */ +ssize_t sendfd(int sockfd, struct file_t file) +{ + struct msghdr msg = {0}; + struct iovec iov[1] = {0}; + struct cmsghdr *cmsg; + int *fdptr; + + union { + char buf[CMSG_SPACE(sizeof(file.fd))]; + struct cmsghdr align; + } u; + + /* + * We need to send some other data along with the ancillary data, + * otherwise the other side won't recieve any data. This is very + * well-hidden in the documentation (and only applies to + * SOCK_STREAM). See the bottom part of unix(7). + */ + iov[0].iov_base = file.name; + iov[0].iov_len = strlen(file.name) + 1; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = u.buf; + msg.msg_controllen = sizeof(u.buf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + + fdptr = (int *) CMSG_DATA(cmsg); + memcpy(fdptr, &file.fd, sizeof(int)); + + return sendmsg(sockfd, &msg, 0); +} + +/* + * Receives a file descriptor from the sockfd provided. Returns the file + * descriptor as sent from sendfd(). It will return the file descriptor + * or die (literally) trying. Any synchronisation and preparation of + * state should be done external to this (we expect the other side to be + * in sendfd() in the code). + */ +struct file_t recvfd(int sockfd) +{ + struct msghdr msg = {0}; + struct iovec iov[1] = {0}; + struct cmsghdr *cmsg; + struct file_t file = {0}; + int *fdptr; + int olderrno; + + union { + char buf[CMSG_SPACE(sizeof(file.fd))]; + struct cmsghdr align; + } u; + + /* Allocate a buffer. */ + /* TODO: Make this dynamic with MSG_PEEK. */ + file.name = malloc(TAG_BUFFER); + if (!file.name) + error("recvfd: failed to allocate file.tag buffer\n"); + + /* + * We need to "recieve" the non-ancillary data even though we don't + * plan to use it at all. Otherwise, things won't work as expected. + * See unix(7) and other well-hidden documentation. + */ + iov[0].iov_base = file.name; + iov[0].iov_len = TAG_BUFFER; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = u.buf; + msg.msg_controllen = sizeof(u.buf); + + ssize_t ret = recvmsg(sockfd, &msg, 0); + if (ret < 0) + goto err; + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg) + error("recvfd: got NULL from CMSG_FIRSTHDR"); + if (cmsg->cmsg_level != SOL_SOCKET) + error("recvfd: expected SOL_SOCKET in cmsg: %d", cmsg->cmsg_level); + if (cmsg->cmsg_type != SCM_RIGHTS) + error("recvfd: expected SCM_RIGHTS in cmsg: %d", cmsg->cmsg_type); + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) + error("recvfd: expected correct CMSG_LEN in cmsg: %lu", cmsg->cmsg_len); + + fdptr = (int *) CMSG_DATA(cmsg); + if (!fdptr || *fdptr < 0) + error("recvfd: recieved invalid pointer"); + + file.fd = *fdptr; + return file; + +err: + olderrno = errno; + free(file.name); + errno = olderrno; + return (struct file_t){0}; +} diff --git a/vendor/github.com/kubernetes-incubator/cri-o/conmon/cmsg.h b/vendor/github.com/kubernetes-incubator/cri-o/conmon/cmsg.h new file mode 100644 index 000000000..7c7aefe6e --- /dev/null +++ b/vendor/github.com/kubernetes-incubator/cri-o/conmon/cmsg.h @@ -0,0 +1,38 @@ +/* + * Copyright 2016 SUSE LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTE: This code comes directly from runc/libcontainer/utils/cmsg.h. */ + +#pragma once + +#if !defined(CMSG_H) +#define CMSG_H + +#include + +/* TODO: Implement this properly with MSG_PEEK. */ +#define TAG_BUFFER 4096 + +/* This mirrors Go's (*os.File). */ +struct file_t { + char *name; + int fd; +}; + +struct file_t recvfd(int sockfd); +ssize_t sendfd(int sockfd, struct file_t file); + +#endif /* !defined(CMSG_H) */ diff --git a/vendor/github.com/kubernetes-incubator/cri-o/conmon/conmon.c b/vendor/github.com/kubernetes-incubator/cri-o/conmon/conmon.c new file mode 100644 index 000000000..66d1bbe08 --- /dev/null +++ b/vendor/github.com/kubernetes-incubator/cri-o/conmon/conmon.c @@ -0,0 +1,1474 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cmsg.h" + +#define pexit(fmt, ...) \ + do { \ + fprintf(stderr, "[conmon:e]: " fmt " %m\n", ##__VA_ARGS__); \ + syslog(LOG_ERR, "conmon : " fmt ": %m\n", ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } while (0) + +#define nexit(fmt, ...) \ + do { \ + fprintf(stderr, "[conmon:e]: " fmt "\n", ##__VA_ARGS__); \ + syslog(LOG_ERR, "conmon : " fmt " \n", ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } while (0) + +#define nwarn(fmt, ...) \ + do { \ + fprintf(stderr, "[conmon:w]: " fmt "\n", ##__VA_ARGS__); \ + syslog(LOG_INFO, "conmon : " fmt " \n", ##__VA_ARGS__); \ + } while (0) + +#define ninfo(fmt, ...) \ + do { \ + fprintf(stderr, "[conmon:i]: " fmt "\n", ##__VA_ARGS__); \ + syslog(LOG_INFO, "conmon : " fmt " \n", ##__VA_ARGS__); \ + } while (0) + +#define _cleanup_(x) __attribute__((cleanup(x))) + +static inline void freep(void *p) +{ + free(*(void **)p); +} + +static inline void closep(int *fd) +{ + if (*fd >= 0) + close(*fd); + *fd = -1; +} + +static inline void fclosep(FILE **fp) { + if (*fp) + fclose(*fp); + *fp = NULL; +} + +static inline void gstring_free_cleanup(GString **string) +{ + if (*string) + g_string_free(*string, TRUE); +} + +static inline void strv_cleanup(char ***strv) +{ + if (strv) + g_strfreev (*strv); +} + +#define _cleanup_free_ _cleanup_(freep) +#define _cleanup_close_ _cleanup_(closep) +#define _cleanup_fclose_ _cleanup_(fclosep) +#define _cleanup_gstring_ _cleanup_(gstring_free_cleanup) +#define _cleanup_strv_ _cleanup_(strv_cleanup) + +#define BUF_SIZE 8192 +#define CMD_SIZE 1024 +#define MAX_EVENTS 10 + +#define DEFAULT_SOCKET_PATH "/var/lib/crio" + +static bool opt_terminal = false; +static bool opt_stdin = false; +static char *opt_cid = NULL; +static char *opt_cuuid = NULL; +static char *opt_runtime_path = NULL; +static char *opt_bundle_path = NULL; +static char *opt_pid_file = NULL; +static bool opt_systemd_cgroup = false; +static bool opt_no_pivot = false; +static char *opt_exec_process_spec = NULL; +static bool opt_exec = false; +static char *opt_log_path = NULL; +static char *opt_exit_dir = NULL; +static int opt_timeout = 0; +static int64_t opt_log_size_max = -1; +static char *opt_socket_path = DEFAULT_SOCKET_PATH; +static GOptionEntry opt_entries[] = +{ + { "terminal", 't', 0, G_OPTION_ARG_NONE, &opt_terminal, "Terminal", NULL }, + { "stdin", 'i', 0, G_OPTION_ARG_NONE, &opt_stdin, "Stdin", NULL }, + { "cid", 'c', 0, G_OPTION_ARG_STRING, &opt_cid, "Container ID", NULL }, + { "cuuid", 'u', 0, G_OPTION_ARG_STRING, &opt_cuuid, "Container UUID", NULL }, + { "runtime", 'r', 0, G_OPTION_ARG_STRING, &opt_runtime_path, "Runtime path", NULL }, + { "no-pivot", 0, 0, G_OPTION_ARG_NONE, &opt_no_pivot, "do not use pivot_root", NULL }, + { "bundle", 'b', 0, G_OPTION_ARG_STRING, &opt_bundle_path, "Bundle path", NULL }, + { "pidfile", 'p', 0, G_OPTION_ARG_STRING, &opt_pid_file, "PID file", NULL }, + { "systemd-cgroup", 's', 0, G_OPTION_ARG_NONE, &opt_systemd_cgroup, "Enable systemd cgroup manager", NULL }, + { "exec", 'e', 0, G_OPTION_ARG_NONE, &opt_exec, "Exec a command in a running container", NULL }, + { "exec-process-spec", 0, 0, G_OPTION_ARG_STRING, &opt_exec_process_spec, "Path to the process spec for exec", NULL }, + { "exit-dir", 0, 0, G_OPTION_ARG_STRING, &opt_exit_dir, "Path to the directory where exit files are written", NULL }, + { "log-path", 'l', 0, G_OPTION_ARG_STRING, &opt_log_path, "Log file path", NULL }, + { "timeout", 'T', 0, G_OPTION_ARG_INT, &opt_timeout, "Timeout in seconds", NULL }, + { "log-size-max", 0, 0, G_OPTION_ARG_INT64, &opt_log_size_max, "Maximum size of log file", NULL }, + { "socket-dir-path", 0, 0, G_OPTION_ARG_STRING, &opt_socket_path, "Location of container attach sockets", NULL }, + { NULL } +}; + +/* strlen("1997-03-25T13:20:42.999999999+01:00 stdout ") + 1 */ +#define TSBUFLEN 44 + +#define CGROUP_ROOT "/sys/fs/cgroup" + +static int log_fd = -1; + +static ssize_t write_all(int fd, const void *buf, size_t count) +{ + size_t remaining = count; + const char *p = buf; + ssize_t res; + + while (remaining > 0) { + do { + res = write(fd, p, remaining); + } while (res == -1 && errno == EINTR); + + if (res <= 0) + return -1; + + remaining -= res; + p += res; + } + + return count; +} + +#define WRITEV_BUFFER_N_IOV 128 + +typedef struct { + int iovcnt; + struct iovec iov[WRITEV_BUFFER_N_IOV]; +} writev_buffer_t; + +static ssize_t writev_buffer_flush (int fd, writev_buffer_t *buf) +{ + size_t count = 0; + ssize_t res; + struct iovec *iov; + int iovcnt; + + iovcnt = buf->iovcnt; + iov = buf->iov; + + while (iovcnt > 0) { + do { + res = writev(fd, iov, iovcnt); + } while (res == -1 && errno == EINTR); + + if (res <= 0) + return -1; + + count += res; + + while (res > 0) { + size_t from_this = MIN((size_t)res, iov->iov_len); + iov->iov_len -= from_this; + res -= from_this; + + if (iov->iov_len == 0) { + iov++; + iovcnt--; + } + } + } + + buf->iovcnt = 0; + + return count; +} + +ssize_t writev_buffer_append_segment(int fd, writev_buffer_t *buf, const void *data, ssize_t len) +{ + if (data == NULL) + return 1; + + if (len < 0) + len = strlen ((char *)data); + + if (buf->iovcnt == WRITEV_BUFFER_N_IOV && + writev_buffer_flush (fd, buf) < 0) + return -1; + + if (len > 0) { + buf->iov[buf->iovcnt].iov_base = (void *)data; + buf->iov[buf->iovcnt].iov_len = (size_t)len; + buf->iovcnt++; + } + + return 1; +} + +int set_k8s_timestamp(char *buf, ssize_t buflen, const char *pipename) +{ + struct tm *tm; + struct timespec ts; + char off_sign = '+'; + int off, len, err = -1; + + if (clock_gettime(CLOCK_REALTIME, &ts) < 0) { + /* If CLOCK_REALTIME is not supported, we set nano seconds to 0 */ + if (errno == EINVAL) { + ts.tv_nsec = 0; + } else { + return err; + } + } + + if ((tm = localtime(&ts.tv_sec)) == NULL) + return err; + + + off = (int) tm->tm_gmtoff; + if (tm->tm_gmtoff < 0) { + off_sign = '-'; + off = -off; + } + + len = snprintf(buf, buflen, "%d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d %s ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, ts.tv_nsec, + off_sign, off / 3600, off % 3600, pipename); + + if (len < buflen) + err = 0; + return err; +} + +/* stdpipe_t represents one of the std pipes (or NONE). + * Sync with const in container_attach.go */ +typedef enum { + NO_PIPE, + STDIN_PIPE, /* unused */ + STDOUT_PIPE, + STDERR_PIPE, +} stdpipe_t; + +const char *stdpipe_name(stdpipe_t pipe) +{ + switch (pipe) { + case STDIN_PIPE: + return "stdin"; + case STDOUT_PIPE: + return "stdout"; + case STDERR_PIPE: + return "stderr"; + default: + return "NONE"; + } +} + +/* + * The CRI requires us to write logs with a (timestamp, stream, line) format + * for every newline-separated line. write_k8s_log writes said format for every + * line in buf, and will partially write the final line of the log if buf is + * not terminated by a newline. + */ +static int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen) +{ + char tsbuf[TSBUFLEN]; + static stdpipe_t trailing_line = NO_PIPE; + writev_buffer_t bufv = {0}; + static int64_t bytes_written = 0; + int64_t bytes_to_be_written = 0; + + /* + * Use the same timestamp for every line of the log in this buffer. + * There is no practical difference in the output since write(2) is + * fast. + */ + if (set_k8s_timestamp(tsbuf, sizeof tsbuf, stdpipe_name(pipe))) + /* TODO: We should handle failures much more cleanly than this. */ + return -1; + + while (buflen > 0) { + const char *line_end = NULL; + ptrdiff_t line_len = 0; + bool insert_newline = FALSE; + bool insert_timestamp = FALSE; + + /* Find the end of the line, or alternatively the end of the buffer. */ + line_end = memchr(buf, '\n', buflen); + if (line_end == NULL) + line_end = &buf[buflen-1]; + line_len = line_end - buf + 1; + + bytes_to_be_written = line_len; + if (trailing_line != pipe) { + /* + * Write the (timestamp, stream) tuple if there isn't any trailing + * output from the previous line (or if there is trailing output but + * the current buffer being printed is from a different pipe). + */ + insert_timestamp = TRUE; + bytes_to_be_written += (TSBUFLEN - 1); + /* + * If there was a trailing line from a different pipe, prepend a + * newline to split it properly. This technically breaks the flow + * of the previous line (adding a newline in the log where there + * wasn't one output) but without modifying the file in a + * non-append-only way there's not much we can do. + */ + if (trailing_line != NO_PIPE) { + insert_newline = TRUE; + bytes_to_be_written += 1; + } + } + + /* + * We re-open the log file if writing out the bytes will exceed the max + * log size. We also reset the state so that the new file is started with + * a timestamp. + */ + if ((opt_log_size_max > 0) && (bytes_written + bytes_to_be_written) > opt_log_size_max) { + ninfo("Creating new log file"); + insert_newline = FALSE; + insert_timestamp = TRUE; + bytes_written = 0; + + /* Close the existing fd */ + close(fd); + + /* Unlink the file */ + if (unlink(opt_log_path) < 0) { + pexit("Failed to unlink log file"); + } + + /* Open the log path file again */ + log_fd = open(opt_log_path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0600); + if (log_fd < 0) + pexit("Failed to open log file"); + fd = log_fd; + } + + /* Output a newline */ + if (insert_newline) { + if (writev_buffer_append_segment(fd, &bufv, "\n", -1) < 0) { + nwarn("failed to write newline to log"); + goto next; + } + } + + /* Output a timestamp */ + if (insert_timestamp) { + if (writev_buffer_append_segment(fd, &bufv, tsbuf, -1) < 0) { + nwarn("failed to write (timestamp, stream) to log"); + goto next; + } + } + + /* Output the actual contents. */ + if (writev_buffer_append_segment(fd, &bufv, buf, line_len) < 0) { + nwarn("failed to write buffer to log"); + goto next; + } + + bytes_written += bytes_to_be_written; + + /* If we did not output a full line, then we are a trailing_line. */ + trailing_line = (*line_end == '\n') ? NO_PIPE : pipe; + +next: + /* Update the head of the buffer remaining to output. */ + buf += line_len; + buflen -= line_len; + } + + if (writev_buffer_flush (fd, &bufv) < 0) { + nwarn("failed to flush buffer to log"); + } + + ninfo("Total bytes written: %"PRId64"", bytes_written); + + return 0; +} + +/* + * Returns the path for specified controller name for a pid. + * Returns NULL on error. + */ +static char *process_cgroup_subsystem_path(int pid, const char *subsystem) { + _cleanup_free_ char *cgroups_file_path = g_strdup_printf("/proc/%d/cgroup", pid); + _cleanup_fclose_ FILE *fp = NULL; + fp = fopen(cgroups_file_path, "re"); + if (fp == NULL) { + nwarn("Failed to open cgroups file: %s", cgroups_file_path); + return NULL; + } + + _cleanup_free_ char *line = NULL; + ssize_t read; + size_t len = 0; + char *ptr, *path; + char *subsystem_path = NULL; + int i; + while ((read = getline(&line, &len, fp)) != -1) { + _cleanup_strv_ char **subsystems = NULL; + ptr = strchr(line, ':'); + if (ptr == NULL) { + nwarn("Error parsing cgroup, ':' not found: %s", line); + return NULL; + } + ptr++; + path = strchr(ptr, ':'); + if (path == NULL) { + nwarn("Error parsing cgroup, second ':' not found: %s", line); + return NULL; + } + *path = 0; + path++; + subsystems = g_strsplit (ptr, ",", -1); + for (i = 0; subsystems[i] != NULL; i++) { + if (strcmp (subsystems[i], subsystem) == 0) { + char *subpath = strchr(subsystems[i], '='); + if (subpath == NULL) { + subpath = ptr; + } else { + *subpath = 0; + } + + subsystem_path = g_strdup_printf("%s/%s%s", CGROUP_ROOT, subpath, path); + subsystem_path[strlen(subsystem_path) - 1] = '\0'; + return subsystem_path; + } + } + } + + return NULL; +} + +static char *escape_json_string(const char *str) +{ + GString *escaped; + const char *p; + + p = str; + escaped = g_string_sized_new(strlen(str)); + + while (*p != 0) { + char c = *p++; + if (c == '\\' || c == '"') { + g_string_append_c(escaped, '\\'); + g_string_append_c(escaped, c); + } else if (c == '\n') { + g_string_append_printf (escaped, "\\n"); + } else if (c == '\t') { + g_string_append_printf (escaped, "\\t"); + } else if ((c > 0 && c < 0x1f) || c == 0x7f) { + g_string_append_printf (escaped, "\\u00%02x", (guint)c); + } else { + g_string_append_c (escaped, c); + } + } + + return g_string_free (escaped, FALSE); +} + +static int get_pipe_fd_from_env(const char *envname) +{ + char *pipe_str, *endptr; + int pipe_fd; + + pipe_str = getenv(envname); + if (pipe_str == NULL) + return -1; + + errno = 0; + pipe_fd = strtol(pipe_str, &endptr, 10); + if (errno != 0 || *endptr != '\0') + pexit("unable to parse %s", envname); + if (fcntl(pipe_fd, F_SETFD, FD_CLOEXEC) == -1) + pexit("unable to make %s CLOEXEC", envname); + + return pipe_fd; +} + +static void add_argv(GPtrArray *argv_array, ...) G_GNUC_NULL_TERMINATED; + +static void add_argv(GPtrArray *argv_array, ...) +{ + va_list args; + char *arg; + + va_start (args, argv_array); + while ((arg = va_arg (args, char *))) + g_ptr_array_add (argv_array, arg); + va_end (args); +} + +static void end_argv(GPtrArray *argv_array) +{ + g_ptr_array_add(argv_array, NULL); +} + +/* Global state */ + +static int runtime_status = -1; +static int container_status = -1; + +static int masterfd_stdin = -1; +static int masterfd_stdout = -1; +static int masterfd_stderr = -1; + +/* Used for attach */ +static int conn_sock = -1; +static int conn_sock_readable; +static int conn_sock_writable; + +static int oom_event_fd = -1; +static int attach_socket_fd = -1; +static int console_socket_fd = -1; +static int terminal_ctrl_fd = -1; + +static bool timed_out = FALSE; + +static GMainLoop *main_loop = NULL; + +static void conn_sock_shutdown(int how) +{ + if (conn_sock == -1) + return; + shutdown(conn_sock, how); + if (how & SHUT_RD) + conn_sock_readable = false; + if (how & SHUT_WR) + conn_sock_writable = false; + if (!conn_sock_writable && !conn_sock_readable) { + close(conn_sock); + conn_sock = -1; + } +} + +static gboolean stdio_cb(int fd, GIOCondition condition, gpointer user_data); + +static gboolean tty_hup_timeout_scheduled = false; + +static gboolean tty_hup_timeout_cb (G_GNUC_UNUSED gpointer user_data) +{ + tty_hup_timeout_scheduled = false; + g_unix_fd_add (masterfd_stdout, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDOUT_PIPE)); + return G_SOURCE_REMOVE; +} + +static bool read_stdio(int fd, stdpipe_t pipe, bool *eof) +{ + #define STDIO_BUF_SIZE 8192 /* Sync with redirectResponseToOutputStreams() */ + /* We use one extra byte at the start, which we don't read into, instead + we use that for marking the pipe when we write to the attached socket */ + char real_buf[STDIO_BUF_SIZE + 1]; + char *buf = real_buf + 1; + ssize_t num_read = 0; + + if (eof) + *eof = false; + + num_read = read(fd, buf, STDIO_BUF_SIZE); + if (num_read == 0) { + if (eof) + *eof = true; + return false; + } else if (num_read < 0) { + nwarn("stdio_input read failed %s", strerror(errno)); + return false; + } else { + if (write_k8s_log(log_fd, pipe, buf, num_read) < 0) { + nwarn("write_k8s_log failed"); + return G_SOURCE_CONTINUE; + } + + real_buf[0] = pipe; + if (conn_sock_writable && write_all(conn_sock, real_buf, num_read+1) < 0) { + nwarn("Failed to write to socket"); + conn_sock_shutdown(SHUT_WR); + } + return true; + } +} + +static void on_sigchld(G_GNUC_UNUSED int signal) +{ + raise (SIGUSR1); +} + +static void check_child_processes(GHashTable *pid_to_handler) +{ + void (*cb) (GPid, int, gpointer); + + for (;;) { + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + + if (pid < 0 && errno == EINTR) + continue; + if (pid < 0 && errno == ECHILD) { + g_main_loop_quit (main_loop); + return; + } + if (pid < 0) + pexit("Failed to read child process status"); + + if (pid == 0) + return; + + /* If we got here, pid > 0, so we have a valid pid to check. */ + cb = g_hash_table_lookup(pid_to_handler, &pid); + if (cb) + cb(pid, status, 0); + } +} + +static gboolean on_sigusr1_cb(gpointer user_data) +{ + GHashTable *pid_to_handler = (GHashTable *) user_data; + check_child_processes (pid_to_handler); + return G_SOURCE_CONTINUE; +} + +static gboolean stdio_cb(int fd, GIOCondition condition, gpointer user_data) +{ + stdpipe_t pipe = GPOINTER_TO_INT(user_data); + bool read_eof = false; + bool has_input = (condition & G_IO_IN) != 0; + bool has_hup = (condition & G_IO_HUP) != 0; + + /* When we get here, condition can be G_IO_IN and/or G_IO_HUP. + IN means there is some data to read. + HUP means the other side closed the fd. In the case of a pine + this in final, and we will never get more data. However, in the + terminal case this just means that nobody has the terminal + open at this point, and this can be change whenever someone + opens the tty */ + + /* Read any data before handling hup */ + if (has_input) { + read_stdio(fd, pipe, &read_eof); + } + + if (has_hup && opt_terminal && pipe == STDOUT_PIPE) { + /* We got a HUP from the terminal master this means there + are no open slaves ptys atm, and we will get a lot + of wakeups until we have one, switch to polling + mode. */ + + /* If we read some data this cycle, wait one more, maybe there + is more in the buffer before we handle the hup */ + if (has_input && !read_eof) { + return G_SOURCE_CONTINUE; + } + + if (!tty_hup_timeout_scheduled) { + g_timeout_add (100, tty_hup_timeout_cb, NULL); + } + tty_hup_timeout_scheduled = true; + return G_SOURCE_REMOVE; + } + + if (read_eof || (has_hup && !has_input)) { + /* End of input */ + if (pipe == STDOUT_PIPE) + masterfd_stdout = -1; + if (pipe == STDERR_PIPE) + masterfd_stderr = -1; + + close (fd); + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; +} + +static gboolean timeout_cb (G_GNUC_UNUSED gpointer user_data) +{ + timed_out = TRUE; + ninfo ("Timed out, killing main loop"); + g_main_loop_quit (main_loop); + return G_SOURCE_REMOVE; +} + +static gboolean oom_cb(int fd, GIOCondition condition, G_GNUC_UNUSED gpointer user_data) +{ + uint64_t oom_event; + ssize_t num_read = 0; + + if ((condition & G_IO_IN) != 0) { + num_read = read(fd, &oom_event, sizeof(uint64_t)); + if (num_read < 0) { + nwarn("Failed to read oom event from eventfd"); + return G_SOURCE_CONTINUE; + } + + if (num_read > 0) { + if (num_read != sizeof(uint64_t)) + nwarn("Failed to read full oom event from eventfd"); + ninfo("OOM received"); + if (open("oom", O_CREAT, 0666) < 0) { + nwarn("Failed to write oom file"); + } + return G_SOURCE_CONTINUE; + } + } + + /* End of input */ + close (fd); + oom_event_fd = -1; + return G_SOURCE_REMOVE; +} + +static gboolean conn_sock_cb(int fd, GIOCondition condition, G_GNUC_UNUSED gpointer user_data) +{ + #define CONN_SOCK_BUF_SIZE 32*1024 /* Match the write size in CopyDetachable */ + char buf[CONN_SOCK_BUF_SIZE]; + ssize_t num_read = 0; + + if ((condition & G_IO_IN) != 0) { + num_read = read(fd, buf, CONN_SOCK_BUF_SIZE); + if (num_read < 0) + return G_SOURCE_CONTINUE; + + if (num_read > 0 && masterfd_stdin >= 0) { + if (write_all(masterfd_stdin, buf, num_read) < 0) { + nwarn("Failed to write to container stdin"); + } + return G_SOURCE_CONTINUE; + } + } + + /* End of input */ + conn_sock_shutdown(SHUT_RD); + if (masterfd_stdin >= 0 && opt_stdin) { + close(masterfd_stdin); + masterfd_stdin = -1; + } + return G_SOURCE_REMOVE; +} + +static gboolean attach_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data) +{ + conn_sock = accept(fd, NULL, NULL); + if (conn_sock == -1) { + if (errno != EWOULDBLOCK) + nwarn("Failed to accept client connection on attach socket"); + } else { + conn_sock_readable = true; + conn_sock_writable = true; + g_unix_fd_add (conn_sock, G_IO_IN|G_IO_HUP|G_IO_ERR, conn_sock_cb, GINT_TO_POINTER(STDOUT_PIPE)); + ninfo("Accepted connection %d", conn_sock); + } + + return G_SOURCE_CONTINUE; +} + +static gboolean ctrl_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data) +{ + #define CTLBUFSZ 200 + static char ctlbuf[CTLBUFSZ]; + static int readsz = CTLBUFSZ - 1; + static char *readptr = ctlbuf; + ssize_t num_read = 0; + int ctl_msg_type = -1; + int height = -1; + int width = -1; + struct winsize ws; + int ret; + + num_read = read(fd, readptr, readsz); + if (num_read <= 0) { + nwarn("Failed to read from control fd"); + return G_SOURCE_CONTINUE; + } + + readptr[num_read] = '\0'; + ninfo("Got ctl message: %s\n", ctlbuf); + + char *beg = ctlbuf; + char *newline = strchrnul(beg, '\n'); + /* Process each message which ends with a line */ + while (*newline != '\0') { + ret = sscanf(ctlbuf, "%d %d %d\n", &ctl_msg_type, &height, &width); + if (ret != 3) { + nwarn("Failed to sscanf message"); + return G_SOURCE_CONTINUE; + } + ninfo("Message type: %d, Height: %d, Width: %d", ctl_msg_type, height, width); + ret = ioctl(masterfd_stdout, TIOCGWINSZ, &ws); + ninfo("Existing size: %d %d", ws.ws_row, ws.ws_col); + ws.ws_row = height; + ws.ws_col = width; + ret = ioctl(masterfd_stdout, TIOCSWINSZ, &ws); + if (ret == -1) { + nwarn("Failed to set process pty terminal size"); + } + beg = newline + 1; + newline = strchrnul(beg, '\n'); + } + if (num_read == (CTLBUFSZ - 1) && beg == ctlbuf) { + /* + * We did not find a newline in the entire buffer. + * This shouldn't happen as our buffer is larger than + * the message that we expect to receive. + */ + nwarn("Could not find newline in entire buffer\n"); + } else if (*beg == '\0') { + /* We exhausted all messages that were complete */ + readptr = ctlbuf; + readsz = CTLBUFSZ - 1; + } else { + /* + * We copy remaining data to beginning of buffer + * and advance readptr after that. + */ + int cp_rem = 0; + do { + ctlbuf[cp_rem++] = *beg++; + } while (*beg != '\0'); + readptr = ctlbuf + cp_rem; + readsz = CTLBUFSZ - 1 - cp_rem; + } + + return G_SOURCE_CONTINUE; +} + +static gboolean terminal_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data) +{ + const char *csname = user_data; + struct file_t console; + int connfd = -1; + struct termios tset; + + ninfo("about to accept from console_socket_fd: %d", fd); + connfd = accept4(fd, NULL, NULL, SOCK_CLOEXEC); + if (connfd < 0) { + nwarn("Failed to accept console-socket connection"); + return G_SOURCE_CONTINUE; + } + + /* Not accepting anything else. */ + close(fd); + unlink(csname); + + /* We exit if this fails. */ + ninfo("about to recvfd from connfd: %d", connfd); + console = recvfd(connfd); + + ninfo("console = {.name = '%s'; .fd = %d}", console.name, console.fd); + free(console.name); + + /* We change the terminal settings to match kube settings */ + if (tcgetattr(console.fd, &tset) == -1) + pexit("Failed to get console terminal settings"); + + tset.c_oflag |= ONLCR; + + if (tcsetattr(console.fd, TCSANOW, &tset) == -1) + pexit("Failed to set console terminal settings"); + + /* We only have a single fd for both pipes, so we just treat it as + * stdout. stderr is ignored. */ + masterfd_stdin = console.fd; + masterfd_stdout = console.fd; + + /* Clean up everything */ + close(connfd); + + return G_SOURCE_CONTINUE; +} + +static void +runtime_exit_cb (G_GNUC_UNUSED GPid pid, int status, G_GNUC_UNUSED gpointer user_data) +{ + runtime_status = status; + g_main_loop_quit (main_loop); +} + +static void +container_exit_cb (G_GNUC_UNUSED GPid pid, int status, G_GNUC_UNUSED gpointer user_data) +{ + ninfo("container %d exited with status %d\n", pid, status); + container_status = status; + g_main_loop_quit (main_loop); +} + +static void write_sync_fd(int sync_pipe_fd, int res, const char *message) +{ + _cleanup_free_ char *escaped_message = NULL; + _cleanup_free_ char *json = NULL; + const char *res_key; + ssize_t len; + + if (sync_pipe_fd == -1) + return; + + if (opt_exec) + res_key = "exit_code"; + else + res_key = "pid"; + + if (message) { + escaped_message = escape_json_string(message); + json = g_strdup_printf ("{\"%s\": %d, \"message\": \"%s\"}\n", res_key, res, escaped_message); + } else { + json = g_strdup_printf ("{\"%s\": %d}\n", res_key, res); + } + + len = strlen(json); + if (write_all(sync_pipe_fd, json, len) != len) { + pexit("Unable to send container stderr message to parent"); + } +} + +static char *setup_console_socket(void) +{ + struct sockaddr_un addr = {0}; + _cleanup_free_ const char *tmpdir = g_get_tmp_dir(); + _cleanup_free_ char *csname = g_build_filename(tmpdir, "conmon-term.XXXXXX", NULL); + /* + * Generate a temporary name. Is this unsafe? Probably, but we can + * replace it with a rename(2) setup if necessary. + */ + + int unusedfd = g_mkstemp(csname); + if (unusedfd < 0) + pexit("Failed to generate random path for console-socket"); + close(unusedfd); + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, csname, sizeof(addr.sun_path)-1); + + ninfo("addr{sun_family=AF_UNIX, sun_path=%s}", addr.sun_path); + + /* Bind to the console socket path. */ + console_socket_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (console_socket_fd < 0) + pexit("Failed to create console-socket"); + if (fchmod(console_socket_fd, 0700)) + pexit("Failed to change console-socket permissions"); + /* XXX: This should be handled with a rename(2). */ + if (unlink(csname) < 0) + pexit("Failed to unlink temporary random path"); + if (bind(console_socket_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + pexit("Failed to bind to console-socket"); + if (listen(console_socket_fd, 128) < 0) + pexit("Failed to listen on console-socket"); + + return g_strdup(csname); +} + +static char *setup_attach_socket(void) +{ + _cleanup_free_ char *attach_sock_path = NULL; + char *attach_symlink_dir_path; + struct sockaddr_un attach_addr = {0}; + attach_addr.sun_family = AF_UNIX; + + /* + * Create a symlink so we don't exceed unix domain socket + * path length limit. + */ + attach_symlink_dir_path = g_build_filename(opt_socket_path, opt_cuuid, NULL); + if (unlink(attach_symlink_dir_path) == -1 && errno != ENOENT) + pexit("Failed to remove existing symlink for attach socket directory"); + + if (symlink(opt_bundle_path, attach_symlink_dir_path) == -1) + pexit("Failed to create symlink for attach socket"); + + attach_sock_path = g_build_filename(opt_socket_path, opt_cuuid, "attach", NULL); + ninfo("attach sock path: %s", attach_sock_path); + + strncpy(attach_addr.sun_path, attach_sock_path, sizeof(attach_addr.sun_path) - 1); + ninfo("addr{sun_family=AF_UNIX, sun_path=%s}", attach_addr.sun_path); + + /* + * We make the socket non-blocking to avoid a race where client aborts connection + * before the server gets a chance to call accept. In that scenario, the server + * accept blocks till a new client connection comes in. + */ + attach_socket_fd = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (attach_socket_fd == -1) + pexit("Failed to create attach socket"); + + if (fchmod(attach_socket_fd, 0700)) + pexit("Failed to change attach socket permissions"); + + if (bind(attach_socket_fd, (struct sockaddr *)&attach_addr, sizeof(struct sockaddr_un)) == -1) + pexit("Failed to bind attach socket: %s", attach_sock_path); + + if (listen(attach_socket_fd, 10) == -1) + pexit("Failed to listen on attach socket: %s", attach_sock_path); + + g_unix_fd_add (attach_socket_fd, G_IO_IN, attach_cb, NULL); + + return attach_symlink_dir_path; +} + +static void setup_terminal_control_fifo() +{ + _cleanup_free_ char *ctl_fifo_path = g_build_filename(opt_bundle_path, "ctl", NULL); + ninfo("ctl fifo path: %s", ctl_fifo_path); + + /* Setup fifo for reading in terminal resize and other stdio control messages */ + + if (mkfifo(ctl_fifo_path, 0666) == -1) + pexit("Failed to mkfifo at %s", ctl_fifo_path); + + terminal_ctrl_fd = open(ctl_fifo_path, O_RDONLY|O_NONBLOCK|O_CLOEXEC); + if (terminal_ctrl_fd == -1) + pexit("Failed to open control fifo"); + + /* + * Open a dummy writer to prevent getting flood of POLLHUPs when + * last writer closes. + */ + int dummyfd = open(ctl_fifo_path, O_WRONLY|O_CLOEXEC); + if (dummyfd == -1) + pexit("Failed to open dummy writer for fifo"); + + g_unix_fd_add (terminal_ctrl_fd, G_IO_IN, ctrl_cb, NULL); + + ninfo("terminal_ctrl_fd: %d", terminal_ctrl_fd); +} + +static void setup_oom_handling(int container_pid) +{ + /* Setup OOM notification for container process */ + _cleanup_free_ char *memory_cgroup_path = process_cgroup_subsystem_path(container_pid, "memory"); + _cleanup_close_ int cfd = -1; + int ofd = -1; /* Not closed */ + if (!memory_cgroup_path) { + nexit("Failed to get memory cgroup path"); + } + + _cleanup_free_ char *memory_cgroup_file_path = g_build_filename(memory_cgroup_path, "cgroup.event_control", NULL); + + if ((cfd = open(memory_cgroup_file_path, O_WRONLY | O_CLOEXEC)) == -1) { + nwarn("Failed to open %s", memory_cgroup_file_path); + return; + } + + _cleanup_free_ char *memory_cgroup_file_oom_path = g_build_filename(memory_cgroup_path, "memory.oom_control", NULL); + if ((ofd = open(memory_cgroup_file_oom_path, O_RDONLY | O_CLOEXEC)) == -1) + pexit("Failed to open %s", memory_cgroup_file_oom_path); + + if ((oom_event_fd = eventfd(0, EFD_CLOEXEC)) == -1) + pexit("Failed to create eventfd"); + + _cleanup_free_ char *data = g_strdup_printf("%d %d", oom_event_fd, ofd); + if (write_all(cfd, data, strlen(data)) < 0) + pexit("Failed to write to cgroup.event_control"); + + g_unix_fd_add (oom_event_fd, G_IO_IN, oom_cb, NULL); +} + +int main(int argc, char *argv[]) +{ + int ret; + char cwd[PATH_MAX]; + _cleanup_free_ char *default_pid_file = NULL; + _cleanup_free_ char *csname = NULL; + GError *err = NULL; + _cleanup_free_ char *contents = NULL; + int container_pid = -1; + pid_t main_pid, create_pid; + /* Used for !terminal cases. */ + int slavefd_stdin = -1; + int slavefd_stdout = -1; + int slavefd_stderr = -1; + char buf[BUF_SIZE]; + int num_read; + int sync_pipe_fd = -1; + int start_pipe_fd = -1; + GError *error = NULL; + GOptionContext *context; + GPtrArray *runtime_argv = NULL; + _cleanup_close_ int dev_null_r = -1; + _cleanup_close_ int dev_null_w = -1; + int fds[2]; + + main_loop = g_main_loop_new (NULL, FALSE); + + /* Command line parameters */ + context = g_option_context_new("- conmon utility"); + g_option_context_add_main_entries(context, opt_entries, "conmon"); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_print("option parsing failed: %s\n", error->message); + exit(1); + } + + if (opt_cid == NULL) + nexit("Container ID not provided. Use --cid"); + + if (!opt_exec && opt_cuuid == NULL) + nexit("Container UUID not provided. Use --cuuid"); + + if (opt_runtime_path == NULL) + nexit("Runtime path not provided. Use --runtime"); + + if (!opt_exec && opt_exit_dir == NULL) + nexit("Container exit directory not provided. Use --exit-dir"); + + if (opt_bundle_path == NULL && !opt_exec) { + if (getcwd(cwd, sizeof(cwd)) == NULL) { + nexit("Failed to get working directory"); + } + opt_bundle_path = cwd; + } + + dev_null_r = open("/dev/null", O_RDONLY | O_CLOEXEC); + if (dev_null_r < 0) + pexit("Failed to open /dev/null"); + + dev_null_w = open("/dev/null", O_WRONLY | O_CLOEXEC); + if (dev_null_w < 0) + pexit("Failed to open /dev/null"); + + if (opt_exec && opt_exec_process_spec == NULL) { + nexit("Exec process spec path not provided. Use --exec-process-spec"); + } + + if (opt_pid_file == NULL) { + default_pid_file = g_strdup_printf ("%s/pidfile-%s", cwd, opt_cid); + opt_pid_file = default_pid_file; + } + + if (opt_log_path == NULL) + nexit("Log file path not provided. Use --log-path"); + + start_pipe_fd = get_pipe_fd_from_env("_OCI_STARTPIPE"); + if (start_pipe_fd >= 0) { + /* Block for an initial write to the start pipe before + spawning any childred or exiting, to ensure the + parent can put us in the right cgroup. */ + read(start_pipe_fd, buf, BUF_SIZE); + close(start_pipe_fd); + } + + /* In the create-container case we double-fork in + order to disconnect from the parent, as we want to + continue in a daemon-like way */ + main_pid = fork(); + if (main_pid < 0) { + pexit("Failed to fork the create command"); + } else if (main_pid != 0) { + exit(0); + } + + /* Disconnect stdio from parent. We need to do this, because + the parent is waiting for the stdout to end when the intermediate + child dies */ + if (dup2(dev_null_r, STDIN_FILENO) < 0) + pexit("Failed to dup over stdin"); + if (dup2(dev_null_w, STDOUT_FILENO) < 0) + pexit("Failed to dup over stdout"); + if (dup2(dev_null_w, STDERR_FILENO) < 0) + pexit("Failed to dup over stderr"); + + /* Create a new session group */ + setsid(); + + /* Environment variables */ + sync_pipe_fd = get_pipe_fd_from_env("_OCI_SYNCPIPE"); + + /* Open the log path file. */ + log_fd = open(opt_log_path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0600); + if (log_fd < 0) + pexit("Failed to open log file"); + + /* + * Set self as subreaper so we can wait for container process + * and return its exit code. + */ + ret = prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); + if (ret != 0) { + pexit("Failed to set as subreaper"); + } + + if (opt_terminal) { + csname = setup_console_socket(); + } else { + + /* + * Create a "fake" master fd so that we can use the same epoll code in + * both cases. The slavefd_*s will be closed after we dup over + * everything. + * + * We use pipes here because open(/dev/std{out,err}) will fail if we + * used anything else (and it wouldn't be a good idea to create a new + * pty pair in the host). + */ + + if (opt_stdin) { + if (pipe2(fds, O_CLOEXEC) < 0) + pexit("Failed to create !terminal stdin pipe"); + + masterfd_stdin = fds[1]; + slavefd_stdin = fds[0]; + } + + if (pipe2(fds, O_CLOEXEC) < 0) + pexit("Failed to create !terminal stdout pipe"); + + masterfd_stdout = fds[0]; + slavefd_stdout = fds[1]; + } + + /* We always create a stderr pipe, because that way we can capture + runc stderr messages before the tty is created */ + if (pipe2(fds, O_CLOEXEC) < 0) + pexit("Failed to create stderr pipe"); + + masterfd_stderr = fds[0]; + slavefd_stderr = fds[1]; + + runtime_argv = g_ptr_array_new(); + add_argv(runtime_argv, + opt_runtime_path, + NULL); + + /* Generate the cmdline. */ + if (!opt_exec && opt_systemd_cgroup) + add_argv(runtime_argv, + "--systemd-cgroup", + NULL); + + if (opt_exec) { + add_argv(runtime_argv, + "exec", "-d", + "--pid-file", opt_pid_file, + NULL); + } else { + add_argv(runtime_argv, + "create", + "--bundle", opt_bundle_path, + "--pid-file", opt_pid_file, + NULL); + } + + if (!opt_exec && opt_no_pivot) { + add_argv(runtime_argv, + "--no-pivot", + NULL); + } + + if (csname != NULL) { + add_argv(runtime_argv, + "--console-socket", csname, + NULL); + } + + /* Set the exec arguments. */ + if (opt_exec) { + add_argv(runtime_argv, + "--process", opt_exec_process_spec, + NULL); + } + + /* Container name comes last. */ + add_argv(runtime_argv, opt_cid, NULL); + end_argv(runtime_argv); + + /* + * We have to fork here because the current runC API dups the stdio of the + * calling process over the container's fds. This is actually *very bad* + * but is currently being discussed for change in + * https://github.com/opencontainers/runtime-spec/pull/513. Hopefully this + * won't be the case for very long. + */ + + /* Create our container. */ + create_pid = fork(); + if (create_pid < 0) { + pexit("Failed to fork the create command"); + } else if (!create_pid) { + /* FIXME: This results in us not outputting runc error messages to crio's log. */ + if (slavefd_stdin < 0) + slavefd_stdin = dev_null_r; + if (dup2(slavefd_stdin, STDIN_FILENO) < 0) + pexit("Failed to dup over stdout"); + + if (slavefd_stdout < 0) + slavefd_stdout = dev_null_w; + if (dup2(slavefd_stdout, STDOUT_FILENO) < 0) + pexit("Failed to dup over stdout"); + + if (slavefd_stderr < 0) + slavefd_stderr = slavefd_stdout; + if (dup2(slavefd_stderr, STDERR_FILENO) < 0) + pexit("Failed to dup over stderr"); + + execv(g_ptr_array_index(runtime_argv,0), (char **)runtime_argv->pdata); + exit(127); + } + + g_ptr_array_free (runtime_argv, TRUE); + + /* The runtime has that fd now. We don't need to touch it anymore. */ + close(slavefd_stdin); + close(slavefd_stdout); + close(slavefd_stderr); + + /* Map pid to its handler. */ + GHashTable *pid_to_handler = g_hash_table_new (g_int_hash, g_int_equal); + g_hash_table_insert (pid_to_handler, &create_pid, runtime_exit_cb); + + /* + * Glib does not support SIGCHLD so use SIGUSR1 with the same semantic. We will + * catch SIGCHLD and raise(SIGUSR1) in the signal handler. + */ + g_unix_signal_add (SIGUSR1, on_sigusr1_cb, pid_to_handler); + + if (signal(SIGCHLD, on_sigchld) == SIG_ERR) + pexit("Failed to set handler for SIGCHLD"); + + ninfo("about to waitpid: %d", create_pid); + if (csname != NULL) { + guint terminal_watch = g_unix_fd_add (console_socket_fd, G_IO_IN, terminal_accept_cb, csname); + /* Process any SIGCHLD we may have missed before the signal handler was in place. */ + check_child_processes (pid_to_handler); + g_main_loop_run (main_loop); + g_source_remove (terminal_watch); + } else { + int ret; + /* Wait for our create child to exit with the return code. */ + do + ret = waitpid(create_pid, &runtime_status, 0); + while (ret < 0 && errno == EINTR); + if (ret < 0) { + int old_errno = errno; + kill(create_pid, SIGKILL); + errno = old_errno; + pexit("Failed to wait for `runtime %s`", opt_exec ? "exec" : "create"); + } + + } + + if (!WIFEXITED(runtime_status) || WEXITSTATUS(runtime_status) != 0) { + if (sync_pipe_fd > 0) { + /* + * Read from container stderr for any error and send it to parent + * We send -1 as pid to signal to parent that create container has failed. + */ + num_read = read(masterfd_stderr, buf, BUF_SIZE); + if (num_read > 0) { + buf[num_read] = '\0'; + write_sync_fd(sync_pipe_fd, -1, buf); + } + } + nexit("Failed to create container: exit status %d", WEXITSTATUS(runtime_status)); + } + + if (opt_terminal && masterfd_stdout == -1) + nexit("Runtime did not set up terminal"); + + /* Read the pid so we can wait for the process to exit */ + g_file_get_contents(opt_pid_file, &contents, NULL, &err); + if (err) { + nwarn("Failed to read pidfile: %s", err->message); + g_error_free(err); + exit(1); + } + + container_pid = atoi(contents); + ninfo("container PID: %d", container_pid); + + g_hash_table_insert (pid_to_handler, &container_pid, container_exit_cb); + + /* Setup endpoint for attach */ + _cleanup_free_ char *attach_symlink_dir_path = NULL; + if (!opt_exec) { + attach_symlink_dir_path = setup_attach_socket(); + } + + if (!opt_exec) { + setup_terminal_control_fifo(); + } + + /* Send the container pid back to parent */ + if (!opt_exec) { + write_sync_fd(sync_pipe_fd, container_pid, NULL); + } + + setup_oom_handling(container_pid); + + if (masterfd_stdout >= 0) { + g_unix_fd_add (masterfd_stdout, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDOUT_PIPE)); + } + if (masterfd_stderr >= 0) { + g_unix_fd_add (masterfd_stderr, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDERR_PIPE)); + } + + if (opt_timeout > 0) { + g_timeout_add_seconds (opt_timeout, timeout_cb, NULL); + } + + check_child_processes(pid_to_handler); + + g_main_loop_run (main_loop); + + /* Drain stdout and stderr */ + if (masterfd_stdout != -1) { + g_unix_set_fd_nonblocking(masterfd_stdout, TRUE, NULL); + while (read_stdio(masterfd_stdout, STDOUT_PIPE, NULL)) + ; + } + if (masterfd_stderr != -1) { + g_unix_set_fd_nonblocking(masterfd_stderr, TRUE, NULL); + while (read_stdio(masterfd_stderr, STDERR_PIPE, NULL)) + ; + } + + int exit_status = -1; + const char *exit_message = NULL; + + if (timed_out) { + kill(container_pid, SIGKILL); + exit_message = "command timed out"; + } else { + exit_status = WEXITSTATUS(container_status); + } + + if (!opt_exec) { + _cleanup_free_ char *status_str = g_strdup_printf("%d", exit_status); + _cleanup_free_ char *exit_file_path = g_build_filename(opt_exit_dir, opt_cid, NULL); + if (!g_file_set_contents(exit_file_path, status_str, -1, &err)) + nexit("Failed to write %s to exit file: %s\n", + status_str, err->message); + } else { + /* Send the command exec exit code back to the parent */ + write_sync_fd(sync_pipe_fd, exit_status, exit_message); + } + + if (attach_symlink_dir_path != NULL && + unlink(attach_symlink_dir_path) == -1 && errno != ENOENT) { + pexit("Failed to remove symlink for attach socket directory"); + } + + return EXIT_SUCCESS; +} diff --git a/vendor/github.com/kubernetes-incubator/cri-o/vendor.conf b/vendor/github.com/kubernetes-incubator/cri-o/vendor.conf new file mode 100644 index 000000000..c2f3d452a --- /dev/null +++ b/vendor/github.com/kubernetes-incubator/cri-o/vendor.conf @@ -0,0 +1,113 @@ +k8s.io/kubernetes v1.8.1 https://github.com/kubernetes/kubernetes +k8s.io/client-go release-5.0 https://github.com/kubernetes/client-go +k8s.io/apimachinery release-1.8 https://github.com/kubernetes/apimachinery +k8s.io/apiserver release-1.8 https://github.com/kubernetes/apiserver +k8s.io/utils 4fe312863be2155a7b68acd2aff1c9221b24e68c https://github.com/kubernetes/utils +k8s.io/api release-1.8 https://github.com/kubernetes/api +k8s.io/kube-openapi abfc5fbe1cf87ee697db107fdfd24c32fe4397a8 https://github.com/kubernetes/kube-openapi +k8s.io/apiextensions-apiserver release-1.8 https://github.com/kubernetes/apiextensions-apiserver +# +github.com/googleapis/gnostic 0c5108395e2debce0d731cf0287ddf7242066aba +github.com/gregjones/httpcache 787624de3eb7bd915c329cba748687a3b22666a6 +github.com/json-iterator/go 1.0.0 +github.com/peterbourgon/diskv v2.0.1 +github.com/sirupsen/logrus v1.0.0 +github.com/containers/image storage-update https://github.com/nalind/image +github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1 +github.com/ostreedev/ostree-go master +github.com/containers/storage 9e0c323a4b425557f8310ee8d125634acd39d8f5 +github.com/containernetworking/cni v0.4.0 +google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go +github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd +github.com/opencontainers/go-digest v1.0.0-rc0 +github.com/opencontainers/runtime-tools d3f7e9e9e631c7e87552d67dc7c86de33c3fb68a +github.com/opencontainers/runc 45bde006ca8c90e089894508708bcf0e2cdf9e13 +github.com/mrunalp/fileutils master +github.com/vishvananda/netlink master +github.com/vishvananda/netns master +github.com/opencontainers/image-spec v1.0.0 +github.com/opencontainers/runtime-spec v1.0.0 +github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342 +github.com/tchap/go-patricia v2.2.6 +gopkg.in/cheggaaa/pb.v1 v1.0.7 +gopkg.in/inf.v0 v0.9.0 +gopkg.in/yaml.v2 v2 +github.com/docker/docker d4f6db83c21cfc6af54fffb1f13e8acb7199f96a +github.com/docker/spdystream ed496381df8283605c435b86d4fdd6f4f20b8c6e +github.com/docker/distribution 7a8efe719e55bbfaff7bc5718cdf0ed51ca821df +github.com/docker/go-units v0.3.1 +github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d +github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20 +github.com/mistifyio/go-zfs v2.1.1 +github.com/ghodss/yaml 04f313413ffd65ce25f2541bfd2b2ceec5c0908c +github.com/imdario/mergo 0.2.2 +github.com/gorilla/mux v1.3.0 +github.com/gorilla/context v1.1 +github.com/mtrmac/gpgme b2432428689ca58c2b8e8dea9449d3295cf96fc9 +github.com/mattn/go-runewidth v0.0.1 +github.com/seccomp/libseccomp-golang v0.9.0 +github.com/syndtr/gocapability e7cb7fa329f456b3855136a2642b197bad7366ba +github.com/blang/semver v3.5.0 +github.com/BurntSushi/toml v0.2.0 +github.com/mitchellh/go-wordwrap ad45545899c7b13c020ea92b2072220eefad42b8 +github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998 +github.com/davecgh/go-spew v1.1.0 +github.com/go-openapi/spec 6aced65f8501fe1217321abf0749d354824ba2ff +github.com/go-openapi/jsonpointer 779f45308c19820f1a69e9a4cd965f496e0da10f +github.com/go-openapi/jsonreference 36d33bfe519efae5632669801b180bf1a245da3b +github.com/go-openapi/swag 1d0bd113de87027671077d3c71eb3ac5d7dbba72 +github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c +github.com/mailru/easyjson 99e922cf9de1bc0ab38310c277cff32c2147e747 +github.com/PuerkitoBio/purell v1.1.0 +github.com/PuerkitoBio/urlesc 5bd2802263f21d8788851d5305584c82a5c75d7e +github.com/ugorji/go d23841a297e5489e787e72fceffabf9d2994b52a +github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7 +golang.org/x/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2 +golang.org/x/net c427ad74c6d7a814201695e9ffde0c5d400a7674 +golang.org/x/sys 9aade4d3a3b7e6d876cd3823ad20ec45fc035402 +golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 +github.com/kr/pty v1.0.0 +github.com/google/btree 7d79101e329e5a3adf994758c578dab82b90c017 +github.com/gogo/protobuf c0656edd0d9eab7c66d1eb0c568f9039345796f7 +github.com/golang/protobuf 4bd1920723d7b7c925de087aa32e2187708897f7 +github.com/coreos/go-systemd v14 +github.com/coreos/pkg v3 +github.com/golang/groupcache b710c8433bd175204919eb38776e944233235d03 +github.com/fsnotify/fsnotify 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1 +github.com/Azure/go-ansiterm 19f72df4d05d31cbe1c56bfc8045c96babff6c7e +github.com/Microsoft/go-winio 78439966b38d69bf38227fbf57ac8a6fee70f69a +github.com/Microsoft/hcsshim 43f9725307998e09f2e3816c2c0c36dc98f0c982 +github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46 +github.com/emicklei/go-restful-swagger12 1.0.1 +github.com/pkg/errors v0.8.0 +github.com/godbus/dbus a389bdde4dd695d414e47b755e95e72b7826432c +github.com/urfave/cli v1.20.0 +github.com/vbatts/tar-split v0.10.1 +github.com/renstrom/dedent v1.0.0 +github.com/hpcloud/tail v1.0.0 +gopkg.in/fsnotify.v1 v1.4.2 +gopkg.in/tomb.v1 v1 +github.com/fatih/camelcase f6a740d52f961c60348ebb109adde9f4635d7540 +github.com/buger/goterm 2f8dfbc7dbbff5dd1d391ed91482c24df243b2d3 +github.com/dgrijalva/jwt-go v3.0.0 +github.com/exponent-io/jsonpath d6023ce2651d8eafb5c75bb0c7167536102ec9f5 +github.com/hashicorp/golang-lru 0a025b7e63adc15a622f29b0b2c4c3848243bbf6 +github.com/go-openapi/loads 18441dfa706d924a39a030ee2c3b1d8d81917b38 +github.com/go-openapi/analysis b44dc874b601d9e4e2f6e19140e794ba24bead3b +github.com/go-openapi/strfmt 93a31ef21ac23f317792fff78f9539219dd74619 +github.com/asaskevich/govalidator v6 +github.com/go-openapi/errors d24ebc2075bad502fac3a8ae27aa6dd58e1952dc +github.com/mitchellh/mapstructure d0303fe809921458f417bcf828397a65db30a7e4 +gopkg.in/mgo.v2 v2 +github.com/prometheus/client_golang e7e903064f5e9eb5da98208bae10b475d4db0f8c +github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 +github.com/prometheus/common 13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207 +github.com/prometheus/procfs 65c1f6f8f0fc1e2185eb9863a3bc751496404259 +github.com/matttproud/golang_protobuf_extensions fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a +github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4 +github.com/containerd/cgroups 7a5fdd8330119dc70d850260db8f3594d89d6943 +github.com/go-zoo/bone 031b4005dfe248ccba241a0c9de0f9e112fd6b7c +github.com/soheilhy/cmux v0.1.3 +github.com/hashicorp/go-multierror 83588e72410abfbe4df460eeb6f30841ae47d4c4 +github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 +github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac -- cgit v1.2.3-54-g00ecf