#!/usr/bin/env python # -*- coding: utf-8 -*- # # Cherokee easy-install script # # Authors: # Alvaro Lopez Ortega # # Copyright (C) 2001-2011 Alvaro Lopez Ortega # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # import re import os import sys import glob import time import stat import subprocess BUILD_DIR = "/var/tmp/cherokee-build" URL_LATEST_RELEASE = "http://www.cherokee-project.com/cherokee-latest-tarball" URL_SNAPSHOT_RELEASE = "http://www.cherokee-project.com/download/trunk/cherokee-latest-svn.tar.gz" PREFIX_STANDARD = "/opt/cherokee" PREFIX_DEVEL = "/opt/cherokee-dev" PHASE_DOWNLOAD = 1 PHASE_UNPACK = 2 PHASE_COMPILE = 3 PHASE_INSTALL = 4 PHASE_INITD = 5 PHASE_REPORT = 6 # Texts # LAUNCHD_PLIST = """\ Labelorg.cherokee.webserver RunAtLoad ProgramArguments %(prefix)s/sbin/cherokee UserName root """ SOLARIS_SVC = """\ """ INITD_SH = """\ #!/bin/sh -e PATH=/sbin:/bin:/usr/sbin:/usr/bin:%(prefix)s/sbin:%(prefix)s/bin DAEMON=%(prefix)s/sbin/cherokee NAME=cherokee PIDFILE=%(prefix)s/var/run/cherokee.pid set -e test -x $DAEMON || exit 0 case "$1" in start) %(prefix)s/sbin/cherokee -d ;; stop) if [ -f $PIDFILE ]; then PID=$(cat $PIDFILE) kill $PID fi ;; restart) $0 stop sleep 1 $0 start ;; reload|force-reload) printf "Reloading web server: %%s\t" "$NAME" if [ -f $PIDFILE ]; then PID=$(cat $PIDFILE) if ps p $PID | grep $NAME >/dev/null 2>&1; then kill -HUP $PID else echo "PID present, but $NAME not found at PID $PID - Cannot reload" exit 1 fi else echo "No PID file present for $NAME - Cannot reload" exit 1 fi ;; status) printf "%%s web server status:\t" "$NAME" if [ -e $PIDFILE ] ; then PROCNAME=$(ps -p $(cat $PIDFILE) -o comm=) if [ "x$PROCNAME" = "x" ]; then printf "Not running, but PID file present \t" else if [ "$PROCNAME" = "$NAME" ]; then printf "Running\t" else printf "PID file points to process '%%s', not '%%s'\t" "$PROCNAME" "$NAME" fi fi else if PID=$(pidofproc cherokee); then printf "Running (PID %%s), but PIDFILE not present\t" "$PID" else printf "Not running\t" fi fi ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|reload|force-reload|status}" >&2 exit 1 ;; esac if [ $? = 0 ]; then echo . exit 0 else echo failed exit 1 fi exit 0 """ BSD_INIT = """\ #!/bin/sh . /etc/rc.subr name="cherokee" rcvar="`set_rcvar`" command="%(prefix)s/sbin/cherokee" load_rc_config $name command_args="-d" run_rc_command "$1" """ # Globals # prefix = PREFIX_STANDARD start_at = PHASE_DOWNLOAD download_snapshot = False devel_build = False # ANSI Colors # ESC = chr(27) + '[' RESET = '%s0m' %(ESC) def green (s): return ESC + '0;32m' + s + RESET def red (s): return ESC + '0;31m' + s + RESET def yellow (s): return ESC + '1;33m' + s + RESET def blue (s): return ESC + '0;34m' + s + RESET # Utilities # def FATAL_error (error, retcode=1): print (red(error)) sys.exit (retcode) def exe (cmd, colorer=lambda x: x, cd=None, stdin=None, return_fatal=True): print (yellow(cmd)) stdout = '' kwargs = {'shell': True, 'stdout': subprocess.PIPE, 'cwd': cd} if stdin: kwargs['stdin'] = subprocess.PIPE p = subprocess.Popen (cmd, **kwargs) if stdin: try: p.stdin.write (stdin) p.stdin.close() except IOError: pass while True: line = p.stdout.readline() if not line: break line = line.decode('utf-8') stdout += line print (colorer (line.rstrip('\n\r'))) p.wait() # Return if p.returncode != 0 and return_fatal: print ('\n%s: Could not execute: %s' %(red('ERROR'), cmd)) return {'stdout': stdout, 'retcode': p.returncode} _root_password = None def get_root_password(): global _root_password while not _root_password: _root_password = read_input ("root's password: ") if _root_password: _root_password += '\n' return _root_password def exe_sudo (cmd, **kwargs): if os.getuid() != 0: root_password = get_root_password() kwargs['stdin'] = root_password cmd = "sudo -S " + cmd return exe (cmd, **kwargs) def which (program): def is_exe(fpath): return os.path.exists(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None def rm (path): return exe ("rm -rf '%s'" %(path), red) def mkdir (path): return exe ("mkdir -p '%s'" %(path), blue) def download (url, target_file): # Wget wget_bin = which ('wget') if wget_bin: ret = exe ("wget '%(url)s' --output-document='%(target_file)s'" %(locals())) if ret['retcode'] == 0: return # Curl curl_bin = which ('curl') if curl_bin: ret = exe ("curl '%(url)s' --output '%(target_file)s'" %(locals())) if ret['retcode'] == 0: return # Python import urllib2 print ("Downloading %s" %(url)) i = urllib2.urlopen (url) o = open (target_file, 'w+') o.write (i.read()) def read_input (prompt): try: # Python 2.x return raw_input (prompt) except NameError: # Python 3.x return input (prompt) def read_yes_no (prompt, empty_is=None): while True: ret = read_input (prompt).lower() if ret in ('y','yes'): return True if ret in ('n','no'): return False if not ret and empty_is != None: return empty_is def figure_initd_app_level (directory, app, not_found=99): files = [x.lower() for x in os.listdir(directory)] for filename in files: tmp = re.findall (r's(\d+)(.+)', filename) if not tmp: continue if tmp[0][1] == app: return tmp[0][0] return not_found def make_path(): return which("gmake") or which("make") # Cherokee # def cherokee_download (tar_file): # Clean up rm (BUILD_DIR) rm (tar_file) mkdir (BUILD_DIR) # Download if download_snapshot: url = URL_SNAPSHOT_RELEASE else: url = URL_LATEST_RELEASE download (url, tar_file) def cherokee_find_unpacked(): for f in os.listdir (BUILD_DIR): fp = os.path.join (BUILD_DIR, f) tmp = re.findall (r'cherokee-(\d+\.\d+\.\d+)', f) if tmp and os.path.isdir (fp): return fp def cherokee_unpack (latest_local): # Unpack ret = exe ("gzip -dc '%s' | tar xfv -" %(latest_local), cd=BUILD_DIR) if ret['retcode'] != 0: return None # Look for the src directory path = cherokee_find_unpacked() if not path: return # Works around clock issues touch = False for f in [os.path.join (path, x) for x in os.listdir (path)]: if os.stat(f)[stat.ST_MTIME] > time.time(): touch = True break if touch: exe ("find '%s' -exec touch '{}' \;" %(path)) return path def cherokee_compile (src_dir): params="--prefix='%s'" %(prefix) # Look for gettext if not which ("msgfmt"): params += " --enable-nls=no" # Snaphost if download_snapshot: params += " --enable-beta" # Trace if devel_build: params += " --enable-trace" params += " CFLAGS='-ggdb3 -O0'" # Configure ret = exe ("./configure " + params, cd=src_dir) if ret['retcode'] != 0: return True # Build ret = exe (make_path(), cd=src_dir) if ret['retcode'] != 0: return True def cherokee_install (src_dir): if os.access (prefix, os.W_OK): ret = exe ("%s install" %(make_path()), cd=src_dir) else: ret = exe_sudo ("%s install" %(make_path()), cd=src_dir) if ret['retcode'] != 0: return True def cherokee_set_initd(): print('') proceed = read_yes_no ("Do you want Cherokee to be started at boot time? [Y/n] ", True) if not proceed: return variables = globals() variables.update (locals()) # MacOS X if sys.platform == 'darwin': tmp_fp = os.path.join (BUILD_DIR, "launchd-cherokee.plist") plist_fp = os.path.join (prefix, "launchd-cherokee.plist") # Write the plist file txt = LAUNCHD_PLIST %(variables) f = open (tmp_fp, 'w+') f.write (txt) f.close() # Permissions exe_sudo ("cp '%s' '%s'" %(tmp_fp, plist_fp)) exe_sudo ("chown root '%s'" %(plist_fp)) exe_sudo ("chgrp admin '%s'" %(plist_fp)) # Let launchd know about it exe_sudo ("launchctl unload -w '%s'" %(plist_fp)) exe_sudo ("launchctl load -w '%s'" %(plist_fp)) exe_sudo ("launchctl start org.cherokee.webserver") return # Solaris if sys.platform.startswith('sunos'): def smf_present(): return (os.access ("/etc/svc/volatile/repository_door", os.R_OK) and not os.path.isfile ("/etc/svc/volatile/repository_door")) variables['prefix_var'] = os.path.join (prefix, "var") tmp_fp = os.path.join (BUILD_DIR, "http-cherokee.xml") xml_fp = "/var/svc/manifest/network/http-cherokee.xml" # Write the plist file txt = SOLARIS_SVC %(variables) f = open (tmp_fp, 'w+') f.write (txt) f.close() # Permissions exe_sudo ("cp '%s' '%s'" %(tmp_fp, xml_fp)) exe_sudo ("chown root '%s'" %(xml_fp)) exe_sudo ("chgrp sys '%s'" %(xml_fp)) # Let launchd know about it if smf_present(): exe_sudo ("/usr/sbin/svccfg import '%s'" %(xml_fp)) exe_sudo ("/usr/sbin/svcadm enable svc:/network/http:cherokee") else: print ("INFO: Skipping SVC, SMF not present") return # BSD if 'bsd' in sys.platform.lower(): rcd_fp = '/etc/rc.d/cherokee' # Preliminary clean up exe_sudo ("rm -f '%s'"%(rcd_fp)) # Write the init.d file txt = BSD_INIT %(variables) f = open (rcd_fp, 'w+') f.write (txt) f.close() # Permissions exe_sudo ("chown root '%s'" %(rcd_fp)) exe_sudo ("chmod 555 '%s'" %(rcd_fp)) return # Init.d if os.path.isdir ("/etc/init.d"): # Figure runlevel ret = exe ("runlevel") tmp = re.findall (r'(\d+)', ret['stdout']) if not tmp: print (red ("Could not figure the current runlevel. Skiping step.")) return runlevel = tmp[0] # Figure rc.d directory: # /etc/rc2.d # /etc/init.d/rc2.d rc_paths = ('/etc/rc%s.d'%(runlevel), '/etc/init.d/rc%s.d'%(runlevel)) rc_dir = None for d in rc_paths: if os.path.isdir (d): rc_dir = d break assert rc_dir, "Unknow init.d layout" # Build paths tmp_fp = os.path.join (BUILD_DIR, "cherokee.initd") sh_fp = os.path.join (prefix, "cherokee.initd") initd_fp = "/etc/init.d/cherokee-opt" # Figure rc2.d file level level = 99 for k in ('apache', 'apache2', 'httpd', 'lighttpd', 'nginx'): level = min (level, figure_initd_app_level (rc_dir, k)) rcS_fp = os.path.join (rc_dir, "S%02dcherkee-opt"%(level-1)) rcK_fp = os.path.join (rc_dir, "K%02dcherkee-opt"%(level-1)) # Preliminary clean up exe_sudo ("rm -f '%s' '%s' '%s' '%s' '%s'" %(tmp_fp, sh_fp, initd_fp, rcS_fp, rcK_fp)) # Write the init.d file txt = INITD_SH %(variables) f = open (tmp_fp, 'w+') f.write (txt) f.close() # Permissions exe_sudo ("cp '%s' '%s'" %(tmp_fp, sh_fp)) exe_sudo ("chown root '%s'" %(sh_fp)) exe_sudo ("chmod 755 '%s'" %(sh_fp)) # Add it exe_sudo ("ln -s '%s' '%s'" %(sh_fp, initd_fp)) # /etc/init.d/cherokee -> /opt/.. exe_sudo ("ln -s '%s' '%s'" %(initd_fp, rcS_fp)) # /etc/rc2.d/S99cherokee -> /etc/init.d/.. exe_sudo ("ln -s '%s' '%s'" %(initd_fp, rcK_fp)) # /etc/rc2.d/K99cherokee -> /etc/init.d/.. def cherokee_report(): cherokee_fp = os.path.join (prefix, "sbin", "cherokee") print (blue ("Technical details:")) exe ("%s -i" %(cherokee_fp)) print (blue ("How to:")) print (" - Launch manually the server: %s/sbin/cherokee -d" %(prefix)) print (" - Launch the administration GUI: %s/bin/cherokee-admin-launcher" %(prefix)) # Main # def main(): tar_file = os.path.join (BUILD_DIR, "cherokee-latest.tar.gz") if start_at <= PHASE_DOWNLOAD: cherokee_download (tar_file) if start_at <= PHASE_UNPACK: src_dir = cherokee_unpack (tar_file) if not src_dir: return else: src_dir = cherokee_find_unpacked() if not src_dir: return if start_at <= PHASE_COMPILE: error = cherokee_compile (src_dir) if error: return if start_at <= PHASE_INSTALL: error = cherokee_install (src_dir) if error: return if start_at <= PHASE_INITD: cherokee_set_initd() if start_at <= PHASE_REPORT: cherokee_report () def check_prerequisites(): # Check for a C compiler if not which("gcc") and not which("cc"): if sys.platform == 'sunos5': cont = read_yes_no ("SUNWgcc must be installed. Proceed? [Y/n] ", True) if not cont: raise SystemExit exe ("pkg install SUNWgcc") else: FATAL_error ("A C compiler is required") # Check for Python if not which("env"): FATAL_error ("'env' is required") ret = exe ("env python -V") if ret['retcode'] != 0: FATAL_error ("Python is not in the path") # Check for make if not make_path(): FATAL_error ("'make' or 'gmake' is required for the compilation") def process_parameters(): global start_at global download_snapshot global devel_build global prefix if '--help' in sys.argv: print ("Cherokee's assisted deployment script:") print (" USAGE: python install.py [params]") print ("") print (" --snapshot Compile latest development snapshot") print (" --devel snapshot w/ debug under cherokee-dev") print ("") print (" Development:") print (" --from-unpack Start at the 'unpack' phase") print (" --from-compile Start at the 'compilation' phase") print (" --from-install Start at the 'install' phase") print (" --from-initd Start at the 'initd' phase") print (" --from-report Start at the 'report' phase") print ("") print ("Report bugs to: http://bugs.cherokee-project.com/") raise SystemExit # Development if '--snapshot' in sys.argv: download_snapshot = True if '--devel' in sys.argv: devel_build = True download_snapshot = True prefix = PREFIX_DEVEL # Script development if '--from-unpack' in sys.argv: start_at = PHASE_UNPACK if '--from-compile' in sys.argv: start_at = PHASE_COMPILE if '--from-install' in sys.argv: start_at = PHASE_INSTALL if '--from-initd' in sys.argv: start_at = PHASE_INITD if '--from-report' in sys.argv: start_at = PHASE_REPORT if __name__ == '__main__': process_parameters() check_prerequisites() main()