kameleon-builder 2.0.0.dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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