Created
March 24, 2014 18:22
-
-
Save nelsnelson/9746073 to your computer and use it in GitHub Desktop.
Attempt at a minimal lxc template
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# lxc: linux Container library | |
# Authors: | |
# Daniel Lezcano <[email protected]> | |
# Nels Nelson <[email protected]> | |
# This library is free software; you can redistribute it and/or | |
# modify it under the terms of the GNU Lesser General Public | |
# License as published by the Free Software Foundation; either | |
# version 2.1 of the License, or (at your option) any later version. | |
# This library is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
# Lesser General Public License for more details. | |
# You should have received a copy of the GNU Lesser General Public | |
# License along with this library; if not, write to the Free Software | |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
# Detect use under userns (unsupported) | |
for arg in "$@"; do | |
[ "$arg" = "--" ] && break | |
if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then | |
echo "This template can't be used for unprivileged containers." 1>&2 | |
echo "You may want to try the \"download\" template instead." 1>&2 | |
exit 1 | |
fi | |
done | |
# Make sure the usual locations are in PATH | |
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin | |
install_minimal() | |
{ | |
rootfs=$1 | |
tree="\ | |
$rootfs/var/run/minimal \ | |
$rootfs/var/empty/minimal \ | |
$rootfs/var/lib/empty/minimal \ | |
$rootfs/etc/init.d \ | |
$rootfs/etc/rc.d \ | |
$rootfs/etc/sysconfig/network-scripts \ | |
$rootfs/dev/shm \ | |
$rootfs/run/shm \ | |
$rootfs/proc \ | |
$rootfs/sys \ | |
$rootfs/bin \ | |
$rootfs/sbin \ | |
$rootfs/usr \ | |
$rootfs/tmp \ | |
$rootfs/home \ | |
$rootfs/root \ | |
$rootfs/lib \ | |
$rootfs/lib64" | |
mkdir -p $tree | |
if [ $? -ne 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
configure_minimal() | |
{ | |
rootfs=$1 | |
cat <<EOF > $rootfs/etc/passwd | |
root:x:0:0:root:/root:/bin/bash | |
EOF | |
cat <<EOF > $rootfs/etc/group | |
root:x:0:root | |
EOF | |
return 0 | |
} | |
copy_configuration() | |
{ | |
path=$1 | |
rootfs=$2 | |
name=$3 | |
grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config | |
cat <<EOF >> $path/config | |
lxc.utsname = $name | |
lxc.pts = 1024 | |
lxc.kmsg = 0 | |
lxc.cap.drop = sys_module mac_admin mac_override sys_time | |
lxc.mount.entry = /dev dev none ro,bind 0 0 | |
lxc.mount.entry = /lib lib none ro,bind 0 0 | |
lxc.mount.entry = /bin bin none ro,bind 0 0 | |
lxc.mount.entry = /usr usr none ro,bind 0 0 | |
lxc.mount.entry = /sbin sbin none ro,bind 0 0 | |
lxc.mount.entry = /usr/share/lxc/templates/lxc-minimal sbin/init none ro,bind 0 0 | |
lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 | |
lxc.mount.entry = sysfs sys sysfs ro 0 0 | |
lxc.mount.entry = /etc/init.d etc/init.d none ro,bind 0 0 | |
EOF | |
# Oracle Linux and Fedora need the following two bind mounted | |
if [ -d /etc/sysconfig/network-scripts ]; then | |
cat <<EOF >> $path/config | |
lxc.mount.entry = /etc/sysconfig/network-scripts etc/sysconfig/network-scripts none ro,bind 0 0 | |
EOF | |
fi | |
if [ -d /etc/rc.d ]; then | |
cat <<EOF >> $path/config | |
lxc.mount.entry = /etc/rc.d etc/rc.d none ro,bind 0 0 | |
EOF | |
fi | |
# if no .ipv4 section in config, then have the container run dhcp | |
grep -q "^lxc.network.ipv4" $path/config || touch $rootfs/run-dhcp | |
if [ "$(uname -m)" = "x86_64" ]; then | |
cat <<EOF >> $path/config | |
lxc.mount.entry = /lib64 lib64 none ro,bind 0 0 | |
EOF | |
fi | |
} | |
usage() | |
{ | |
cat <<EOF | |
$1 -h|--help -p|--path=<path> [--rootfs=<path>] | |
EOF | |
return 0 | |
} | |
check_for_cmd() | |
{ | |
cmd_path=`type $1` | |
if [ $? -ne 0 ]; then | |
echo "The command '$1' $cmd_path is not accessible on the system" | |
exit 1 | |
fi | |
# we use cut instead of awk because awk is alternatives symlink on ubuntu | |
# and /etc/alternatives isn't bind mounted | |
cmd_path=`echo $cmd_path |cut -d ' ' -f 3` | |
} | |
options=$(getopt -o hp:n:S: -l help,rootfs:,path:,name: -- "$@") | |
if [ $? -ne 0 ]; then | |
usage $(basename $0) | |
exit 1 | |
fi | |
eval set -- "$options" | |
while true | |
do | |
case "$1" in | |
-h|--help) usage $0 && exit 0;; | |
-p|--path) path=$2; shift 2;; | |
--rootfs) rootfs=$2; shift 2;; | |
-n|--name) name=$2; shift 2;; | |
--) shift 1; break ;; | |
*) break ;; | |
esac | |
done | |
if [ "$(id -u)" != "0" ]; then | |
echo "This script should be run as 'root'" | |
exit 1 | |
fi | |
rm -f /tmp/boringd | |
cat << 'EOF' > /tmp/boringd.c | |
#include <stdio.h> | |
#include <signal.h> | |
#include <syslog.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <string.h> | |
#define DAEMON_NAME "boringd" | |
void daemonShutdown(); | |
void signal_handler(int sig); | |
void daemonize(char *rundir, char *pidfile); | |
int pidFilehandle; | |
void signal_handler(int sig) | |
{ | |
switch(sig) | |
{ | |
case SIGHUP: | |
syslog(LOG_WARNING, "Received SIGHUP signal."); | |
break; | |
case SIGINT: | |
case SIGTERM: | |
syslog(LOG_INFO, "Daemon exiting"); | |
daemonShutdown(); | |
exit(EXIT_SUCCESS); | |
break; | |
default: | |
syslog(LOG_WARNING, "Unhandled signal %s", strsignal(sig)); | |
break; | |
} | |
} | |
void daemonShutdown() | |
{ | |
close(pidFilehandle); | |
} | |
void daemonize(char *rundir, char *pidfile) | |
{ | |
int pid, sid, i; | |
char str[10]; | |
struct sigaction newSigAction; | |
sigset_t newSigSet; | |
/* Check if parent process id is set */ | |
if (getppid() == 1) | |
{ | |
/* PPID exists, therefore we are already a daemon */ | |
return; | |
} | |
/* Set signal mask - signals we want to block */ | |
sigemptyset(&newSigSet); | |
sigaddset(&newSigSet, SIGCHLD); /* ignore child - i.e. we don't need to wait for it */ | |
sigaddset(&newSigSet, SIGTSTP); /* ignore Tty stop signals */ | |
sigaddset(&newSigSet, SIGTTOU); /* ignore Tty background writes */ | |
sigaddset(&newSigSet, SIGTTIN); /* ignore Tty background reads */ | |
sigprocmask(SIG_BLOCK, &newSigSet, NULL); /* Block the above specified signals */ | |
/* Set up a signal handler */ | |
newSigAction.sa_handler = signal_handler; | |
sigemptyset(&newSigAction.sa_mask); | |
newSigAction.sa_flags = 0; | |
/* Signals to handle */ | |
sigaction(SIGHUP, &newSigAction, NULL); /* catch hangup signal */ | |
sigaction(SIGTERM, &newSigAction, NULL); /* catch term signal */ | |
sigaction(SIGINT, &newSigAction, NULL); /* catch interrupt signal */ | |
/* Fork*/ | |
pid = fork(); | |
if (pid < 0) | |
{ | |
/* Could not fork */ | |
exit(EXIT_FAILURE); | |
} | |
if (pid > 0) | |
{ | |
/* Child created ok, so exit parent process */ | |
printf("Child process created: %d\n", pid); | |
exit(EXIT_SUCCESS); | |
} | |
/* Child continues */ | |
umask(027); /* Set file permissions 750 */ | |
/* Get a new process group */ | |
sid = setsid(); | |
if (sid < 0) | |
{ | |
exit(EXIT_FAILURE); | |
} | |
/* close all descriptors */ | |
for (i = getdtablesize(); i >= 0; --i) | |
{ | |
close(i); | |
} | |
/* Route I/O connections */ | |
/* Open STDIN */ | |
i = open("/dev/null", O_RDWR); | |
/* STDOUT */ | |
dup(i); | |
/* STDERR */ | |
dup(i); | |
chdir(rundir); /* change running directory */ | |
/* Ensure only one copy */ | |
pidFilehandle = open(pidfile, O_RDWR|O_CREAT, 0600); | |
if (pidFilehandle == -1 ) | |
{ | |
/* Couldn't open lock file */ | |
syslog(LOG_INFO, "Could not open PID lock file %s, exiting", pidfile); | |
exit(EXIT_FAILURE); | |
} | |
/* Try to lock file */ | |
if (lockf(pidFilehandle,F_TLOCK,0) == -1) | |
{ | |
/* Couldn't get lock on lock file */ | |
syslog(LOG_INFO, "Could not lock PID lock file %s, exiting", pidfile); | |
exit(EXIT_FAILURE); | |
} | |
/* Get and format PID */ | |
sprintf(str,"%d\n",getpid()); | |
/* write pid to lockfile */ | |
write(pidFilehandle, str, strlen(str)); | |
} | |
int main() | |
{ | |
/* Debug logging | |
setlogmask(LOG_UPTO(LOG_DEBUG)); | |
openlog(DAEMON_NAME, LOG_CONS, LOG_USER); | |
*/ | |
/* Logging */ | |
setlogmask(LOG_UPTO(LOG_INFO)); | |
openlog(DAEMON_NAME, LOG_CONS | LOG_PERROR, LOG_USER); | |
syslog(LOG_INFO, "Daemon starting up"); | |
/* Deamonize */ | |
daemonize("/tmp/", "/tmp/daemon.pid"); | |
syslog(LOG_INFO, "Daemon running"); | |
while (1) | |
{ | |
//syslog(LOG_INFO, "daemon says hello"); | |
sleep(15); | |
} | |
} | |
EOF | |
/usr/bin/gcc /tmp/boringd.c -o /tmp/boringd | |
rm -f /tmp/boringd.c | |
# When lxc-start | |
if [ $0 = "/sbin/init" ]; then | |
PATH="$PATH:/bin:/sbin:/usr/sbin" | |
check_for_cmd /usr/lib/lxc/lxc-init | |
check_for_cmd /tmp/boringd | |
daemon_path=$cmd_path | |
echo "init'ing with ${daemon_path}" | |
exec /usr/lib/lxc/lxc-init -l TRACE -- $daemon_path | |
exit 1 | |
fi | |
if [ -z "$path" ]; then | |
echo "'path' parameter is required" | |
exit 1 | |
fi | |
# detect rootfs | |
config="$path/config" | |
if [ -z "$rootfs" ]; then | |
if grep -q '^lxc.rootfs' $config 2>/dev/null ; then | |
rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) | |
else | |
rootfs=$path/rootfs | |
fi | |
fi | |
install_minimal $rootfs | |
if [ $? -ne 0 ]; then | |
echo "failed to install minimal's rootfs" | |
exit 1 | |
fi | |
configure_minimal $rootfs | |
if [ $? -ne 0 ]; then | |
echo "failed to configure minimal template" | |
exit 1 | |
fi | |
copy_configuration $path $rootfs $name | |
if [ $? -ne 0 ]; then | |
echo "failed to write configuration file" | |
exit 1 | |
fi | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment