Merge pull request #1559 from Mashimiao/panic-fix-nil-linux

fix panic when Linux is nil for rootless case
diff --git a/.travis.yml b/.travis.yml
index a9032a2..fd8f79f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,4 +30,4 @@
 script:
   - git-validation -run DCO,short-subject -v
   - make BUILDTAGS="${BUILDTAGS}"
-  - make BUILDTAGS="${BUILDTAGS}" clean validate test
+  - make BUILDTAGS="${BUILDTAGS}" clean ci
diff --git a/Makefile b/Makefile
index dc13d2e..0877a07 100644
--- a/Makefile
+++ b/Makefile
@@ -29,43 +29,21 @@
 .DEFAULT: runc
 
 runc: $(SOURCES)
-	$(GO) build $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc .
+	$(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc .
 
 all: runc recvtty
 
 recvtty: contrib/cmd/recvtty/recvtty
 
 contrib/cmd/recvtty/recvtty: $(SOURCES)
-	$(GO) build $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
+	$(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
 
 static: $(SOURCES)
 	CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) cgo static_build" -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o runc .
 	CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) cgo static_build" -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
 
 release:
-	@flag_list=(seccomp selinux apparmor static); \
-	unset expression; \
-	for flag in "$${flag_list[@]}"; do \
-		expression+="' '{'',$${flag}}"; \
-	done; \
-	eval profile_list=("$$expression"); \
-	for profile in "$${profile_list[@]}"; do \
-		output=${RELEASE_DIR}/runc; \
-		for flag in $$profile; do \
-			output+=."$$flag"; \
-		done; \
-		tags="$$profile"; \
-		ldflags="-X main.gitCommit=${COMMIT} -X main.version=${VERSION}"; \
-		CGO_ENABLED=; \
-		[[ "$$profile" =~ static ]] && { \
-			tags="$${tags/static/static_build}"; \
-			tags+=" cgo"; \
-			ldflags+=" -w -extldflags -static"; \
-			CGO_ENABLED=1; \
-		}; \
-		echo "Building target: $$output"; \
-		$(GO) build $(EXTRA_FLAGS) -ldflags "$$ldflags $(EXTRA_LDFLAGS)" -tags "$$tags" -o "$$output" .; \
-	done
+	script/release.sh -r release/$(VERSION) -v $(VERSION)
 
 dbuild: runcimage
 	docker run --rm -v $(CURDIR):/go/src/$(PROJECT) --privileged $(RUNC_IMAGE) make clean all
@@ -137,7 +115,7 @@
 	script/validate-gofmt
 	$(GO) vet $(allpackages)
 
-ci: validate localtest
+ci: validate test release
 
 # memoize allpackages, so that it's executed only once and only if used
 _allpackages = $(shell $(GO) list ./... | grep -v vendor)
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
index d7e7516..cbdfe0d 100644
--- a/libcontainer/container_linux.go
+++ b/libcontainer/container_linux.go
@@ -186,8 +186,17 @@
 	if status == Stopped {
 		return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
 	}
+	if err := c.cgroupManager.Set(&config); err != nil {
+		// Set configs back
+		if err2 := c.cgroupManager.Set(c.config); err2 != nil {
+			logrus.Warnf("Setting back cgroup configs failed due to error: %v, your state.json and actual configs might be inconsistent.", err2)
+		}
+		return err
+	}
+	// After config setting succeed, update config and states
 	c.config = &config
-	return c.cgroupManager.Set(c.config)
+	_, err = c.updateState(nil)
+	return err
 }
 
 func (c *linuxContainer) Start(process *Process) error {
@@ -1388,7 +1397,9 @@
 }
 
 func (c *linuxContainer) updateState(process parentProcess) (*State, error) {
-	c.initProcess = process
+	if process != nil {
+		c.initProcess = process
+	}
 	state, err := c.currentState()
 	if err != nil {
 		return nil, err
diff --git a/libcontainer/container_linux_test.go b/libcontainer/container_linux_test.go
index 3fdd4a8..75c2898 100644
--- a/libcontainer/container_linux_test.go
+++ b/libcontainer/container_linux_test.go
@@ -9,6 +9,7 @@
 
 	"github.com/opencontainers/runc/libcontainer/cgroups"
 	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/system"
 )
 
 type mockCgroupManager struct {
@@ -216,3 +217,65 @@
 		}
 	}
 }
+
+func TestGetContainerStateAfterUpdate(t *testing.T) {
+	var (
+		pid = os.Getpid()
+	)
+	stat, err := system.Stat(pid)
+	if err != nil {
+		t.Fatal(err)
+	}
+	container := &linuxContainer{
+		id: "myid",
+		config: &configs.Config{
+			Namespaces: []configs.Namespace{
+				{Type: configs.NEWPID},
+				{Type: configs.NEWNS},
+				{Type: configs.NEWNET},
+				{Type: configs.NEWUTS},
+				{Type: configs.NEWIPC},
+			},
+			Cgroups: &configs.Cgroup{
+				Resources: &configs.Resources{
+					Memory: 1024,
+				},
+			},
+		},
+		initProcess: &mockProcess{
+			_pid:    pid,
+			started: stat.StartTime,
+		},
+		cgroupManager: &mockCgroupManager{},
+	}
+	container.state = &createdState{c: container}
+	state, err := container.State()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if state.InitProcessPid != pid {
+		t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid)
+	}
+	if state.InitProcessStartTime != stat.StartTime {
+		t.Fatalf("expected process start time %d but received %d", stat.StartTime, state.InitProcessStartTime)
+	}
+	if state.Config.Cgroups.Resources.Memory != 1024 {
+		t.Fatalf("expected Memory to be 1024 but received %q", state.Config.Cgroups.Memory)
+	}
+
+	// Set initProcessStartTime so we fake to be running
+	container.initProcessStartTime = state.InitProcessStartTime
+	container.state = &runningState{c: container}
+	newConfig := container.Config()
+	newConfig.Cgroups.Resources.Memory = 2048
+	if err := container.Set(newConfig); err != nil {
+		t.Fatal(err)
+	}
+	state, err = container.State()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if state.Config.Cgroups.Resources.Memory != 2048 {
+		t.Fatalf("expected Memory to be 2048 but received %q", state.Config.Cgroups.Memory)
+	}
+}
diff --git a/script/release.sh b/script/release.sh
new file mode 100755
index 0000000..a1ebc95
--- /dev/null
+++ b/script/release.sh
@@ -0,0 +1,130 @@
+#!/bin/bash
+# Copyright (C) 2017 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.
+
+set -e
+
+## --->
+# Project-specific options and functions. In *theory* you shouldn't need to
+# touch anything else in this script in order to use this elsewhere.
+project="runc"
+root="$(readlink -f "$(dirname "${BASH_SOURCE}")/..")"
+
+# This function takes an output path as an argument, where the built
+# (preferably static) binary should be placed.
+function build_project() {
+	builddir="$(dirname "$1")"
+
+	# Build with all tags enabled.
+	make -C "$root" COMMIT_NO= BUILDTAGS="seccomp selinux apparmor" static
+	mv "$root/$project" "$1"
+}
+
+# End of the easy-to-configure portion.
+## <---
+
+# Print usage information.
+function usage() {
+	echo "usage: release.sh [-S <gpg-key-id>] [-c <commit-ish>] [-r <release-dir>] [-v <version>]" >&2
+	exit 1
+}
+
+# Log something to stderr.
+function log() {
+	echo "[*] $*" >&2
+}
+
+# Log something to stderr and then exit with 0.
+function bail() {
+	log "$@"
+	exit 0
+}
+
+# Conduct a sanity-check to make sure that GPG provided with the given
+# arguments can sign something. Inability to sign things is not a fatal error.
+function gpg_cansign() {
+	gpg "$@" --clear-sign </dev/null >/dev/null
+}
+
+# When creating releases we need to build static binaries, an archive of the
+# current commit, and generate detached signatures for both.
+keyid=""
+commit="HEAD"
+version=""
+releasedir=""
+hashcmd=""
+while getopts "S:c:r:v:h:" opt; do
+	case "$opt" in
+		S)
+			keyid="$OPTARG"
+			;;
+		c)
+			commit="$OPTARG"
+			;;
+		r)
+			releasedir="$OPTARG"
+			;;
+		v)
+			version="$OPTARG"
+			;;
+		h)
+			hashcmd="$OPTARG"
+			;;
+		\:)
+			echo "Missing argument: -$OPTARG" >&2
+			usage
+			;;
+		\?)
+			echo "Invalid option: -$OPTARG" >&2
+			usage
+			;;
+	esac
+done
+
+version="${version:-$(<"$root/VERSION")}"
+releasedir="${releasedir:-release/$version}"
+hashcmd="${hashcmd:-sha256sum}"
+goarch="$(go env GOARCH || echo "amd64")"
+
+log "creating $project release in '$releasedir'"
+log "  version: $version"
+log "   commit: $commit"
+log "      key: ${keyid:-DEFAULT}"
+log "     hash: $hashcmd"
+
+# Make explicit what we're doing.
+set -x
+
+# Make the release directory.
+rm -rf "$releasedir" && mkdir -p "$releasedir"
+
+# Build project.
+build_project "$releasedir/$project.$goarch"
+
+# Generate new archive.
+git archive --format=tar --prefix="$project-$version/" "$commit" | xz > "$releasedir/$project.tar.xz"
+
+# Generate sha256 checksums for both.
+( cd "$releasedir" ; "$hashcmd" "$project".{"$goarch",tar.xz} > "$project.$hashcmd" ; )
+
+# Set up the gpgflags.
+[[ "$keyid" ]] && export gpgflags="--default-key $keyid"
+gpg_cansign $gpgflags || bail "Could not find suitable GPG key, skipping signing step."
+
+# Sign everything.
+gpg $gpgflags --detach-sign --armor "$releasedir/$project.$goarch"
+gpg $gpgflags --detach-sign --armor "$releasedir/$project.tar.xz"
+gpg $gpgflags --clear-sign --armor \
+	--output "$releasedir/$project.$hashcmd"{.tmp,} && \
+	mv "$releasedir/$project.$hashcmd"{.tmp,}