#!/usr/bin/python
#
#Copyright (c) 2003, 2004, 2005, Olivier Sessink
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions 
#are met:
#  * Redistributions of source code must retain the above copyright 
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above 
#    copyright notice, this list of conditions and the following 
#    disclaimer in the documentation and/or other materials provided 
#    with the distribution.
#  * The names of its contributors may not be used to endorse or 
#    promote products derived from this software without specific 
#    prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
#"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
#LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
#FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
#COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
#INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
#BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
#LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
#ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
#POSSIBILITY OF SUCH DAMAGE.
#

import ConfigParser
import sys
import os
import string
import getopt
import shutil
import stat

LIBDIR='/usr/share/jailkit'
sys.path.append(LIBDIR)
import jk_lib

def logsocket(config,chroot):
	if (not os.path.exists(chroot+'/dev')):
		os.mkdir(chroot+'/dev')
	fd = open('/etc/jailkit/jk_socketd.ini', 'r')
	line = fd.readline()
	while (line):
		if (line == '['+chroot+'/dev/log]'):
			if (config['verbose']):
				print 'we already have '+chroot+'/dev/log in /etc/jailkit/jk_socketd.ini'
			return
		line = fd.readline()
	fd = open('/etc/jailkit/jk_socketd.ini', 'a')
	fd.write('\n['+chroot+'/dev/log]\nbase=512\npeek=2048\ninterval=10\n')
	fd.close()

def proc_mount(config, chroot):
	if (config['verbose']):
		print 'appending proc mount '+chroot+'proc to /etc/fstab'
	fd = open('/etc/fstab', 'a')
	fd.write("proc 	"+chroot+"proc 	proc 	defaults 	0 	0\n")
	fd.close()
	if (config['verbose']):
		print 'executing mount '+chroot+'proc'
	os.spawnlp(os.P_WAIT, 'mount','mount', chroot+'proc')

def add_jk_socketd_entry(config, chroot):
	print 'not yet implemented'

def init_device(config, chroot, path):
	jk_lib.create_full_path(chroot+os.path.dirname(path), config['verbose'])
	if (os.path.exists(chroot+path)):
		if (config['verbose']):
			print 'device '+chroot+path+' does exist already'
		return
	sb = os.stat(path)
	try:
		major = sb.st_rdev / 256  #major = st_rdev divided by 256
		minor = sb.st_rdev % 256 #minor = remainder of st_rdev divided by 256
		if (stat.S_ISCHR(sb.st_mode)): 
			mode = 'c'
		elif (stat.S_ISBLK(sb.st_mode)): 
			mode = 'b'
		else:
			print 'WARNING, '+path+' is not a character or block device'
			return 1
		if (config['verbose']):
			print 'creating device '+chroot+path
		ret = os.spawnlp(os.P_WAIT, 'mknod','mknod', chroot+path, str(mode), str(major), str(minor))
		jk_lib.copy_permissions(path, chroot+path, 0, 0)
	except:
		print 'failed to create device '+path+', this is a know problem with python 2.1'
		print 'use "ls -l '+path+'" to find out the mode, major and minor for the device'
		print 'use "mknod '+path+' mode major minor" to create the device'
		print 'use chmod and chown to correct the permissions as found by ls -l'

def init_files(config,chroot,files):
	if (chroot[-1] == '/'):
		chroot = chroot[:-1]
#	print 'init_files, chroot='+chroot
	for file in files:
#		print 'DEBUG: working on '+chroot+file
		if (not os.path.isfile(chroot+file) or config['force']==1):
			jk_lib.create_full_path(chroot+os.path.dirname(file), config['verbose'])
			if (config['verbose']):
				print 'copying '+file+' to '+chroot+file
#			shutil.copyfile(file,chroot+file)
#			if (not jk_lib.test_suid_sgid(file)):
			jk_lib.copy_with_permissions(file,chroot+file,config['verbose'])
#			else:
#				print 'WARNING: did not copy setuid or setgid permissions for '+file
		else:
			if (config['verbose']):
				print chroot+file+' exists'

def init_directory(config,chroot,dirs):
	for tree in dirs:
		if (tree[-1:] == '/'):
			tree = tree[:-1]
		if (os.path.isdir(tree)):
			if (not os.path.isdir(chroot+tree)):
#				if (config['verbose']):
#					print 'creating dir '+chroot+tree
				jk_lib.create_full_path(chroot+tree, config['verbose'])
				jk_lib.copy_permissions(tree, chroot+tree, config['verbose'])
			ldir = os.listdir(tree)
			for entry in ldir:
				if (os.path.isdir(tree+'/'+entry)):
					init_directory(config,chroot,[tree+'/'+entry])
				elif (os.path.isfile(tree+'/'+entry)):
					init_files(config,chroot,[tree+'/'+entry])
#					else:
#						print 'doing nothing for '+tree+'/'+entry
		else:
			print 'source directory '+tree+' does not exist'

def handle_cfg_section(config,chroot,cfg,section):
	if(chroot[-1] == '/'):
		chroot = chroot[:-1]
	sections = jk_lib.config_get_option_as_list(cfg,section,'includesections')
	for tmp in sections:
		handle_cfg_section(config,chroot,cfg,tmp)
	libs = jk_lib.config_get_option_as_list(cfg,section,'libraries')
	jk_lib.copy_binaries_and_libs(chroot, libs, config['force'], config['verbose'], 1)
 	execs = jk_lib.config_get_option_as_list(cfg,section,'executables')
	jk_lib.copy_binaries_and_libs(chroot, execs, config['force'], config['verbose'], 1)
 	regulars = jk_lib.config_get_option_as_list(cfg,section,'regularfiles')
	init_files(config,chroot,regulars)
	dirs = jk_lib.config_get_option_as_list(cfg,section,'directories')
	init_directory(config,chroot,dirs)
	emptydirs = jk_lib.config_get_option_as_list(cfg,section,'emptydirs')
	for edir in emptydirs:
#		print 'DEBUG emptydir='+edir
		jk_lib.create_full_path(chroot+edir, config['verbose'])
	users = jk_lib.config_get_option_as_list(cfg,section,'users')
	groups = jk_lib.config_get_option_as_list(cfg,section,'groups')
	jk_lib.init_passwd_and_group(chroot,users,groups,config['verbose'])
	if (cfg.has_option(section,'need_proc')):
		do_proc = cfg.get(section,'need_proc')
		if (do_proc):
			proc_mount(config,chroot)
	if (cfg.has_option(section,'need_logsocket')):
		do_logsocket = cfg.get(section,'need_logsocket')
		if (do_logsocket):
			logsocket(config,chroot)
	devices = jk_lib.config_get_option_as_list(cfg,section,'devices')
	for tmp in devices:
		init_device(config,chroot,tmp)

def activateConfig(config, args):
	cfg = ConfigParser.ConfigParser()
	cfg.read([config['file']])
	for section in args[1:]:
		if (cfg.has_section(section)):
			handle_cfg_section(config,args[0],cfg,section)
		else:
			print 'WARNING: section '+section+' does not exist in '+config['file']

def usage():
	print ''
	print "Usage: "+sys.argv[0]+" [OPTIONS] chrootdir sections..."
	print ''
	print "-h --help              : this help screen"
	print "-c, --configfile=FILE  : specify configfile location"
	print '-l, --list             : list all available sections in the configfile'
	print "-v, --verbose          : show what is being done"
	print "-f, --force            : force overwriting of existing files"
	print ''

def listsections(file):
	cfg = ConfigParser.ConfigParser()
	cfg.read(file)
	sections = cfg.sections()
	sections.sort()
	print '\n** Available sections in '+file+' **\n'
	for sec in sections:
		if cfg.has_option(sec, 'comment'):
			print sec+' - '+cfg.get(sec, 'comment')
		else:
			print sec
	print ''

def testargs(args,config):
	if (len(args)<2):
		jk_lib.clean_exit(2,'need at least a chrootdir and a configfile-section',usage)
	if (jk_lib.chroot_is_safe(args[0]) != 1):
		jk_lib.clean_exit(3,'chrootdir '+args[0]+' is not safe',usage)
	if (not os.path.isfile(config['file'])):
		jk_lib.clean_exit(3,'configfile '+config['file']+' does not exist',usage)

def main():
	try:
		opts, args = getopt.getopt(sys.argv[1:], "vhflc:", ["help", "configfile=", "verbose", "force", 'list'])
	except getopt.GetoptError:
		usage()
		sys.exit(1)
	config = {}
	config['file'] = '/etc/jailkit/jk_init.ini'
	config['verbose'] = 0
	config['force'] = 0
	list = 0
	for o, a in opts:
		if o in ("-h", "--help"):
			usage()
			sys.exit()
		if o in ("-c", "--configfile"):
			config['file'] = a
		if  o in ("-l", "--list"):
			list = 1
		if o in ("-v", "--verbose"):
			config['verbose'] = 1
		if o in ("-f", "--force"):
			config['force'] = 1
	if (list ==1):
		listsections(config['file'])
		sys.exit()
	testargs(args, config)
	activateConfig(config, args)
 
if __name__ == "__main__":
    main()
