14b8b7cad0d8fec7803277efeebcd51c731829b8
[sysadmin-cookbook] / recepies / lxc / lxc-watchdog.sh
1 #! /bin/sh
2 ### BEGIN INIT INFO
3 # Provides:          lxc-watchdog
4 # Required-Start:    $remote_fs $named $network $time
5 # Required-Stop:     $remote_fs $named $network
6 # Required-Start:    
7 # Required-Stop:     
8 # Default-Start:     2 3 4 5
9 # Default-Stop:      0 1 6
10 # Short-Description: Manage Linux Containers startup/shutdown
11 # Description:       Uses clever inotify hack to monitor container's
12 #                    halt/reboot events watching /var/run/utmp
13 ### END INIT INFO
14
15 # Author: Dobrica Pavlinusic <dpavlin@rot13.org>
16 #
17 # based on Tony Risinger post to lxc-users mailing list
18 # http://www.mail-archive.com/lxc-users@lists.sourceforge.net/msg00074.html
19 #
20 # Install with:
21 # ln -sf /srv/sysadmin-cookbook/recepies/lxc/lxc-watchdog.sh /etc/init.d/lxc-watchdog
22 # update-rc.d lxc-watchdog defaults
23
24
25 which inotifywait >/dev/null || apt-get install inotify-tools
26
27
28 lxc_exists() {
29         name=$1
30
31         if [ ! -e /var/lib/lxc/$name/config ] ; then
32                 echo "Usage: $0 name"
33                 lxc_status
34                 exit 1
35         fi
36 }
37
38
39 lxc_rootfs() {
40         grep '^ *lxc\.rootfs *=' "/var/lib/lxc/$1/config" | cut -d= -f2 | sed 's/^ *//'
41 }
42
43 lxc_hostname() {
44         inside=`cat $(lxc_rootfs $1)/etc/hostname`
45         config=`grep lxc.utsname /var/lib/lxc/$name/config | cut -d= -f2`
46         echo "$config [$inside]";
47 }
48
49 lxc_ip() {
50         ( grep lxc.network.ipv4 /var/lib/lxc/$name/config | cut -d= -f2 || \
51         grep address $(lxc_rootfs $name)/etc/network/interfaces | sed 's/.*address //' ) | \
52         sed -e 's/ *//g' -e 's/\/.*$//'
53 }
54
55 lxc_status() {
56         ( find /var/lib/lxc/ -name "config" | cut -d/ -f5 | sort -u | xargs -i lxc-info -n {} | sed "s/'//g" | while read name is status ; do
57                 boot="-"
58                 test -s /var/lib/lxc/$name/on_boot && boot="boot"
59                 echo "$name $status $boot $(lxc_rootfs $name) $(lxc_ip $name) $(lxc_hostname $name)"
60         done ) | column -t
61 }
62
63
64 cleanup_init_scripts() {
65         rootfs=$(lxc_rootfs $1)
66
67         ls \
68                 $rootfs/etc/rc?.d/*umountfs \
69                 $rootfs/etc/rc?.d/*umountroot \
70                 $rootfs/etc/rc?.d/*hwclock* \
71                 $rootfs/etc/rc?.d/*udev* \
72         2>/dev/null | xargs -i rm -v {}
73
74         echo $1 > $rootfs/etc/hostname
75         grep $1 $rootfs/etc/hosts || echo "$(lxc_ip $1) $1" >> $rootfs/etc/hosts
76 }
77
78
79 setup_inittab() {
80         rootfs=$(lxc_rootfs $1)
81         remove=$2
82         add=$3
83
84         # let container respond to kill -SIGPWR
85         inittab=$rootfs/etc/inittab
86         if test -e $inittab && ! grep "$add" ${inittab} >/dev/null ; then
87                 grep -v "$remove" ${inittab} > ${inittab}.new
88                 echo $add >> ${inittab}.new
89                 mv ${inittab}.new ${inittab}
90                 echo "$inittab modified with $add"
91         fi
92 }
93
94
95 lxc_log() {
96         echo `date +%Y-%m-%dT%H:%M:%S` $*
97 }
98
99
100 lxc_kill() {
101         name=$1
102         sig=$2
103
104         init_pid=`lxc-ps -C init -o pid | grep "^$name" | cut -d" " -f2-`
105         if [ -z "$init_pid" ] ; then
106                 lxc-info -n $name
107                 exit 1
108         fi
109         lxc_log "$name kill $sig $init_pid"
110         /bin/kill $sig $init_pid
111 }
112
113 lxc_stop() {
114         lxc_log "$name stop"
115         lxc_kill $name -SIGPWR
116         lxc-wait -n $name -s STOPPED
117         lxc_log "$name stoped"
118 #       rm -f /var/lib/lxc/${name}/on_boot
119 }
120
121
122 lxc_start() {
123         name=$1
124         rootfs=$(lxc_rootfs $1)
125
126         if [ ! -e $rootfs ] ; then
127                 echo "ERROR $name rootfs $rootfs not found"
128                 return
129         fi
130
131         if ! lxc-info -n $name | grep RUNNING ; then
132                 lxc_log "$name start"
133                 lxc-start -n $name -o /tmp/${name}.log -d
134                 lxc-wait  -n $name -s RUNNING
135                 lxc-info  -n $name
136                 test -f /var/lib/lxc/${name}/on_boot || echo $name > /var/lib/lxc/${name}/on_boot
137         fi
138 }
139
140 lxc_watchdog() {
141 name=$1
142 rootfs=$(lxc_rootfs $1)
143
144 while true; do
145         vps_utmp=${rootfs}/var/run/utmp
146         tasks=`wc -l < /cgroup/${name}/tasks`
147         test -z "$tasks" && exit 1
148         if [ "$tasks" -eq 1 ]; then
149
150                 runlevel="$(runlevel ${vps_utmp})"
151                 lxc_log "$name runlevel $runlevel"
152
153                 case $runlevel in
154                 N*)
155                         # nothing for new boot state
156                 ;;
157                 ??0)
158                         lxc_log "$name halt"
159                         lxc-stop -n "${name}"
160                         lxc-wait -n ${name} -s STOPPED
161                         break
162                 ;;
163                 ??6)
164                         lxc_log "$name reboot";
165                         lxc-stop -n ${name}
166                         lxc-wait -n ${name} -s STOPPED
167                         lxc-start -d -n ${name} -o /tmp/${name}.log
168                 ;;
169                 *)
170                         # make sure vps is still running
171                         state="$(lxc-info -n "${name}" | sed -e 's/.* is //')"
172                         [ "$state" = "RUNNING" ] || break
173                 ;;
174                 esac
175         else
176                 lxc_log "$name $tasks tasks"
177         fi
178
179         # time of 5 minutes on it JUST IN CASE...
180         inotifywait -qqt 300 ${vps_utmp}
181 done
182
183 lxc_log "$name watchdog exited"
184
185 }
186
187
188 usage() {
189         echo "Usage: $0 {start|stop|restart|status|boot|disable} [name name ... ]" >&2
190         exit 3
191 }
192
193 command_on_lxc() {
194 command=$1
195 shift
196
197 echo "# $command $1"
198
199 case "$command" in
200
201 start)
202         lxc_exists $1
203         cleanup_init_scripts $1
204         setup_inittab $1 ":respawn:/sbin/getty.*tty1"   "c1:12345:respawn:/sbin/getty 38400 tty1 linux"
205         setup_inittab $1 "::power"                      "p0::powerfail:/sbin/init 0"
206         setup_inittab $1 "::ctrlaltdel"                 "p6::ctrlaltdel:/sbin/init 6"
207         lxc_start $1
208         # give container 5 seconds to start more than one process
209         ( sleep 5 ; nohup $0 watchdog $1 >> /tmp/$1.log 2>/dev/null ) &
210         ;;
211 stop|halt)
212         lxc_exists $1
213         lxc_stop $1
214         ;;
215 reload|force-reload|restart|reboot)
216         lxc_kill $1 -SIGINT
217         ;;
218 watchdog)
219         lxc_watchdog $1
220         ;;
221 boot)
222         echo $1 > /var/lib/lxc/$1/on_boot
223         ;;
224 disable)
225         echo -n > /var/lib/lxc/$1/on_boot
226         ;;
227 *)
228         usage
229         ;;
230
231 esac
232
233 }
234
235 command=$1
236 test -z "$command" && usage
237 test "$command" = "status" && lxc_status && exit
238 shift
239
240 if [ -z "$1" ] ; then
241         ls /var/lib/lxc/*/on_boot | while read path ; do
242                 name=`echo $path | cut -d/ -f5`
243                 if [ "$command" != "start" -o "$command" = "start" -a -s $path ] ; then
244                         command_on_lxc $command $name
245                 else
246                         echo "# skip $command $name"
247                 fi
248         done
249 else
250         while [ ! -z "$1" ] ; do
251                 command_on_lxc $command $1
252                 shift
253         done
254 fi
255