#!/usr/bin/python import os import sys import argparse import datetime import subprocess import pwd import logging import re logging.basicConfig() logger = logging.getLogger('docker_shell') logger.setLevel(logging.INFO) g_dry = False def fatal(msg, rc=1): logger.critical(msg) sys.exit(rc) def execute(cmd, msg=None): logger.debug('+ %s ...' % cmd) rc = 0 if not g_dry: rc = subprocess.call(cmd, shell=True) if rc == 0: logger.debug('+ %s [OK]' % cmd) else: logger.debug('+ %s [FAILED (%d)]' % (cmd, rc)) if msg: fatal(msg) else: logger.debug('+ %s [DRY]' % cmd) return rc class User(object): default_shell="/bin/bash" default_user="root:0" def __init__(self, uspec): (self.name, c, rest) = uspec.partition(':') self.validate_name() (self.uid, c, rest) = rest.partition(':') self.validate_uid() (self.shell, c, rest) = rest.partition(':') if self.shell == '': self.shell = User.default_shell if not os.path.exists(self.shell): fatal("Requested shell '%s' does not exist." % self.shell) (self.home, c, rest) = rest.partition(':') logger.debug("User(%s) initialized." % self) def __str__(self): return "%s:%s:%s" % (self.name, self.uid, self.shell) def validate_name(self): NAME_REGEX=r"[a-z][-a-z0-9]*" if re.match(NAME_REGEX, self.name) is None: fatal("'%s' is not a valid username." % self.name) logger.debug("username %s is validated." % self.name) def validate_uid(self): try: self.uid = int(self.uid) logger.debug("uid %d is validated." % self.uid) except: if self.uid == '': if self.name == 'root': self.uid = 0 else: fatal("A user id is required for user '%s'" % self.name) else: fatal("'%s' is not a valid user id." % self.uid) def add(self): logger.debug("Process user %s ..." % self) existing_user = None try: existing_user = pwd.getpwnam(self.name) if existing_user and existing_user.pw_uid != self.uid: fatal("User %s already exists with a different uid (%d).\n" % (self.name, existing_user.pw_uid)) except KeyError: logger.debug("User %s does not exist." % self.name) try: existing_uid = pwd.getpwuid(self.uid) if existing_uid and existing_uid.pw_name != self.name: fatal("UID %d already exists with a different user (%s).\n" % (self.uid, existing_uid.pw_name)) except KeyError: logger.debug("UID %d does not exist." % self.uid) if existing_user: logger.debug("User %s already exists." % self) else: logger.debug("Creating user %s ..." % (self)) cmd = "useradd %s --uid %s --shell %s" % (self.name, self.uid, self.shell) if self.home not in [ None, '' ]: cmd += " -d %s" % (self.home) execute(cmd, "User creation failed for user %s" % self) if not g_dry and self.name != 'root': with open("/etc/sudoers.d/%s" % self.name, "w") as f: f.write("%s ALL=(ALL:ALL) NOPASSWD:ALL\n" % self.name) ############################################################ ap = argparse.ArgumentParser("ONL Docker Shell Build and Development Container Init Script.") ap.add_argument("--user", "-u", help="Run as the given user:uid. The user is created if necessary. The default is %s" % User.default_user, default=User.default_user, metavar="USERNAME:UID[:SHELL]") ap.add_argument("--addusers", "-a", help="Add additional users.", nargs='+', metavar="USERNAME:UID[:SHELL]", default=[]) ap.add_argument("--verbose", "-v", help="Set verbose logging.", action='store_true') ap.add_argument("--start-cacher", help="Start apt-cacher-ng in the container. It does not run by default.", action='store_true') ap.add_argument("--dry", help="Dry run.", action='store_true') ap.add_argument('--command', '-c', help="The command to run. All arguments after this option are considered part of the command.", nargs=argparse.REMAINDER, default=["/bin/bash"]) ops = ap.parse_args() opsdict = vars(ops) g_dry = ops.dry if ops.verbose or ops.dry: logger.setLevel(logging.DEBUG) g_user = User(ops.user) g_user.add() for u in ops.addusers: User(u).add() if ops.start_cacher: execute("/etc/init.d/apt-cacher-ng start", "The apt-cacher-ng service could not be started.") logger.debug("checking if qemu-ppc exists") if os.path.isfile("/proc/sys/fs/binfmt_misc/qemu-ppc"): logger.debug("qemu-ppc already exists") else: if not os.path.ismount("/proc/sys/fs/binfmt_misc"): execute("sudo mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc", "The binfmt_misc system could not be mounted.") execute("sudo /etc/init.d/binfmt-support start", "The binfmt-support service could not be started.") # Fixme: change this to os.execvp() c = "/usr/bin/sudo -E -u %s %s" % (g_user.name, " ".join(ops.command)) sys.exit(execute(c))