#!/bin/bash ## # Copyright (c) 2005-2009 Apple Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ## ## # WARNING: This script is intended for use by developers working on # the Calendar Server code base. It is not intended for use in a # deployment configuration. # # DO NOT use this script as a system startup tool (eg. in /etc/init.d, # /Library/StartupItems, launchd plists, etc.) # # For those uses, install the server properly (eg. with "./run -i # /tmp/foo && cd /tmp/foo && pax -pe -rvw . /") and use the caldavd # executable to start the server. ## set -e set -u wd="$(cd "$(dirname "$0")" && pwd)"; ## # Command line ## verbose=""; do_get="true"; do_setup="true"; do_run="true"; force_setup="false"; disable_setup="false"; print_path="false"; install=""; daemonize="-X"; kill="false"; restart="false"; plugin_name="caldav"; service_type="Combined"; read_key=""; profile=""; reactor=""; if [ -z "${CALENDARSERVER_CACHE_DEPS-}" ]; then cache_deps="${wd}/.dependencies"; else cache_deps="${CALENDARSERVER_CACHE_DEPS}"; fi; usage () { program="$(basename "$0")"; if [ "${1--}" != "-" ]; then echo "$@"; echo; fi; echo "Usage: ${program} [-hvgsfnpdkrR] [-K key] [-iI dst] [-t type] [-S statsdirectory] [-P plugin]"; echo "Options:"; echo " -h Print this help and exit"; echo " -v Be verbose"; echo " -g Get dependencies only; don't run setup or run the server."; echo " -s Run setup only; don't run server"; echo " -f Force setup to run"; echo " -n Do not run setup"; echo " -p Print PYTHONPATH value for server and exit"; echo " -d Run caldavd as a daemon"; echo " -k Stop caldavd"; echo " -r Restart caldavd"; echo " -K Print value of configuration key and exit"; echo " -i Perform a system install into dst; implies -s"; echo " -I Perform a home install into dst; implies -s"; echo " -t Select the server process type (Master, Slave or Combined) [${service_type}]"; echo " -S Write a pstats object for each process to the given directory when the server is stopped."; echo " -P Select the twistd plugin name [${plugin_name}]"; echo " -R Twisted Reactor plugin to execute [${reactor}]"; if [ "${1-}" == "-" ]; then return 0; fi; exit 64; } while getopts 'hvgsfnpdkrK:i:I:t:S:P:R:' option; do case "$option" in '?') usage; ;; 'h') usage -; exit 0; ;; 'v') verbose="-v"; ;; 'f') force_setup="true"; ;; 'k') kill="true"; ;; 'r') restart="true"; ;; 'd') daemonize=""; ;; 'n') do_setup="false"; do_get="false"; ;; 'P') plugin_name="${OPTARG}"; ;; 'R') reactor="-R ${OPTARG}"; ;; 't') service_type="${OPTARG}"; ;; 'K') read_key="${OPTARG}"; ;; 'S') profile="--profiler cprofile -p ${OPTARG}"; ;; 'g') do_get="true" ; do_setup="false"; do_run="false"; ;; 's') do_get="true" ; do_setup="true" ; do_run="false"; ;; 'p') do_get="false"; do_setup="false"; do_run="false"; print_path="true"; ;; 'i') do_get="true" ; do_setup="true" ; do_run="false"; install="${OPTARG}"; install_flag="--root="; ;; 'I') do_get="true" ; do_setup="true" ; do_run="false"; install="${wd}/build/dst"; install_flag="--root="; install_home="${OPTARG}"; ;; esac; done; shift $((${OPTIND} - 1)); if [ $# != 0 ]; then usage "Unrecognized arguments:" "$@"; fi; ## # Set up paths ## py_version () { local python="$1"; shift; echo "$("${python}" -c "from distutils.sysconfig import get_python_version; print get_python_version()")"; } try_python () { local python="$1"; shift; if [ -z "${python}" ]; then return 1; fi; if ! type "${python}" > /dev/null 2>&1; then return 1; fi; local py_version="$(py_version "${python}")"; if [ "${py_version/./}" -lt "25" ]; then return 1; fi; return 0; } for v in "" "2.6" "2.5"; do for p in \ "${PYTHON:=}" \ "python${v}" \ "/usr/local/bin/python${v}" \ "/usr/local/python/bin/python${v}" \ "/usr/local/python${v}/bin/python${v}" \ "/opt/bin/python${v}" \ "/opt/python/bin/python${v}" \ "/opt/python${v}/bin/python${v}" \ "/Library/Frameworks/Python.framework/Versions/${v}/bin/python" \ "/opt/local/bin/python${v}" \ "/sw/bin/python${v}" \ ; do if try_python "${p}"; then python="${p}"; break; fi; done; if [ -n "${python:-}" ]; then break; fi; done; if [ -z "${python:-}" ]; then echo "No suitable python found. Python 2.5 is required."; exit 1; fi; if [ -z "${caldav:-}" ]; then caldav="${wd}"; fi; if [ -z "${caldavd_wrapper_command:-}" ]; then if [ "$(uname -s)" == "Darwin" ] && [ "$(uname -r | cut -d . -f 1)" -ge 9 ]; then caldavd_wrapper_command="launchctl bsexec /"; else caldavd_wrapper_command=""; fi; fi; top="$(cd "${caldav}/.." && pwd -L)" patches="${caldav}/lib-patches"; twisted="${top}/Twisted"; dav="${twisted}/twisted/web2/dav" if [ -z "${config:-}" ]; then config="${wd}/conf/caldavd-dev.plist"; fi; conf_read_key () { local key="$1"; shift; # FIXME: This only works for simple values (no arrays, dicts) tr '\n' ' ' < "${config}" \ | xpath "/plist/dict/*[preceding-sibling::key[1]='${key}'" 2> /dev/null \ | sed -n 's|^<[^<][^<]*>\([^<]*\).*$|\1|p'; } if [ -n "${read_key}" ]; then conf_read_key "${read_key}"; exit $?; fi; if "${kill}" || "${restart}"; then pidfile="$(conf_read_key "PIDFile")"; if [ ! -r "${pidfile}" ]; then pidfile="${caldav}/logs/caldavd.pid"; echo "Assuming PID file: ${pidfile}"; fi; if [ ! -r "${pidfile}" ]; then echo "Unreadable PID file: ${pidfile}"; exit 1; fi; pid="$(cat "${pidfile}" | head -1)"; if [ -z "${pid}" ]; then echo "No PID in PID file: ${pidfile}"; exit 1; fi; echo "Killing process ${pid}"; kill -TERM "${pid}"; if ! "${restart}"; then exit 0; fi; fi; if [ -z "${PYTHONPATH:-}" ]; then user_python_path=""; else user_python_path=":${PYTHONPATH}"; fi; py_platform="$("${python}" -c "from distutils.util import get_platform; print get_platform()")"; py_version="$(py_version "${python}")"; py_platform_libdir="lib.${py_platform}-${py_version}"; if [ -n "${install}" ] && ! echo "${install}" | grep '^/' > /dev/null; then install="$(pwd)/${install}"; fi; svn_uri_base="$(svn info "${caldav}" --xml 2> /dev/null | sed -n 's|^.*\(.*\).*$|\1|p')"; if [ -z "${svn_uri_base}" ]; then svn_uri_base="http://svn.calendarserver.org/repository/calendarserver"; fi; export PYTHONPATH="${caldav}"; # Find a command that can hash up a string for us if type -t openssl > /dev/null; then hash="hash"; hash () { openssl dgst -md5; } elif type -t md5 > /dev/null; then hash="md5"; elif type -t md5sum > /dev/null; then hash="md5sum"; elif type -t cksum > /dev/null; then hash="hash"; hash () { cksum | cut -f 1 -d " "; } elif type -t sum > /dev/null; then hash="hash"; hash () { sum | cut -f 1 -d " "; } else hash=""; fi; ## # Routines ## run () { if "${print_path}"; then echo "${PYTHONPATH}"; exit 0; fi; echo ""; echo "Using ${python} as Python"; if "${do_run}"; then if [ ! -f "${config}" ]; then echo ""; echo "Missing config file: ${config}"; echo "You might want to start by copying the test configuration:"; echo ""; echo " cp conf/caldavd-test.plist conf/caldavd-dev.plist"; echo ""; exit 1; fi; cd "${wd}"; if [ ! -d "${wd}/logs" ]; then mkdir "${wd}/logs"; fi; echo ""; echo "Starting server..."; exec ${caldavd_wrapper_command} \ "${caldav}/bin/caldavd" ${daemonize} \ -f "${config}" \ -T "${twisted}/bin/twistd" \ -P "${plugin_name}" \ -t "${service_type}" \ ${reactor} \ ${profile}; cd /; fi; } jmake () { case "$(uname -s)" in Darwin|Linux) ncpu="$(getconf _NPROCESSORS_ONLN)"; ;; FreeBSD) ncpu="$(sysctl hw.ncpu)"; ncpu="${cpu##hw.ncpu: }"; ;; esac; if [ -n "${ncpu:-}" ] && [[ "${ncpu}" =~ ^[0-9]+$ ]]; then make -j "${ncpu}" "$@"; else make "$@"; fi; } apply_patches () { local name="$1"; shift; local path="$1"; shift; if [ -d "${patches}/${name}" ]; then echo ""; echo "Applying patches to ${name} in ${path}..."; cd "${path}"; find "${patches}/${name}" \ -type f \ -name '*.patch' \ -print \ -exec patch -p0 --forward -i '{}' ';'; cd /; fi; echo "" echo "Removing build directory ${path}/build..." rm -rf "${path}/build"; echo "Removing pyc files from ${path}..." find "${path}" -type f -name '*.pyc' -print0 | xargs -0 rm -f; } www_get () { if ! "${do_get}"; then return 0; fi; local name="$1"; shift; local path="$1"; shift; local url="$1"; shift; if "${force_setup}" || [ ! -d "${path}" ]; then local ext="$(echo "${url}" | sed 's|^.*\.\([^.]*\)$|\1|')"; case "${ext}" in gz|tgz) decompress="gzip -d -c"; ;; bz2) decompress="bzip2 -d -c"; ;; tar) decompress="cat"; ;; *) echo "Unknown extension: ${ext}"; exit 1; ;; esac; echo ""; if [ -n "${cache_deps}" ] && [ -n "${hash}" ]; then mkdir -p "${cache_deps}"; cache_file="${cache_deps}/${name}-$(echo "${url}" | "${hash}")-$(basename "${url}")"; if [ ! -f "${cache_file}" ]; then echo "Downloading ${name}..."; curl -L "${url}" -o "${cache_file}"; fi; echo "Unpacking ${name} from cache..."; get () { cat "${cache_file}"; } else echo "Downloading ${name}..."; get () { curl -L "${url}"; } fi; rm -rf "${path}"; cd "$(dirname "${path}")"; get | ${decompress} | tar -xvf -; apply_patches "${name}" "${path}"; cd /; fi; } svn_get () { if ! "${do_get}"; then return 0; fi; local name="$1"; shift; local path="$1"; shift; local uri="$1"; shift; local revision="$1"; shift; if [ -d "${path}" ]; then local wc_uri="$(svn info --xml "${path}" 2> /dev/null | sed -n 's|^.*\(.*\).*$|\1|p')"; if "${force_setup}"; then # Verify that we have a working copy checked out from the correct URI if [ "${wc_uri}" != "${uri}" ]; then echo ""; echo "Current working copy (${path}) is from the wrong URI: ${wc_uri} != ${uri}"; rm -rf "${path}"; svn_get "${name}" "${path}" "${uri}" "${revision}"; return $?; fi; echo ""; echo "Reverting ${name}..."; svn revert -R "${path}"; echo "Updating ${name}..."; svn update -r "${revision}" "${path}"; apply_patches "${name}" "${path}"; else if ! "${print_path}"; then # Verify that we have a working copy checked out from the correct URI if [ "${wc_uri}" != "${uri}" ]; then echo ""; echo "Current working copy (${path}) is from the wrong URI: ${wc_uri} != ${uri}"; echo "Performing repository switch for ${name}..."; svn switch -r "${revision}" "${uri}" "${path}"; apply_patches "${name}" "${path}"; else local svnversion="$(svnversion "${path}")"; if [ "${svnversion%%[M:]*}" != "${revision}" ]; then echo ""; echo "Updating ${name}..."; svn update -r "${revision}" "${path}"; apply_patches "${name}" "${path}"; fi; fi; fi; fi; else echo ""; checkout () { echo "Checking out ${name}..."; svn checkout -r "${revision}" "${uri}@${revision}" "${path}"; } if [ "${revision}" != "HEAD" ] && [ -n "${cache_deps}" ] && [ -n "${hash}" ]; then local cache_file="${cache_deps}/${name}-$(echo "${uri}" | "${hash}")@r${revision}.tgz"; mkdir -p "${cache_deps}"; if [ -f "${cache_file}" ]; then echo "Unpacking ${name} from cache..."; mkdir -p "${path}"; tar -C "${path}" -xvzf "${cache_file}"; else checkout; echo "Caching ${name}..."; tar -C "${path}" -cvzf "${cache_file}" .; fi; else checkout; fi; apply_patches "${name}" "${path}"; fi; } py_build () { local name="$1"; shift; local path="$1"; shift; local optional="$1"; shift; if "${do_setup}"; then echo ""; echo "Building ${name}..."; cd "${path}"; if ! "${python}" ./setup.py -q build --build-lib "build/${py_platform_libdir}" "$@"; then if "${optional}"; then echo "WARNING: ${name} failed to build."; echo "WARNING: ${name} is not required to run the server; continuing without it."; else return $?; fi; fi; cd /; fi; } py_install () { local name="$1"; shift; local path="$1"; shift; if [ -n "${install}" ]; then echo ""; echo "Installing ${name}..."; cd "${path}"; "${python}" ./setup.py install "${install_flag}${install}"; cd /; fi; } py_have_module () { local module="$1"; shift; PYTHONPATH="" "${python}" -c "import ${module}" > /dev/null 2>&1; } ## # Download and set up dependancies ## # # Zope Interface # if ! py_have_module zope.interface; then zope="${top}/zope.interface-3.3.0"; www_get "Zope Interface" "${zope}" http://www.zope.org/Products/ZopeInterface/3.3.0/zope.interface-3.3.0.tar.gz; py_build "Zope Interface" "${zope}" false; py_install "Zope Interface" "${zope}"; export PYTHONPATH="${PYTHONPATH}:${zope}/build/${py_platform_libdir}"; fi; # # PyXML # if ! py_have_module xml.dom.ext; then xml="${top}/PyXML-0.8.4"; www_get "PyXML" "${xml}" http://internap.dl.sourceforge.net/sourceforge/pyxml/PyXML-0.8.4.tar.gz; py_build "PyXML" "${xml}" false; py_install "PyXML" "${xml}"; export PYTHONPATH="${PYTHONPATH}:${xml}/build/${py_platform_libdir}"; fi; # # PyOpenSSL # if ! py_have_module OpenSSL; then ssl="${top}/pyOpenSSL-0.7"; www_get "PyOpenSSL" "${ssl}" http://pypi.python.org/packages/source/p/pyOpenSSL/pyOpenSSL-0.7.tar.gz; py_build "PyOpenSSL" "${ssl}" false; py_install "PyOpenSSL" "${ssl}"; export PYTHONPATH="${PYTHONPATH}:${ssl}/build/${py_platform_libdir}"; fi; # # PyKerberos # # if type krb5-config > /dev/null; then # if ! py_have_module kerberos; then # kerberos="${top}/PyKerberos"; # svn_get "PyKerberos" "${kerberos}" "${svn_uri_base}/PyKerberos/trunk" 4241; # py_build "PyKerberos" "${kerberos}" false; # FIXME: make optional # py_install "PyKerberos" "${kerberos}"; # export PYTHONPATH="${PYTHONPATH}:${kerberos}/build/${py_platform_libdir}"; # fi; # fi; # # PyOpenDirectory # if [ "$(uname -s)" == "Darwin" ]; then if ! py_have_module opendirectory; then opendirectory="${top}/PyOpenDirectory"; svn_get "PyOpenDirectory" "${opendirectory}" "${svn_uri_base}/PyOpenDirectory/trunk" 4106; py_build "PyOpenDirectory" "${opendirectory}" false; py_install "PyOpenDirectory" "${opendirectory}"; export PYTHONPATH="${PYTHONPATH}:${opendirectory}/build/${py_platform_libdir}"; fi; fi; # # xattr # if ! py_have_module xattr; then xattr="${top}/xattr"; svn_get "xattr" "${xattr}" http://svn.red-bean.com/bob/xattr/releases/xattr-0.5 1013; py_build "xattr" "${xattr}" false; py_install "xattr" "${xattr}"; export PYTHONPATH="${PYTHONPATH}:${xattr}/build/${py_platform_libdir}"; fi; # # select26 # if [ "${py_version}" != "${py_version##2.5}" ] && ! py_have_module select26; then select26="${top}/select26-0.1a3" www_get "select26" "${select26}" http://pypi.python.org/packages/source/s/select26/select26-0.1a3.tar.gz; py_build "select26" "${select26}" true; py_install "select26" "${select26}"; export PYTHONPATH="${PYTHONPATH}:${select26}/build/${py_platform_libdir}"; fi; if ! type memcached >& /dev/null; then # # libevent # libevent="${top}/libevent-1.4.8-stable" www_get "libevent" "${libevent}" http://monkey.org/~provos/libevent-1.4.8-stable.tar.gz if "${do_setup}" && ( "${force_setup}" || [ ! -d "${libevent}/_root" ] ); then echo ""; echo "Building libevent..."; cd "${libevent}"; ./configure --prefix="${libevent}/_root"; jmake; jmake install; fi; export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:${libevent}/_root/lib"; export DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH:-}:${libevent}/_root/lib"; # # memcached # memcached="${top}/memcached-1.2.6" www_get "memcached" "${memcached}" http://www.danga.com/memcached/dist/memcached-1.2.6.tar.gz; if "${do_setup}" && ( "${force_setup}" || [ ! -d "${memcached}/_root" ] ); then echo ""; echo "Building memcached..."; cd "${memcached}"; ./configure --prefix="${memcached}/_root" \ --with-libevent="${libevent}/_root" \ --enable-threads; jmake; jmake install; fi; export PATH="${PATH}:${top}/memcached-1.2.6/_root/bin"; fi; # # Twisted # case "${USER}" in wsanchez) proto="svn+ssh"; ;; *) proto="svn"; ;; esac; svn_uri="${proto}://svn.twistedmatrix.com/svn/Twisted/branches/dav-take-two-3081-4"; svn_get "Twisted" "${twisted}" "${svn_uri}" 26969; # No py_build step, since we tend to do edit Twisted, we want the sources in # PYTHONPATH, not a build directory. py_install "Twisted" "${twisted}"; export PYTHONPATH="${PYTHONPATH}:${twisted}"; # twisted.web2 doesn't get installed by default if [ -n "${install}" ]; then echo ""; echo "Installing Twisted.web2..."; cd "${twisted}"; "${python}" ./twisted/web2/topfiles/setup.py install "${install_flag}${install}"; cd /; fi; # # dateutil # if ! py_have_module dateutil; then dateutil="${top}/python-dateutil-1.4.1"; www_get "dateutil" "${dateutil}" http://www.labix.org/download/python-dateutil/python-dateutil-1.4.1.tar.gz; py_install "dateutil" "${dateutil}"; export PYTHONPATH="${PYTHONPATH}:${dateutil}"; fi; # # vobject # vobject="${top}/vobject"; case "${USER}" in cyrusdaboo) base="svn+ssh://cdaboo@svn.osafoundation.org/svn"; ;; *) base="http://svn.osafoundation.org"; ;; esac; svn_uri="${base}/vobject/trunk"; svn_get "vObject" "${vobject}" "${svn_uri}" 212; py_install "vObject" "${vobject}"; export PYTHONPATH="${PYTHONPATH}:${vobject}"; # # PyDirector # if ! py_have_module pydirector; then pydirector="${top}/pydirector-1.0.0"; www_get "PyDirector" "${pydirector}" http://internap.dl.sourceforge.net/sourceforge/pythondirector/pydirector-1.0.0.tar.gz; py_build "PyDirector" "${pydirector}" false; py_install "PyDirector" "${pydirector}"; export PYTHONPATH="${PYTHONPATH}:${pydirector}/build/${py_platform_libdir}"; fi; # # CalDAVTester # # caldavtester="${top}/CalDAVTester"; # svn_get "CalDAVTester" "${caldavtester}" "${svn_uri_base}/CalDAVTester/trunk" 4221; # # PyFlakes # #pyflakes="${top}/Pyflakes"; #svn_get "Pyflakes" "${pyflakes}" http://divmod.org/svn/Divmod/trunk/Pyflakes 17198; # # Calendar Server # py_install "Calendar Server" "${caldav}"; ## # Do home install # This is a hack, but it's needed because installing with --home doesn't work for python-dateutil. ## if [ -n "${install_home:-}" ]; then py_prefix="$("${python}" -c "import sys; print sys.prefix;")"; py_libdir="$("${python}" -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1);")"; install -d "${install_home}"; install -d "${install_home}/bin"; install -d "${install_home}/conf"; install -d "${install_home}/lib/python"; rsync -av "${install}${py_prefix}/bin/" "${install_home}/bin/"; rsync -av "${install}${py_libdir}/" "${install_home}/lib/python/"; rsync -av "${install}${py_prefix}/caldavd/" "${install_home}/caldavd/"; rm -rf "${install}"; fi; ## # Run the server ## export PYTHONPATH="${PYTHONPATH}${user_python_path}"; run;