1#! /bin/sh -e
2
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#   http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14
15BACKGROUND=false
16DEFAULT_CONFIG_DIR=${CMAKE_INSTALL_PREFIX}/etc/couchdb/default.d
17DEFAULT_CONFIG_FILE=${CMAKE_INSTALL_PREFIX}/etc/couchdb/default.ini
18ERL_OS_MON_OPTIONS="-os_mon \
19    start_memsup false \
20    start_cpu_sup false \
21    disk_space_check_interval 1 \
22    disk_almost_full_threshold 1"
23ERL_START_OPTIONS="$ERL_OS_MON_OPTIONS -sasl errlog_type error +K true +A 4"
24HEART_BEAT_TIMEOUT=11
25HEART_COMMAND="'${CMAKE_INSTALL_PREFIX}/bin/couchdb' -k"
26INTERACTIVE=false
27KILL=false
28LOCAL_CONFIG_DIR=${CMAKE_INSTALL_PREFIX}/etc/couchdb/local.d
29LOCAL_CONFIG_FILE=${CMAKE_INSTALL_PREFIX}/etc/couchdb/local.ini
30PID_FILE=${CMAKE_INSTALL_PREFIX}/var/run/couchdb/couchdb.pid
31RECURSED=false
32RESET_CONFIG=true
33RESPAWN_TIMEOUT=0
34SCRIPT_ERROR=1
35SCRIPT_OK=0
36SHUTDOWN=false
37STDERR_FILE=couchdb.stderr
38STDOUT_FILE=couchdb.stdout
39
40print_arguments=""
41start_arguments=""
42background_start_arguments=""
43
44basename=`basename $0`
45
46display_version () {
47    cat << EOF
48$basename - Apache CouchDB ${COUCHDB_VERSION}
49
50Licensed under the Apache License, Version 2.0 (the "License"); you may not use
51this file except in compliance with the License. You may obtain a copy of the
52License at
53
54  http://www.apache.org/licenses/LICENSE-2.0
55
56Unless required by applicable law or agreed to in writing, software distributed
57under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
58CONDITIONS OF ANY KIND, either express or implied. See the License for the
59specific language governing permissions and limitations under the License.
60
61EOF
62}
63
64display_help () {
65    cat << EOF
66Usage: $basename [OPTION]
67
68The $basename command runs the Apache CouchDB server.
69
70Erlang is called with:
71
72    $ERL_START_OPTIONS
73
74Erlang inherits the environment of this command.
75
76You can override these options using the environment:
77
78    ERL_AFLAGS, ERL_FLAGS, ERL_ZFLAGS
79
80See erl(1) for more information about the environment variables.
81
82The exit status is 0 for success or 1 for failure.
83
84Options:
85
86  -h          display a short help message and exit
87  -V          display version information and exit
88  -a FILE     add configuration FILE to chain
89  -A DIR      add configuration DIR to chain
90  -n          reset configuration file chain (including system default)
91  -c          print configuration file chain and exit
92  -i          use the interactive Erlang shell
93  -b          spawn as a background process
94  -p FILE     set the background PID FILE (overrides system default)
95  -r SECONDS  respawn background process after SECONDS (defaults to no respawn)
96  -o FILE     redirect background stdout to FILE (defaults to $STDOUT_FILE)
97  -e FILE     redirect background stderr to FILE (defaults to $STDERR_FILE)
98  -s          display the status of the background process
99  -k          kill the background process, will respawn if needed
100  -d          shutdown the background process
101
102Report bugs at <https://www.couchbase.com/issues>.
103EOF
104}
105
106display_error () {
107    if test -n "$1"; then
108        echo $1 >&2
109    fi
110    echo >&2
111    echo "Try \`"$basename" -h' for more information." >&2
112    false
113}
114
115_get_pid () {
116    if test -f $PID_FILE; then
117        PID=`cat $PID_FILE`
118    fi
119    echo $PID
120}
121
122_add_config_file () {
123    if test -z "$print_arguments"; then
124        print_arguments="$1"
125    else
126        print_arguments="`cat <<EOF
127$print_arguments
128$1
129EOF
130`"
131    fi
132    start_arguments="$start_arguments '$1'"
133    background_start_arguments="$background_start_arguments -a '$1'"
134}
135
136_add_config_dir () {
137    for file in "$1"/*.ini; do
138        if [ -r "$file" ]; then
139          _add_config_file "$file"
140        fi
141    done
142}
143
144_load_config () {
145    _add_config_file "$DEFAULT_CONFIG_FILE"
146    _add_config_dir "$DEFAULT_CONFIG_DIR"
147    _add_config_file "$LOCAL_CONFIG_FILE"
148    _add_config_dir "$LOCAL_CONFIG_DIR"
149    if [ "$COUCHDB_ADDITIONAL_CONFIG_FILE" != '' ]
150    then
151        _add_config_file "$COUCHDB_ADDITIONAL_CONFIG_FILE"
152    fi
153}
154
155_reset_config () {
156    print_arguments=""
157    start_arguments=""
158    background_start_arguments="-n"
159}
160
161_print_config () {
162    cat <<EOF
163$print_arguments
164EOF
165}
166
167check_status () {
168    PID=`_get_pid`
169    if test -n "$PID"; then
170        if kill -0 $PID 2> /dev/null; then
171            echo "Apache CouchDB is running as process $PID, time to relax."
172            return $SCRIPT_OK
173        else
174            echo >&2 << EOF
175Apache CouchDB is not running but a stale PID file exists: $PID_FILE"
176EOF
177        fi
178    else
179        echo "Apache CouchDB is not running." >&2
180    fi
181    return $SCRIPT_ERROR
182}
183
184check_environment () {
185    if test "$BACKGROUND" != "true"; then
186        return
187    fi
188    touch $PID_FILE 2> /dev/null || true
189    touch $STDOUT_FILE 2> /dev/null || true
190    touch $STDERR_FILE 2> /dev/null || true
191    message_prefix="Apache CouchDB needs write permission on the"
192    if test ! -w $PID_FILE; then
193        echo "$message_prefix PID file: $PID_FILE" >&2
194        false
195    fi
196    if test ! -w $STDOUT_FILE; then
197        echo "$message_prefix STDOUT file: $STDOUT_FILE" >&2
198        false
199    fi
200    if test ! -w $STDERR_FILE; then
201        echo "$message_prefix STDERR file: $STDERR_FILE" >&2
202        false
203    fi
204    message_prefix="Apache CouchDB needs a regular"
205    if test `echo 2> /dev/null >> $PID_FILE; echo $?` -gt 0; then
206        echo "$message_prefix PID file: $PID_FILE" >&2
207        false
208    fi
209    if test `echo 2> /dev/null >> $STDOUT_FILE; echo $?` -gt 0; then
210        echo "$message_prefix STDOUT file: $STDOUT_FILE" >&2
211        false
212    fi
213    if test `echo 2> /dev/null >> $STDERR_FILE; echo $?` -gt 0; then
214        echo "$message_prefix STDERR file: $STDERR_FILE" >&2
215        false
216    fi
217}
218
219start_couchdb () {
220    if test ! "$RECURSED" = "true"; then
221        if check_status 2> /dev/null; then
222            exit
223        fi
224        check_environment
225    fi
226    interactive_option="+Bd -noinput"
227    if test "$INTERACTIVE" = "true"; then
228        interactive_option=""
229    fi
230    if test "$BACKGROUND" = "true"; then
231        touch $PID_FILE
232        interactive_option="+Bd -noinput"
233    fi
234    ERL_LIBS="$ERL_LIBS:'${CMAKE_INSTALL_PREFIX}/lib/couchdb/erlang/lib'"
235
236    # Find plugins and add them to the Erlang path.
237    if test -d "${CMAKE_INSTALL_PREFIX}/lib/couchdb/erlang/lib/../../plugins"; then
238        for plugin in "${CMAKE_INSTALL_PREFIX}/lib/couchdb/erlang/lib/../../plugins"/*; do
239            if echo "$ERL_ZFLAGS" | grep "$plugin/ebin" > /dev/null 2> /dev/null; then
240                : # It's already loaded.
241            else
242                if echo "$COUCH_PLUGIN_BLACKLIST" | grep "$plugin" > /dev/null 2> /dev/null; then
243                    : # Do not use this plugin.
244                else
245                    ERL_ZFLAGS="$ERL_ZFLAGS -pz '$plugin/ebin'"
246                    if test -d "$plugin/deps"; then
247                        ERL_LIBS="$ERL_LIBS:'$plugin/deps'"
248                    fi
249                fi
250            fi
251        done
252        export ERL_ZFLAGS
253    fi
254
255    command="'${ERL_EXECUTABLE}' $interactive_option $ERL_START_OPTIONS \
256        -env ERL_LIBS $ERL_LIBS -couch_ini $start_arguments -s couch"
257
258    if test "$BACKGROUND" = "true" -a "$RECURSED" = "false"; then
259        $0 $background_start_arguments -b -r $RESPAWN_TIMEOUT -p $PID_FILE \
260            -o $STDOUT_FILE -e $STDERR_FILE -R &
261        echo "Apache CouchDB has started, time to relax."
262    else
263        if test "$RECURSED" = "true"; then
264            while true; do
265                export HEART_COMMAND
266                export HEART_BEAT_TIMEOUT
267                `eval $command -pidfile $PID_FILE -heart \
268                    >> $STDOUT_FILE 2>> $STDERR_FILE` || true
269                PID=`_get_pid`
270                if test -n "$PID"; then
271                    if kill -0 $PID 2> /dev/null; then
272                        return $SCRIPT_ERROR
273                    fi
274                else
275                    return $SCRIPT_OK
276                fi
277                if test "$RESPAWN_TIMEOUT" = "0"; then
278                    return $SCRIPT_OK
279                fi
280                if test "$RESPAWN_TIMEOUT" != "1"; then
281                    plural_ending="s"
282                fi
283                cat << EOF
284Apache CouchDB crashed, restarting in $RESPAWN_TIMEOUT second$plural_ending.
285EOF
286                sleep $RESPAWN_TIMEOUT
287            done
288        else
289            eval exec "$command"
290        fi
291    fi
292}
293
294stop_couchdb () {
295    PID=`_get_pid`
296    if test -n "$PID"; then
297        if test "$1" = "false"; then
298            echo > $PID_FILE
299        fi
300        if kill -0 $PID 2> /dev/null; then
301            if kill -1 $PID 2> /dev/null; then
302                if test "$1" = "false"; then
303                    echo "Apache CouchDB has been shutdown."
304                else
305                    echo "Apache CouchDB has been killed."
306                fi
307                return $SCRIPT_OK
308            else
309                echo "Apache CouchDB could not be killed." >&2
310                return $SCRIPT_ERROR
311            fi
312            if test "$1" = "false"; then
313                echo "Stale PID file exists but Apache CouchDB is not running."
314            else
315                echo "Stale PID file existed but Apache CouchDB is not running."
316            fi
317        fi
318    else
319        echo "Apache CouchDB is not running."
320    fi
321}
322
323parse_script_option_list () {
324    _load_config
325    set +e
326    options=`getopt hVa:A:ncibp:r:Ro:e:skd $@`
327    if test ! $? -eq 0; then
328        display_error
329    fi
330    set -e
331    eval set -- $options
332    while [ $# -gt 0 ]; do
333        case "$1" in
334            -h) shift; display_help; exit;;
335            -V) shift; display_version; exit;;
336            -a) shift; _add_config_file "$1"; shift;;
337            -A) shift; _add_config_dir "$1"; shift;;
338            -n) shift; _reset_config;;
339            -c) shift; _print_config; exit;;
340            -i) shift; INTERACTIVE=true;;
341            -b) shift; BACKGROUND=true;;
342            -r) shift; RESPAWN_TIMEOUT=$1; shift;;
343            -R) shift; RECURSED=true;;
344            -p) shift; PID_FILE=$1; shift;;
345            -o) shift; STDOUT_FILE=$1; shift;;
346            -e) shift; STDERR_FILE=$1; shift;;
347            -s) shift; check_status; exit;;
348            -k) shift; KILL=true;;
349            -d) shift; SHUTDOWN=true;;
350            --) shift; break;;
351            *) display_error "Unknown option: $1" >&2;;
352        esac
353    done
354    if test "$KILL" = "true" -o "$SHUTDOWN" = "true"; then
355        stop_couchdb $KILL
356    else
357        start_couchdb
358    fi
359}
360
361parse_script_option_list $@
362