kameleon-builder 2.0.0.dev

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.editorconfig +23 -0
  2. data/.env +51 -0
  3. data/.gitignore +22 -0
  4. data/AUTHORS +19 -0
  5. data/CHANGELOG +36 -0
  6. data/COPYING +340 -0
  7. data/Gemfile +4 -0
  8. data/README.md +53 -0
  9. data/Rakefile +24 -0
  10. data/Vagrantfile +68 -0
  11. data/bin/kameleon +16 -0
  12. data/contrib/kameleon_bashrc.sh +138 -0
  13. data/contrib/scripts/VirtualBox_deploy.sh +12 -0
  14. data/contrib/scripts/chroot_env +9 -0
  15. data/contrib/scripts/create_passwd.py +17 -0
  16. data/contrib/scripts/umount-chroot.sh +290 -0
  17. data/contrib/steps/bootstrap/debian/bootstrap_if_needed.yaml +47 -0
  18. data/contrib/steps/bootstrap/debian/bootstrap_static.yaml +38 -0
  19. data/contrib/steps/setup/add_timestamp.yaml +6 -0
  20. data/contrib/steps/setup/autologin.yaml +16 -0
  21. data/contrib/steps/setup/copy_ssh_auth_file.yaml +10 -0
  22. data/contrib/steps/setup/debian/add_network_interface.yaml +7 -0
  23. data/contrib/steps/setup/debian/cluster_tools_install.yaml +16 -0
  24. data/contrib/steps/setup/debian/network_config_static.yaml +17 -0
  25. data/contrib/steps/setup/generate_user_ssh_key.yaml +15 -0
  26. data/contrib/steps/setup/install_my_ssh_key.yaml +26 -0
  27. data/contrib/steps/setup/make_swap_file.yaml +9 -0
  28. data/contrib/steps/setup/root_ssh_config.yaml +18 -0
  29. data/contrib/steps/setup/set_user_password.yaml +7 -0
  30. data/contrib/steps/setup/system_optimization.yaml +8 -0
  31. data/docs/.gitignore +1 -0
  32. data/docs/Makefile +177 -0
  33. data/docs/make.bat +242 -0
  34. data/docs/source/_static/.gitignore +0 -0
  35. data/docs/source/aliases.rst +29 -0
  36. data/docs/source/checkpoint.rst +28 -0
  37. data/docs/source/cli.rst +3 -0
  38. data/docs/source/commands.rst +62 -0
  39. data/docs/source/conf.py +254 -0
  40. data/docs/source/context.rst +42 -0
  41. data/docs/source/faq.rst +3 -0
  42. data/docs/source/getting_started.rst +3 -0
  43. data/docs/source/index.rst +38 -0
  44. data/docs/source/installation.rst +3 -0
  45. data/docs/source/recipe.rst +256 -0
  46. data/docs/source/why.rst +3 -0
  47. data/docs/source/workspace.rst +11 -0
  48. data/kameleon-builder.gemspec +37 -0
  49. data/lib/kameleon.rb +75 -0
  50. data/lib/kameleon/cli.rb +176 -0
  51. data/lib/kameleon/context.rb +83 -0
  52. data/lib/kameleon/engine.rb +357 -0
  53. data/lib/kameleon/environment.rb +38 -0
  54. data/lib/kameleon/error.rb +51 -0
  55. data/lib/kameleon/logger.rb +53 -0
  56. data/lib/kameleon/recipe.rb +474 -0
  57. data/lib/kameleon/shell.rb +290 -0
  58. data/lib/kameleon/step.rb +213 -0
  59. data/lib/kameleon/utils.rb +45 -0
  60. data/lib/kameleon/version.rb +3 -0
  61. data/templates/COPYRIGHT +21 -0
  62. data/templates/aliases/defaults.yaml +83 -0
  63. data/templates/checkpoints/docker.yaml +14 -0
  64. data/templates/checkpoints/qcow2.yaml +44 -0
  65. data/templates/debian-wheezy-chroot.yaml +98 -0
  66. data/templates/debian-wheezy-docker.yaml +97 -0
  67. data/templates/fedora-docker.yaml +96 -0
  68. data/templates/steps/bootstrap/debian/debootstrap.yaml +13 -0
  69. data/templates/steps/bootstrap/fedora/docker_bootstrap.yaml +25 -0
  70. data/templates/steps/bootstrap/fedora/yum_bootstrap.yaml +22 -0
  71. data/templates/steps/bootstrap/prepare_appliance_with_nbd.yaml +93 -0
  72. data/templates/steps/bootstrap/prepare_docker.yaml +38 -0
  73. data/templates/steps/bootstrap/start_chroot.yaml +53 -0
  74. data/templates/steps/bootstrap/start_docker.yaml +12 -0
  75. data/templates/steps/export/build_appliance_from_docker.yaml +105 -0
  76. data/templates/steps/export/clean_appliance.yaml +3 -0
  77. data/templates/steps/export/save_appliance_from_nbd.yaml +54 -0
  78. data/templates/steps/setup/create_user.yaml +12 -0
  79. data/templates/steps/setup/debian/kernel_install.yaml +20 -0
  80. data/templates/steps/setup/debian/keyboard_config.yaml +10 -0
  81. data/templates/steps/setup/debian/network_config.yaml +30 -0
  82. data/templates/steps/setup/debian/software_install.yaml +15 -0
  83. data/templates/steps/setup/debian/system_config.yaml +12 -0
  84. data/templates/steps/setup/fedora/kernel_install.yaml +27 -0
  85. data/templates/steps/setup/fedora/software_install.yaml +10 -0
  86. data/tests/helper.rb +22 -0
  87. data/tests/recipes/dummy_recipe.yaml +48 -0
  88. data/tests/recipes/steps/bootstrap/dummy_distro/dummy_bootstrap_static.yaml +4 -0
  89. data/tests/recipes/steps/export/dummy_save_appliance.yaml +9 -0
  90. data/tests/recipes/steps/setup/default/dummy_root_passwd.yaml +8 -0
  91. data/tests/recipes/steps/setup/dummy_distro/dummy_software_install.yaml +7 -0
  92. data/tests/test_context.rb +16 -0
  93. data/tests/test_recipe.rb +15 -0
  94. data/tests/test_version.rb +9 -0
  95. metadata +300 -0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kameleon.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Kameleon
2
+
3
+ Kameleon should be seen as a simple but powerful tool to generate customized
4
+ appliances. With Kameleon, you make your recipe that describes how to create
5
+ step by step your own distribution. At start Kameleon is used to create custom
6
+ kvm, LXC, VirtualBox, iso images, ..., but as it is designed to be very
7
+ generic you can probably do a lot more than that.
8
+
9
+ ## Installation
10
+ Simply install it from the Gem repository (not working yet):
11
+
12
+ gem install kameleon
13
+
14
+ Or from source:
15
+
16
+ git clone git://scm.gforge.inria.fr/kameleon/kameleon.git
17
+ cd kameleon
18
+ gem build kameleon.gemspec
19
+ gem install kameleon-<version>.gem
20
+
21
+ ## Usage
22
+
23
+ Just type:
24
+
25
+ kameleon
26
+
27
+ ## Quick start
28
+
29
+ First, you should select a template. To see the available templates use:
30
+
31
+ kameleon templates
32
+
33
+ Then, create a new recipe from the template you've just choose. This will
34
+ create a `recipes` folder in the current directory. (use `-w` option to set a
35
+ different workspace).
36
+
37
+ kameleon new my_test_recipe -t template_name
38
+
39
+ Then build your new recipe with the build command:
40
+
41
+ kameleon build my_test_recipe
42
+
43
+ A `builds` directory was created and contains your new image!
44
+
45
+ To go further, get more documentation in the docs folder.
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork it
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ begin
5
+ require 'coveralls/rake/task'
6
+
7
+ Coveralls::RakeTask.new
8
+ task :ci => ['set_coverage', 'test', 'coveralls:push']
9
+ rescue LoadError; end
10
+
11
+
12
+ task :set_coverage do
13
+ ENV['COVERAGE'] = 'true'
14
+ end
15
+
16
+
17
+ Rake::TestTask.new do |t|
18
+ t.libs << 'tests'
19
+ t.pattern = "tests/test_*.rb"
20
+ end
21
+
22
+
23
+ desc 'Default task which runs all tests with code coverage enabled'
24
+ task :default => ['set_coverage', 'test']
data/Vagrantfile ADDED
@@ -0,0 +1,68 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ ENV['VAGRANT_DEFAULT_PROVIDER'] = 'libvirt'
5
+
6
+ Vagrant.configure("2") do |config|
7
+ config.vm.box = "debian7-dev"
8
+ config.vm.box_url = "http://cdn.quicker.fr/vagrant/libvirt/debian7-dev.box"
9
+ config.vm.hostname = "kameleon-devel"
10
+
11
+ # Config provider
12
+ config.vm.provider :libvirt do |vm|
13
+ vm.memory = 2024
14
+ vm.cpus = 2
15
+ end
16
+
17
+ # shared folders
18
+ config.vm.synced_folder ".", "/vagrant", :nfs => true
19
+
20
+ # Provision
21
+ config.vm.provision "shell", privileged: true, inline: <<-EOF
22
+ export DEBIAN_FRONTEND=noninteractive
23
+ apt-get update
24
+ apt-get -y --force-yes install git python-pip debootstrap \
25
+ rsync sed qemu-utils
26
+
27
+ apt-get -y --force-yes install ruby1.9.1 ruby1.9.1-dev \
28
+ rubygems1.9.1 irb1.9.1 ri1.9.1 rdoc1.9.1 \
29
+ build-essential libopenssl-ruby1.9.1 libssl-dev zlib1g-dev
30
+
31
+ update-alternatives --install /usr/bin/ruby ruby /usr/bin/ruby1.9.1 400 \
32
+ --slave /usr/share/man/man1/ruby.1.gz ruby.1.gz \
33
+ /usr/share/man/man1/ruby1.9.1.1.gz \
34
+ --slave /usr/bin/ri ri /usr/bin/ri1.9.1 \
35
+ --slave /usr/bin/irb irb /usr/bin/irb1.9.1 \
36
+ --slave /usr/bin/rdoc rdoc /usr/bin/rdoc1.9.1
37
+
38
+ # choose your interpreter
39
+ # changes symlinks for /usr/bin/ruby , /usr/bin/gem
40
+ # /usr/bin/irb, /usr/bin/ri and man (1) ruby
41
+ update-alternatives --set ruby /usr/bin/ruby1.9.1
42
+
43
+ gem install bundle
44
+
45
+ # Helpful tools
46
+ pip install pyped
47
+ EOF
48
+
49
+ config.vm.provision "shell", privileged: false, inline: <<-EOF
50
+ cat > ~/.bash_profile <<< "
51
+ export FORCE_AUTOENV=1
52
+ source ~/.profile
53
+ source /vagrant/.env
54
+ cd /vagrant
55
+ "
56
+ cd /vagrant && git stash && bundle install && git stash pop
57
+ EOF
58
+
59
+ # shared folders
60
+ if File.exists? File.expand_path('~/.dotfiles')
61
+ config.vm.synced_folder "~/.dotfiles", "/home/vagrant/.dotfiles", :nfs => true
62
+ config.vm.provision "shell", privileged: false, inline: "python /home/vagrant/.dotfiles/install.py"
63
+ end
64
+
65
+ # Network
66
+ config.ssh.forward_agent = true
67
+ config.vm.network :private_network, ip: "10.10.10.120"
68
+ end
data/bin/kameleon ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Exit cleanly from an early interrupt
4
+ Signal.trap("INT") { exit 1 }
5
+
6
+ # Stdout/stderr should not buffer output
7
+ $stdout.sync = true
8
+ $stderr.sync = true
9
+
10
+ require 'kameleon'
11
+
12
+ require 'kameleon/cli'
13
+ # Force Thor to raise exceptions so we can exit non-zero.
14
+ ENV["THOR_DEBUG"] = "1"
15
+
16
+ Kameleon.with_friendly_errors { Kameleon::CLI.start }
@@ -0,0 +1,138 @@
1
+ # If not running interactively, don't do anything
2
+ export USER=${USER:-"root"}
3
+ export HOME=${HOME:-"/root"}
4
+ export PATH=/usr/bin:/usr/sbin:/bin:/sbin:$PATH
5
+ export LC_ALL=${LC_ALL:-"POSIX"}
6
+
7
+ export DEBIAN_FRONTEND=noninteractive
8
+
9
+ mkdir -p $(dirname <%= @bash_history_file %>) ; touch "<%= @bash_history_file %>"
10
+ mkdir -p $(dirname <%= @bash_env_file %>) ; touch "<%= @bash_env_file %>"
11
+
12
+ source /etc/bash.bashrc 2> /dev/null
13
+
14
+ export KAMELEON_CONTEXT_NAME="<%= @context_name %>_context"
15
+ export HISTFILE="<%= @bash_history_file %>"
16
+
17
+ ## functions
18
+
19
+ function fail {
20
+ echo $@ 1>&2
21
+ false
22
+ }
23
+
24
+ ## aliases
25
+ if [ -t 1 ] ; then
26
+ # restore previous env
27
+ source "<%= @bash_env_file %>" 2> /dev/null
28
+ export TERM=xterm
29
+ # for fast typing
30
+ alias h='history'
31
+ alias g='git status'
32
+ alias l='ls -lah'
33
+ alias ll='ls -lh'
34
+ alias la='ls -Ah'
35
+
36
+ # for human readable output
37
+ alias ls='ls -h'
38
+ alias df='df -h'
39
+ alias du='du -h'
40
+
41
+ # simple history browsing
42
+ export HISTCONTROL=erasedups
43
+ export HISTSIZE=10000
44
+ export HISTIGNORE="history*"
45
+ shopt -s histappend
46
+ bind '"\e[A"':history-search-backward
47
+ bind '"\e[B"':history-search-forward
48
+
49
+ # check the window size after each command and, if necessary,
50
+ # update the values of LINES and COLUMNS.
51
+ shopt -s checkwinsize
52
+
53
+ # make less more friendly for non-text input files, see lesspipe(1)
54
+ [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
55
+
56
+ # If this is an xterm set the title to user@host:dir
57
+ PROMPT_COMMAND='echo -ne "\033]0;${KAMELEON_CONTEXT_NAME:+($KAMELEON_CONTEXT_NAME)}${USER}@${HOSTNAME}: ${PWD}\007"'
58
+
59
+ # set variable to show git branch when in a git repository
60
+ # source: https://github.com/jimeh/git-aware-prompt/blob/master/prompt.sh
61
+ # added highlighting of repo part in path
62
+ function find_git_branch {
63
+ git_subpath='/'
64
+ local dir=${PWD} head
65
+ until [ "$dir" = "" ]; do
66
+ if [ -f "$dir/.git/HEAD" ]; then
67
+ head=$(< "$dir/.git/HEAD")
68
+ if [[ $head == ref:\ refs/heads/* ]]; then
69
+ git_branch=" (${head#*/*/})"
70
+ elif [[ $head != '' ]]; then
71
+ git_describe=$(git describe --always)
72
+ git_branch=" (detached: $git_describe)"
73
+ else
74
+ git_branch=' (unknown)'
75
+ fi
76
+ prompt_dir="${dir/$HOME/~}"
77
+ return
78
+ fi
79
+ git_subpath="/${dir##*/}$git_subpath"
80
+ dir="${dir%/*}"
81
+ done
82
+ git_branch=''
83
+ prompt_dir="${PWD/$HOME/~}"
84
+ git_subpath=''
85
+ }
86
+ function find_git_dirty {
87
+ st=$(git status -s 2>/dev/null | tail -n 1)
88
+ if [[ $st == "" ]]; then
89
+ git_dirty=''
90
+ else
91
+ git_dirty='*'
92
+ fi
93
+ }
94
+ export find_git_branch
95
+ export find_git_dirty
96
+ PROMPT_COMMAND="find_git_branch; find_git_dirty; history -a ; $PROMPT_COMMAND"
97
+
98
+ # set a fancy prompt (non-color, unless we know we "want" color)
99
+ case "$TERM" in
100
+ xterm-color) color_prompt=yes;;
101
+ esac
102
+
103
+ # if the terminal has the capability; turned
104
+ # off by default to not distract the user: the focus in a terminal window
105
+ # should be on the output of commands, not on the prompt
106
+ force_color_prompt=yes
107
+
108
+ if [ -n "$force_color_prompt" ]; then
109
+ if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
110
+ # We have color support; assume it's compliant with Ecma-48
111
+ # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
112
+ # a case would tend to support setf rather than setaf.)
113
+ color_prompt=yes
114
+ else
115
+ color_prompt=
116
+ fi
117
+ fi
118
+
119
+ if [ "$color_prompt" = yes ]; then
120
+ PS1='${KAMELEON_CONTEXT_NAME:+($KAMELEON_CONTEXT_NAME) }\[\033[01;32m\]\u@\h\[\033[00m\]: \[\e[1;37m\]$prompt_dir\[\e[1;36m\]$git_subpath\[\e[0;31m\]$git_branch\[\e[1;33m\]$git_dirty\[\033[01;34m\] \$\[\033[00m\] '
121
+ else
122
+ PS1='${KAMELEON_CONTEXT_NAME:+($KAMELEON_CONTEXT_NAME) }\u@\h: $prompt_dir$git_subpath$git_branch$git_dirty \$ '
123
+ fi
124
+
125
+ # colors
126
+ if [ -x /usr/bin/dircolors ]; then
127
+ eval "`dircolors -b`"
128
+ alias ls='ls --color=auto'
129
+ #alias dir='dir --color=auto'
130
+ #alias vdir='vdir --color=auto'
131
+
132
+ alias grep='grep --color=auto'
133
+ alias fgrep='fgrep --color=auto'
134
+ alias egrep='egrep --color=auto'
135
+ else
136
+ alias ls='ls -G'
137
+ fi
138
+ fi
@@ -0,0 +1,12 @@
1
+ #!/bin/bash
2
+ NAME=$1
3
+ VBOX_DISK=$2
4
+
5
+ VBoxManage createvm --name $NAME --register
6
+ VBoxManage modifyvm $NAME --memory 512
7
+ VBoxManage storagectl $NAME --name SATA --add sata --controller IntelAhci --bootable on --sataportcount 1
8
+ VBoxManage storageattach $NAME --storagectl SATA --port 0 --device 0 --type hdd --medium $VBOX_DISK
9
+ #VBoxManage modifyvm $NAME --nic1 hostonly
10
+ #VBoxManage modifyvm $NAME --nic1 nat
11
+ VBoxManage startvm $NAME
12
+ #VBoxManage unregistervm $NAME --delete
@@ -0,0 +1,9 @@
1
+ #kameleon chroot base bash environnement
2
+
3
+ umask 022
4
+
5
+ export USER=root
6
+ export HOME=/root
7
+ export PATH=/usr/bin:/usr/sbin:/bin:/sbin
8
+ export LC_ALL=POSIX
9
+
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env python3
2
+ ### WARNING ###
3
+ # requires Python >= 3.3
4
+
5
+ import sys, crypt, getpass;
6
+
7
+ # this script generate a password using salted SHA512 whitch is the default on debian wheezy
8
+
9
+ cleartext = getpass.getpass("Password:")
10
+ cleartext2 = getpass.getpass("Again:")
11
+ if cleartext2 != cleartext:
12
+ print ('Not matched!')
13
+ sys.exit(1)
14
+
15
+ salt = crypt.mksalt(crypt.METHOD_SHA512)
16
+ print (crypt.crypt(cleartext, salt))
17
+
@@ -0,0 +1,290 @@
1
+ #!/bin/sh -e
2
+ # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3
+ # Use of this source code is governed by a BSD-style license that can be
4
+ # found in the LICENSE file.
5
+
6
+ APPLICATION="${0##*/}"
7
+ ALLCHROOTS=''
8
+ BINDIR="`dirname "\`readlink -f "$0"\`"`"
9
+ CHROOTS="`readlink -f "$BINDIR/../chroots"`"
10
+ EXCLUDEROOT=''
11
+ FORCE=''
12
+ PRINT=''
13
+ SIGNAL='TERM'
14
+ TRIES=5
15
+ YES=''
16
+
17
+ USAGE="$APPLICATION [options] name [...]
18
+
19
+ Unmounts one or more chroots, optionally killing any processes still running
20
+ inside them.
21
+
22
+ By default, it will run in interactive mode where it will ask to kill any
23
+ remaining processes if unable to unmount the chroot within 5 seconds.
24
+
25
+ Options:
26
+ -a Unmount all chroots in the CHROOTS directory.
27
+ -c CHROOTS Directory the chroots are in. Default: $CHROOTS
28
+ -f Forces a chroot to unmount, potentially breaking or killing
29
+ other instances of the same chroot.
30
+ -k KILL Send the processes SIGKILL instead of SIGTERM.
31
+ -p Print to STDOUT the processes stopping a chroot from unmounting.
32
+ -t TRIES Number of seconds to try before signalling the processes.
33
+ Use -t inf to be exceedingly patient. Default: $TRIES
34
+ -x Keep the root directory of the chroot mounted.
35
+ -y Signal any remaining processes without confirmation.
36
+ Automatically escalates from SIGTERM to SIGKILL."
37
+
38
+ # Function to exit with exit code $1, spitting out message $@ to stderr
39
+ error() {
40
+ local ecode="$1"
41
+ shift
42
+ echo "$*" 1>&2
43
+ exit "$ecode"
44
+ }
45
+
46
+ # Process arguments
47
+ while getopts 'ac:fkpt:xy' f; do
48
+ case "$f" in
49
+ a) ALLCHROOTS='y';;
50
+ c) CHROOTS="`readlink -f "$OPTARG"`";;
51
+ f) FORCE='y';;
52
+ k) SIGNAL="KILL";;
53
+ p) PRINT='y';;
54
+ t) TRIES="$OPTARG";;
55
+ x) EXCLUDEROOT='y';;
56
+ y) YES='a';;
57
+ \?) error 2 "$USAGE";;
58
+ esac
59
+ done
60
+ shift "$((OPTIND-1))"
61
+
62
+ # Need at least one chroot listed, or -a; not both.
63
+ if [ $# = 0 -a -z "$ALLCHROOTS" ] || [ ! $# = 0 -a -n "$ALLCHROOTS" ]; then
64
+ error 2 "$USAGE"
65
+ fi
66
+
67
+ # Make sure TRIES is valid
68
+ if [ "$TRIES" = inf ]; then
69
+ TRIES=-1
70
+ elif [ "$TRIES" -lt -1 ]; then
71
+ error 2 "$USAGE"
72
+ fi
73
+
74
+ # We need to run as root
75
+ if [ ! "$USER" = root -a ! "$UID" = 0 ]; then
76
+ error 2 "$APPLICATION must be run as root."
77
+ fi
78
+
79
+ # Check if a chroot is running with this directory. We detect the
80
+ # appropriate commands by checking if the command's parent root is not equal
81
+ # to the pid's root. This avoids not unmounting due to a lazy-quitting
82
+ # background application within the chroot. We also don't consider processes
83
+ # that have a parent PID of 1 (which would mean an orphaned process in this
84
+ # case), as enter-chroot never orphans its children, and we don't consider
85
+ # processes that have CROUTON=CORE in the environment.
86
+ # $1: $base; the canonicalized base path of the chroot
87
+ # Returns: non-zero if the chroot is in use.
88
+ checkusage() {
89
+ if [ -n "$FORCE" ]; then
90
+ return 0
91
+ fi
92
+ local b="${1%/}/" pid ppid proot prootdir root rootdir
93
+ for root in /proc/*/root; do
94
+ if [ ! -r "$root" ]; then
95
+ continue
96
+ fi
97
+ rootdir="`readlink -f "$root"`"
98
+ rootdir="${rootdir%/}/"
99
+ if [ "${rootdir#"$b"}" = "$rootdir" ]; then
100
+ continue
101
+ fi
102
+ pid="${root#/proc/}"
103
+ pid="${pid%/root}"
104
+ ppid="`ps -p "$pid" -o ppid= 2>/dev/null | sed 's/ //g'`"
105
+ if [ -z "$ppid" ] || [ "$ppid" -eq 1 ]; then
106
+ continue
107
+ fi
108
+ proot="/proc/$ppid/root"
109
+ if [ -r "$proot" ]; then
110
+ prootdir="`readlink -f "$proot"`"
111
+ if [ "${prootdir%/}/" = "$rootdir" ]; then
112
+ continue
113
+ fi
114
+ fi
115
+ if grep -q 'CROUTON=CORE' "/proc/$pid/environ" 2>/dev/null; then
116
+ continue
117
+ fi
118
+ if [ -n "$PRINT" ]; then
119
+ ps -p "$pid" -o pid= -o cmd= || true
120
+ fi
121
+ return 1
122
+ done
123
+ return 0
124
+ }
125
+
126
+ # If we specified all chroots, bring in all chroots.
127
+ if [ -n "$ALLCHROOTS" ]; then
128
+ set -- "$CHROOTS/"*
129
+ fi
130
+
131
+ # Follows and fixes dangerous symlinks, returning the canonicalized path.
132
+ fixabslinks() {
133
+ local p="$CHROOT/$1" c
134
+ # Follow and fix dangerous absolute symlinks
135
+ while c="`readlink -m "$p"`" && [ ! "$c" = "$p" ]; do
136
+ p="$CHROOT${c#"$CHROOT"}"
137
+ done
138
+ echo "$p"
139
+ }
140
+
141
+ # Unmount each chroot
142
+ ret=0
143
+ for NAME in "$@"; do
144
+ if [ -z "$NAME" ]; then
145
+ continue
146
+ fi
147
+
148
+ NAME="${NAME#"$CHROOTS/"}"
149
+
150
+ # Check for existence
151
+ CHROOT="$CHROOTS/$NAME"
152
+ if [ ! -d "$CHROOT" ]; then
153
+ echo "$CHROOT not found." 1>&2
154
+ ret=1
155
+ continue
156
+ fi
157
+
158
+ # Switch to the unencrypted mount for encrypted chroots.
159
+ if [ -f "$CHROOT/.ecryptfs" ]; then
160
+ CHROOT="$CHROOTS/.secure/$NAME"
161
+ fi
162
+
163
+ base="`readlink -f "$CHROOT"`"
164
+
165
+ if ! checkusage "$base"; then
166
+ echo "Not unmounting $CHROOT as another instance is using it." 1>&2
167
+ ret=1
168
+ continue
169
+ fi
170
+
171
+ # Kill the chroot's system dbus if one is running; failure is fine
172
+ env -i chroot "$CHROOT" su -s '/bin/sh' -c '
173
+ pidfile="/var/run/dbus/pid"
174
+ if [ ! -f "$pidfile" ]; then
175
+ exit 0
176
+ fi
177
+ pid="`cat "$pidfile"`"
178
+ if ! grep -q "^dbus-daemon" "/proc/$pid/cmdline" 2>/dev/null; then
179
+ exit 0
180
+ fi
181
+ kill $pid' - root 2>/dev/null || true
182
+
183
+ # Unmount all mounts
184
+ ntries=0
185
+ if [ -z "$EXCLUDEROOT" ]; then
186
+ echo "Unmounting $CHROOT..." 1>&2
187
+ else
188
+ echo "Pruning $CHROOT mounts..." 1>&2
189
+ fi
190
+ baseesc="`echo "$base" | sed 's= =//=g'`"
191
+
192
+ # Define the mountpoint filter to only unmount specific mounts.
193
+ # The filter is run on the escaped version of the mountpoint.
194
+ filter() {
195
+ if [ -z "$EXCLUDEROOT" ]; then
196
+ grep "^$baseesc\\(/.*\\)\\?\$"
197
+ else
198
+ # Don't include the base directory
199
+ grep "^$baseesc/."
200
+ fi
201
+ }
202
+
203
+ # Sync for safety
204
+ sync
205
+
206
+ # Make sure the chroot's system media bind-mount is marked as slave to avoid
207
+ # unmounting devices system-wide. We still want to unmount locally-mounted
208
+ # media, though.
209
+ media="`fixabslinks '/var/host/media'`"
210
+ if mountpoint -q "$media"; then
211
+ mount --make-rslave "$media"
212
+ fi
213
+
214
+ while ! sed "s=\\\\040=//=g" /proc/mounts | cut -d' ' -f2 \
215
+ | filter | sed 's=//= =g' | xargs --no-run-if-empty -d '
216
+ ' -n 50 umount 2>/dev/null; do
217
+ if [ "$ntries" -eq "$TRIES" ]; then
218
+ # Send signal to all processes running under the chroot
219
+ # ...but confirm first.
220
+ printonly=''
221
+ if [ "${YES#[Aa]}" = "$YES" ]; then
222
+ echo -n "Failed to unmount $CHROOT. Kill processes? [a/k/y/p/N] " 1>&2
223
+ read YES
224
+ if [ ! "${YES#[Kk]}" = "$YES" ]; then
225
+ SIGNAL='KILL'
226
+ elif [ ! "${YES#[Pp]}" = "$YES" ]; then
227
+ printonly=y
228
+ elif [ "${YES#[AaYy]}" = "$YES" ]; then
229
+ echo "Skipping unmounting of $CHROOT" 1>&2
230
+ ret=1
231
+ break
232
+ fi
233
+ fi
234
+ if [ -z "$printonly" ]; then
235
+ echo "Sending SIG$SIGNAL to processes under $CHROOT..." 1>&2
236
+ fi
237
+ for root in /proc/*/root; do
238
+ if [ ! -r "$root" ] \
239
+ || [ ! "`readlink -f "$root"`" = "$base" ]; then
240
+ continue
241
+ fi
242
+ pid="${root#/proc/}"
243
+ pid="${pid%/root}"
244
+ if [ -z "$FORCE" ] \
245
+ && grep -q 'CROUTON=CORE' \
246
+ "/proc/$pid/environ" 2>/dev/null; then
247
+ continue
248
+ fi
249
+ if [ -n "${printonly:-"$PRINT"}" ]; then
250
+ ps -p "$pid" -o pid= -o cmd= || true
251
+ fi
252
+ if [ -z "$printonly" ]; then
253
+ kill "-$SIGNAL" "$pid" 2>/dev/null || true
254
+ fi
255
+ done
256
+
257
+ # Escalate
258
+ if [ ! "${YES#[Aa]}" = "$YES" ]; then
259
+ SIGNAL='KILL'
260
+ fi
261
+
262
+ if [ -z "$printonly" ]; then
263
+ ntries=0
264
+ fi
265
+ else
266
+ ntries="$((ntries+1))"
267
+ fi
268
+ sleep 1
269
+ if ! checkusage "$base"; then
270
+ echo "Aborting unmounting $CHROOT as another instance has begun using it." 1>&2
271
+ ret=1
272
+ break
273
+ fi
274
+ done
275
+
276
+ # More sync for more safety
277
+ sync
278
+ done
279
+
280
+ # Re-disable USB persistence (the Chromium OS default) if we no longer
281
+ # have chroots running with a root in removable media
282
+ if checkusage /media; then
283
+ for usbp in /sys/bus/usb/devices/*/power/persist; do
284
+ if [ -e "$usbp" ]; then
285
+ echo 0 > "$usbp"
286
+ fi
287
+ done
288
+ fi
289
+
290
+ exit $ret