Created
April 8, 2014 20:46
-
-
Save nelsnelson/10189332 to your computer and use it in GitHub Desktop.
lxc-minimal
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 | |
# 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-rackos-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