2016-04-15 06:42:40 +00:00
package main
import (
"flag"
"fmt"
"os"
2016-04-15 08:54:02 +00:00
"os/exec"
2016-04-15 06:42:40 +00:00
"runtime"
2016-04-15 08:54:02 +00:00
"strings"
2016-04-15 06:42:40 +00:00
"github.com/Sirupsen/logrus"
2016-04-15 08:54:02 +00:00
aaprofile "github.com/docker/docker/profiles/apparmor"
2016-04-15 06:42:40 +00:00
"github.com/opencontainers/runc/libcontainer"
2016-04-15 08:54:02 +00:00
"github.com/opencontainers/runc/libcontainer/apparmor"
2016-04-17 01:23:42 +00:00
_ "github.com/opencontainers/runc/libcontainer/nsenter"
2016-04-16 23:05:09 +00:00
"github.com/opencontainers/runc/libcontainer/user"
2016-04-15 08:54:02 +00:00
"github.com/opencontainers/runtime-spec/specs-go"
2016-04-15 06:42:40 +00:00
)
const (
// BANNER is what is printed for help/info output.
BANNER = ` _ _ _
| | __ ( _ ) _ __ ___ | | _ _ __
| ' _ \ | | ' _ \ / __ | __ | ' __ |
| | _ ) | | | | | ( __ | | _ | |
| _ . __ / | _ | _ | | _ | \ ___ | \ __ | _ |
2016-04-15 08:54:02 +00:00
Fully static , self - contained container including the rootfs
that can be run by an unprivileged user .
Embedded Image : % s - % s
2016-04-15 06:42:40 +00:00
Version : % s
GitCommit : % s
2016-04-15 08:54:02 +00:00
2016-04-15 06:42:40 +00:00
`
2016-04-18 08:09:18 +00:00
defaultRoot = "/tmp/binctr"
2016-04-15 08:54:02 +00:00
defaultRootfsDir = "rootfs"
defaultApparmorProfile = "docker-default"
2016-04-15 06:42:40 +00:00
)
var (
console = os . Getenv ( "console" )
containerID string
2016-04-15 08:54:02 +00:00
pidFile string
2016-04-15 06:42:40 +00:00
root string
2016-04-18 08:09:18 +00:00
passkey string
2016-04-15 06:42:40 +00:00
2016-04-17 01:23:42 +00:00
allocateTty bool
detach bool
readonly bool
useSystemdCgroup bool
2016-04-15 08:54:02 +00:00
hooks specs . Hooks
hookflags stringSlice
2016-04-15 06:42:40 +00:00
debug bool
version bool
// GITCOMMIT is git commit the binary was compiled against.
GITCOMMIT = ""
// VERSION is the binary version.
VERSION = "v0.1.0"
2016-04-15 08:54:02 +00:00
// IMAGE is the name of the image that is embedded at compile time.
IMAGE = "alpine"
// IMAGESHA is the sha digest of the image that is embedded at compile time.
IMAGESHA = "sha256:70c557e50ed630deed07cbb0dc4d28aa0f2a485cf7af124cc48f06bce83f784b"
2016-04-15 06:42:40 +00:00
)
2016-04-15 08:54:02 +00:00
// stringSlice is a slice of strings
type stringSlice [ ] string
// implement the flag interface for stringSlice
func ( s * stringSlice ) String ( ) string {
return fmt . Sprintf ( "%s" , * s )
}
func ( s * stringSlice ) Set ( value string ) error {
* s = append ( * s , value )
return nil
}
func ( s stringSlice ) ParseHooks ( ) ( hooks specs . Hooks , err error ) {
for _ , v := range s {
parts := strings . SplitN ( v , ":" , 2 )
if len ( parts ) <= 1 {
return hooks , fmt . Errorf ( "parsing %s as hook_name:exec failed" , v )
}
cmd := strings . Split ( parts [ 1 ] , " " )
exec , err := exec . LookPath ( cmd [ 0 ] )
if err != nil {
return hooks , fmt . Errorf ( "looking up exec path for %s failed: %v" , cmd [ 0 ] , err )
}
hook := specs . Hook {
Path : exec ,
}
if len ( cmd ) > 1 {
hook . Args = cmd [ : 1 ]
}
switch parts [ 0 ] {
case "prestart" :
hooks . Prestart = append ( hooks . Prestart , hook )
case "poststart" :
hooks . Poststart = append ( hooks . Poststart , hook )
case "poststop" :
hooks . Poststop = append ( hooks . Poststop , hook )
default :
return hooks , fmt . Errorf ( "%s is not a valid hook, try 'prestart', 'poststart', or 'poststop'" , parts [ 0 ] )
}
}
return hooks , nil
}
2016-04-15 06:42:40 +00:00
func init ( ) {
// Parse flags
2016-04-15 08:54:02 +00:00
flag . StringVar ( & containerID , "id" , IMAGE , "container ID" )
2016-04-15 06:42:40 +00:00
flag . StringVar ( & console , "console" , console , "the pty slave path for use with the container" )
2016-04-15 08:54:02 +00:00
flag . StringVar ( & pidFile , "pid-file" , "" , "specify the file to write the process id to" )
2016-04-15 06:42:40 +00:00
flag . StringVar ( & root , "root" , defaultRoot , "root directory of container state, should be tmpfs" )
2016-04-18 08:09:18 +00:00
flag . StringVar ( & passkey , "key" , "" , "key to decrypt the embedded tarball" )
2016-04-15 08:54:02 +00:00
flag . Var ( & hookflags , "hook" , "Hooks to prefill into spec file. (ex. --hook prestart:netns)" )
flag . BoolVar ( & allocateTty , "t" , true , "allocate a tty for the container" )
flag . BoolVar ( & detach , "d" , false , "detach from the container's process" )
2016-04-17 01:23:42 +00:00
// TODO (jess): do not enable this flag, the error is very gross on systemd
// flag.BoolVar(&useSystemdCgroup, "systemd-cgroup", false, "enable systemd cgroup support")
2016-04-15 08:54:02 +00:00
flag . BoolVar ( & readonly , "read-only" , false , "make container filesystem readonly" )
2016-04-15 06:42:40 +00:00
flag . BoolVar ( & version , "version" , false , "print version and exit" )
flag . BoolVar ( & version , "v" , false , "print version and exit (shorthand)" )
2016-04-15 08:54:02 +00:00
flag . BoolVar ( & debug , "D" , false , "run in debug mode" )
2016-04-15 06:42:40 +00:00
flag . Usage = func ( ) {
2016-04-15 08:54:02 +00:00
fmt . Fprint ( os . Stderr , fmt . Sprintf ( BANNER , IMAGE , IMAGESHA , VERSION , GITCOMMIT ) )
2016-04-15 06:42:40 +00:00
flag . PrintDefaults ( )
}
flag . Parse ( )
if version {
2016-04-15 08:54:02 +00:00
fmt . Printf ( "%s, commit: %s, image: %s, image digest: %s" , VERSION , GITCOMMIT , IMAGE , IMAGESHA )
2016-04-15 06:42:40 +00:00
os . Exit ( 0 )
}
// Set log level
if debug {
logrus . SetLevel ( logrus . DebugLevel )
}
2016-04-15 08:54:02 +00:00
// parse the hook flags
var err error
hooks , err = hookflags . ParseHooks ( )
if err != nil {
logrus . Fatal ( err )
}
2016-04-15 06:42:40 +00:00
}
2016-04-15 08:54:02 +00:00
//go:generate go run generate.go
2016-04-15 06:42:40 +00:00
func main ( ) {
if len ( os . Args ) > 1 && os . Args [ 1 ] == "init" {
runInit ( )
return
}
2016-04-15 08:54:02 +00:00
notifySocket := os . Getenv ( "NOTIFY_SOCKET" )
if notifySocket != "" {
setupSdNotify ( spec , notifySocket )
}
// override the cmd in the spec with any args specified
if len ( flag . Args ( ) ) > 0 {
spec . Process . Args = flag . Args ( )
}
// setup readonly fs in spec
spec . Root . Readonly = readonly
// setup tty in spec
spec . Process . Terminal = allocateTty
// pass in any hooks
spec . Hooks = hooks
// install the default apparmor profile
if apparmor . IsEnabled ( ) {
2016-04-16 23:05:09 +00:00
// check if we have the docker-default apparmor profile loaded
if err := aaprofile . IsLoaded ( defaultApparmorProfile ) ; err != nil {
logrus . Warnf ( "AppArmor enabled on system but the %s profile is not loaded. apparmor_parser needs root to load a profile so we can't do it for you." , defaultApparmorProfile )
2016-04-15 08:54:02 +00:00
} else {
spec . Process . ApparmorProfile = defaultApparmorProfile
}
}
2016-04-16 23:05:09 +00:00
// set the CgroupsPath as this user
2016-04-18 04:13:18 +00:00
u , err := user . CurrentUser ( )
2016-04-16 23:05:09 +00:00
if err != nil {
logrus . Fatal ( err )
}
2016-04-18 04:13:18 +00:00
spec . Linux . CgroupsPath = sPtr ( u . Name )
// setup UID mappings
spec . Linux . UIDMappings = [ ] specs . IDMapping {
{
HostID : uint32 ( u . Uid ) ,
ContainerID : 0 ,
Size : 1 ,
} ,
}
// setup GID mappings
spec . Linux . GIDMappings = [ ] specs . IDMapping {
{
HostID : uint32 ( u . Gid ) ,
ContainerID : 0 ,
Size : 1 ,
} ,
}
2016-04-16 23:05:09 +00:00
2016-04-18 08:09:18 +00:00
if err := unpackRootfs ( spec , passkey ) ; err != nil {
2016-04-15 06:42:40 +00:00
logrus . Fatal ( err )
}
2016-04-17 01:23:42 +00:00
status , err := startContainer ( spec , containerID , pidFile , detach , useSystemdCgroup )
2016-04-15 06:42:40 +00:00
if err != nil {
logrus . Fatal ( err )
}
2016-04-15 08:54:02 +00:00
if err := os . RemoveAll ( defaultRootfsDir ) ; err != nil {
logrus . Warnf ( "removing rootfs failed: %v" , err )
}
2016-04-15 06:42:40 +00:00
// exit with the container's exit status
os . Exit ( status )
}
func runInit ( ) {
2016-04-15 08:54:02 +00:00
runtime . GOMAXPROCS ( 1 )
runtime . LockOSThread ( )
factory , _ := libcontainer . New ( "" )
if err := factory . StartInitialization ( ) ; err != nil {
// as the error is sent back to the parent there is no need to log
// or write it to stderr because the parent process will handle this
os . Exit ( 1 )
2016-04-15 06:42:40 +00:00
}
2016-04-15 08:54:02 +00:00
panic ( "libcontainer: container init failed to exec" )
2016-04-15 06:42:40 +00:00
}