LAMPInstall
From Noah.org
Contents |
LAMP Install
This script installs almost everything you need for a LAMP server. The server install directory can be set anywhere you like. The entire installation is also relocatable and can coexist with other applications and libraries. All libraries such as zlib and openssl are installed separately from the system library files, so you don't have to worry about ".so Hell".
Components:
"zlib","libpng","tiff","jpegsrc","freetype","gd","libxml2","openssl","httpd","subversion","php-5","mediawiki"
Automatic configuration enhancements
The INSTALL script will automatically configure some components with some useful defaults.
- php.ini
- php.ini-recommended is copied to php.ini
- httpd.conf
- SSL is turned on
- a snake-oil self-signed certificate is installed (~/conf/extra/httpd-ssl.conf)
- ~/conf/extra/httpd-default.conf is enabled.
- PHP5 handler is turned on
- mod_deflate is turned on for text/html text/plain text/xml
- combined log format is enabled
- index.php is turned on as a Directory Index
INSTALL
#!/usr/bin/env python """This builds an Apache2 web server. The server directory structure follows a virtual server structure, so you can install the entire web server and all of its libraries under any directory on your server. All libraries and external tools are kept separate from the rest of the file system. Most of the options are NOT set through the command line. Instead, you edit this script. The parts that you care about are: USERS: This sets the user:group that the given component will use. VIRTUAL_SERVER_ROOT: This sets the root path where everything gets installed COMPONENTS: This sets the list of components that you want built. I am not a Makefile. 2006 Noah Spurrier no copyright no license no warranty no refunds public domain TODO: Check for missing external applications such as: libtool, gcc, autoconf, lex or flex, mysql includes (/usr/include/mysql/.) msgfmt/gettext (Ubuntu require 'gettext' package be installed before building wget. This only matters for wget dependency). TODO: Add apache2/bin to LD_LIBRARY_PATH Libraries have been installed in: /var/www/usr/local/apache2/lib If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR' flag during linking and do at least one of the following: * add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution * add LIBDIR to the `LD_RUN_PATH' environment variable during linking * use the `-Wl,--rpath -Wl,LIBDIR' linker flag See any operating system documentation about shared libraries for more information, such as the ld(1) and ld.so(8) manual pages. """ USERS = {'httpd':{'user':'www','group':'www'}, 'mysql':{'user':'mysql','group':'mysql'}} # Note: Make sure that EVERY directory in the path for # VIRTUAL_SERVER_ROOT is readable and executable by the httpd user. # Otherwise you will get a 403 error. VIRTUAL_SERVER_ROOT = '/var/www' #VIRTUAL_SERVER_ROOT = os.getcwd() + '/WWW' COMPONENTS=["zlib","libpng","tiff","jpegsrc","freetype","gd","openssl","libxml2","httpd","curl","subversion","php-5","mediawiki","webalizer"] ########################################################################## GLOBAL_LOGFILE = None import sys, os, shutil, re, time, tarfile, platform, traceback import pexpect from pyed import pyed import getopt def exit_with_usage (): print globals()['__doc__'] os._exit(1) def parse_args (options='', long_options=[]): try: optlist, args = getopt.getopt(sys.argv[1:], options+'h?', long_options+['help','h','?']) except Exception, e: print str(e) exit_with_usage() options = dict(optlist) if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]: exit_with_usage() return (options, args) def main (): (options, args) = parse_args('v') # if args<=0: # exit_with_usage() if '-v' in options: verbose_flag = True else: verbose_flag = False if platform.dist()[0]=='debian' or platform.dist()[0]=='ubuntu': install_prerequisites_ubuntu () else: print "THIS SCRIPT ONLY KNOWS HOW TO INSTALL PREREQUISITES FOR UBUNTU AND DEBIAN DISTROS." print "ALL OTHERS ARE ON THEIR OWN. SEE THE FUNCTION install_prerequisites_ubuntu()" print "IN THIS SCRIPT FOR HINTS ON WHAT YOU MAY NEED TO INSTALL BY HAND." print "THIS SCRIPT WILL NOW CONTINUE AND HOPE FOR THE BEST." global VIRTUAL_SERVER_ROOT global COMPONENTS env={} env['prefix_path'] = VIRTUAL_SERVER_ROOT env['platform'] = platform.system().lower() # For some reason Apache2 MUST be installed under ./usr/local/apache2. env['prefix_path_httpd'] = env['prefix_path'] + '/usr/local/apache2' # Some build systems will not create these and expect them to already exist: mkdir(env['prefix_path']) mkdir(env['prefix_path']+'/etc') mkdir(env['prefix_path']+'/bin') mkdir(env['prefix_path']+'/include') mkdir(env['prefix_path']+'/lib') mkdir(env['prefix_path']+'/man/man1') cp ('WWW.ENV', env['prefix_path']+'/WWW.ENV') cp ('APACHECTL', env['prefix_path']+'/APACHECTL') chmod (env['prefix_path']+'/APACHECTL', 0774) cp ('PHP', env['prefix_path']+'/PHP') chmod (env['prefix_path']+'/PHP', 0774) document = pyed() document.read (env['prefix_path']+'/WWW.ENV') if document.find_first('^\s*PREFIX_PATH\s*=.*') is not None: document.cur_line = 'PREFIX_PATH=%(prefix_path)s' % env document.write(env['prefix_path']+'/WWW.ENV') document.read(env['prefix_path']+'/APACHECTL') if document.find_first('^{SOURCE_WWW.ENV}') is not None: document.cur_line = '. %(prefix_path)s/WWW.ENV' % env document.write(env['prefix_path']+'/APACHECTL') document.read(env['prefix_path']+'/PHP') if document.find_first('^{SOURCE_WWW.ENV}') is not None: document.cur_line = '. %(prefix_path)s/WWW.ENV' % env document.write(env['prefix_path']+'/PHP') if 'x86_64' in uname('-m'): print "BUILDING FOR 64-BIT LINUX" env['x86_64'] = '1' # # THIS ENVIRONMENT SHOULD MATCH WHAT IS DEFINED IN THE 'WWW.ENV' FILE. # PATH = env['prefix_path']+'/bin:'+ env['prefix_path']+'/usr/local/apache2/bin:'+env['prefix_path']+'/php/bin:'+import_env('PATH') SSL_BASE = env['prefix_path'] + '/ssl' CPPFLAGS= '-I' + env['prefix_path']+'/include' LD_LIBRARY_PATH = env['prefix_path']+'/lib:' + SSL_BASE+'/lib:' + import_env('LD_LIBRARY_PATH') # libpng needs this (can't do it with ./configure): LDFLAGS = '-L' + env['prefix_path']+'/lib' #INCLUDES = env['prefix_path']+'/include' # Using both INCLUDES and CPPFLAGS can confuse some ./configures if 'x86_64' in env: LDFLAGS = LDFLAGS + ' -L/usr/lib64 -L/usr/lib64/mysql' # This is necessary for 64-bit Linux (PHP5 build bug). export_env('LDFLAGS', LDFLAGS) export_env('PATH', PATH) export_env('SSL_BASE', SSL_BASE) export_env('CPPFLAGS', CPPFLAGS) export_env('LD_LIBRARY_PATH', LD_LIBRARY_PATH) #export_env('LDFLAGS', LDFLAGS) #export_env('INCLUDES', INCLUDES) env.update(os.environ) print "OPEN TARBALLS" components_build_info = process_tarballs(COMPONENTS) # normalize paths and contstruct class names from their component names build_root_path = pwd() for k,v in components_build_info.items(): v['build_path'] = build_root_path + '/build/' + v['build_path'] v['tar_path'] = build_root_path + '/' + v['tar_path'] v['build_class'] = 'build_' + k.replace('-','') print "BUILD COMPONENTS" for c in COMPONENTS: print time.asctime() print "building", c env.update(components_build_info[c]) b = eval (components_build_info[c]['build_class']+'(env)') b.build() print u = USERS['httpd'] print "Setting ownership of all files under %s" % ( env['prefix_path'] ) print "to user %s, group %s" % (u['user'], u['group']) chown (env['prefix_path'], u['user'], u['group'], recursive=True) print "------------------------------------------------------------------" print "Done!" print "------------------------------------------------------------------" print "Apache is installed under " + env['prefix_path_httpd'] print "To start Apache:" print " " + env['prefix_path_httpd'] + "/bin/apachectl start" print "This will also start HTTPS, but with a snakeoil self-signed cert." print "You should get a CA signed Cert for production use." print print "To use other Apache and PHP utilities you must first" print "source this environment file:" print " . " + env['prefix_path'] + "/WWW.ENV" print "This will set the LD_LIBRARY_PATH and the PATH." return def clean(path_list): """This is kind of like "make clean". FIXME: THIS DOES NOT WORK YET. """ for f in path_list: rm (f) def print_run_output(d): """This is a callback for run() to print dots every few seconds. """ sys.stdout.write (".") sys.stdout.flush() def run(cmd): """This is a wrapper around pexpect.run(). It adds logging to a global logfile. """ global GLOBAL_LOGFILE print >>GLOBAL_LOGFILE, "RUNNING COMMAND" print >>GLOBAL_LOGFILE, cmd (result, exitstatus) = pexpect.run(cmd, events={pexpect.TIMEOUT:print_run_output}, timeout=5, withexitstatus=True, logfile=GLOBAL_LOGFILE) if exitstatus != 0: print >>GLOBAL_LOGFILE, "FOR THE FOLLOWING COMMAND RETURNED EXIT STATUS %s:" % (str(exitstatus)) print >>GLOBAL_LOGFILE, " " + str(cmd) print "FOR THE FOLLOWING COMMAND RETURNED EXIT STATUS %s:" % (str(exitstatus)) print " " + str(cmd) return (result, exitstatus) def export_env (env_name, variable=None): """This puts the given variable name and value into the os.environment. This is kind of like "export" in a bash script. """ if variable == None: del os.environ[env_name] return os.environ[env_name] = variable def import_env (env_name): if not env_name in os.environ: return '' return os.environ[env_name] def cd (path): os.chdir (path) def uname (args='-a'): (result, exitstatus) = run ('uname %s' % args) return result def cp (path_from, path_to): if path_from[-1] == '/': path_from = path_from[0:-1] if os.path.isdir(path_from): shutil.copytree(path_from, path_to) else: shutil.copy2(path_from, path_to) def chown (path, user, group=None, recursive=False): opts = '' if recursive: opts = '-R' if group is not None: run ('chown -h %s %s:%s %s' % (opts, user, group, path)) else: run ('chown -h %s %s %s' % (opts, user, path)) def chmod (path, mode): os.chmod (path, mode) def ls (path='.'): return os.listdir(path) def ln (target, link_name): run('ln -sf %s %s' % (target, link_name)) def pwd (): return os.getcwd() def rm (path): shutil.rmtree(path) def exit (status=0): sys.exit(status) def mkdir (path): """This is like 'mkdir -p'. """ try: os.makedirs(path) except: return None class builder (object): """This is the common component builder base class. This does a standard "configure && make && make install" style of install. TODO: Does not actually do the &&. Should check exit status TODO: before each step. """ def __init__ (self, env): global GLOBAL_LOGFILE self.prefix_path = env['prefix_path'] self.build_path = env['build_path'] self.env = env GLOBAL_LOGFILE = open('./log/'+self.__class__.__name__+'.log', 'wb') def build (self): # setup # build # express environment # tear down home = pwd() # by this point the build path has been automatically opened from the tarball. cd (self.build_path) # the run function automatically logs output to the ./log directory. # FIXME I should not run make install if configure or make fails... run ('./configure --prefix=%(prefix_path)s' % self.env) run ('make') run ('make install') cd (home) def install_prerequisites_ubuntu (): """This installs packages necessary for operation under Ubuntu. This should fail harmlessly on non-ubuntu systems. TODO: Add a check for non-ubuntu systems. """ global GLOBAL_LOGFILE GLOBAL_LOGFILE = open('./log/apt-get.log', 'wb') run ('apt-get -q -y install gettext') # For wget run ('apt-get -q -y install libxml2-dev libxml2-utils') run ('apt-get -q -y install build-essential autoconf sed flex bison') run ('apt-get -q -y install mysql-server libmysqlclient15-dev') class build_zlib (builder): def build (self): # setup # build # express environment # tear down home = pwd() cd (self.build_path) run ('./configure --shared --prefix=%(prefix_path)s' % self.env) run ('make') run ('make install') cd (home) class build_tiff (builder): pass class build_libpng (builder): def build (self): home = pwd() cd (self.build_path) #run ('./autogen.sh') # Do not do this on Linux! run ('./configure --prefix=%(prefix_path)s' % self.env) run ('make') run ('make install') cd (home) class build_jpegsrc (builder): def build (self): home = pwd() cd (self.build_path) if 'x86_64' in self.env: cp ('/usr/share/libtool/config.sub', '.') # This is necessary for 64-bit Linux. cp ('/usr/share/libtool/config.guess', '.') run ('./configure --prefix=%(prefix_path)s --enable-shared --enable-static'%self.env) run ('make') run ('make install') cd (home) class build_freetype (builder): pass class build_gd (builder): pass class build_libxml2 (builder): def build (self): home = pwd() cd (self.build_path) configure = """./configure --prefix=%(prefix_path)s \ --with-zlib=%(prefix_path)s""" % self.env run (configure) run ('make') run ('make install') cd (home) class build_wget (builder): pass class build_openssl (builder): def build (self): home = pwd() cd (self.build_path) cmd = './config -fPIC zlib shared no-threads no-asm -L'+ self.env['prefix_path']+'/lib' + ' --openssldir=%(SSL_BASE)s' % self.env run (cmd) run ('make') run ('make install') cd (home) class build_mediawiki(builder): def build (self): home = pwd() u = USERS['httpd'] cp (self.build_path, self.env['prefix_path_httpd'] + '/htdocs/wiki') chown (self.env['prefix_path_httpd'] + '/htdocs/wiki', u['user'], u['group'], recursive=True) chmod (self.env['prefix_path_httpd'] + '/htdocs/wiki', 0774) cd (home) class build_mysql (builder): def build (self): create_user_and_group('mysql') u = USERS['mysql'] home = pwd() cd (self.build_path) run ('./configure --prefix=%(prefix_path)s/mysql'%self.env) run ('make') run ('make install') cp ('support-files/my-medium.cnf','%(prefix_path)s/etc/my.cnf'%self.env) cd ('%(prefix_path)s/mysql/bin'%self.env) #run ('./bin/mysql_install_db --user=mysql --basedir=%(prefix_path)s/mysql'%self.env) run ('./mysql_install_db --user=mysql --basedir=%(prefix_path)s/mysql'%self.env) cd ('%(prefix_path)s/mysql'%self.env) run ('chown -R root .') run ('chown -R %s var' % (u['user'])) run ('chgrp -R %s .' % (u['group'])) cd (home) class build_webalizer (builder): def build (self): home = pwd() cd (self.build_path) configure = """./configure --prefix=%(prefix_path_httpd)s \ --with-gd=%(prefix_path)s/include --with-gdlib=%(prefix_path)s/lib""" run (configure) run ('make') run ('make install') cd (home) class build_httpd (builder): """This will install Apache2 with SSL. It will create and activate a self-signed snakeoil certificate. It will update the User and Group that Apache2 runs as. """ def build (self): create_user_and_group('httpd') u = USERS['httpd'] home = pwd() cd (self.build_path) configure = """./configure --prefix=%(prefix_path_httpd)s \ --enable-so \ --enable-mime-magic \ --enable-rewrite \ --enable-vhost-alias \ --enable-headers \ --with-z=%(prefix_path)s \ --enable-deflate \ --enable-dav \ --enable-dav-fs \ --enable-ssl --with-ssl=%(SSL_BASE)s""" % self.env run (configure) run ('make') run ('make install') cd (home) # make a snakeoil self-signed cert (X.509 Certificate) cd (self.env['prefix_path_httpd'] + '/conf') run ('%(prefix_path)s/ssl/bin/openssl req -x509 -batch -newkey rsa:1024 -nodes -keyout server.key -out server.crt' % self.env) # enable SSL in the httpd.conf # update User and Group in httpd.conf # edit Apache's envvars so that apachectl will automatically source environment. conf_filename = self.env['prefix_path_httpd'] + '/bin/envvars' conf = pyed () conf.read (conf_filename) conf.find_last() conf.append('. ' + self.env['prefix_path']+'/WWW.ENV') conf.write(conf_filename) conf_filename = self.env['prefix_path_httpd'] + '/conf/httpd.conf' conf = pyed () conf.read (conf_filename) # Turn on indexes conf.find_first(r"<Directory />") if conf.find_next(r"\w*Options .*"): conf.append(" Options +ExecCGI") conf.append(" Options +FollowSymLinks") conf.append(" Options +Indexes") conf.append(" IndexOptions FancyIndexing") conf.append(" IndexOptions NameWidth=*") conf.append(" IndexOptions SuppressDescription") conf.append(" IndexOptions FoldersFirst") conf.append(" #IndexOptions ScanHTMLTitles") # Allow .htaccess files to work conf.find_first(r"<Directory />") if conf.find_next(r"\w*AllowOverride None"): conf.cur_line = " AllowOverride All" # turn on SSL if conf.find_first('#Include conf/extra/httpd-ssl.conf') is not None: conf.cur_line = 'Include conf/extra/httpd-ssl.conf' # turn on default settings if conf.find_first('#Include conf/extra/httpd-default.conf')