source: trunk/SH/arb_launcher @ 14493

Last change on this file since 14493 was 14493, checked in by westram, 9 years ago
  • arb_launcher
    • added some debug messages; show debug messages
    • wait for subshells in listen_pipe_unlogged
    • call arb_clean session as soon as launcher thinks no arb process is running (basically fixes #492)
  • Property svn:executable set to *
File size: 11.5 KB
Line 
1#!/bin/bash
2
3set -u
4
5trace() {
6    local MSG=$1
7    echo "[arb_launcher[${ARB_LAUNCHER:-}]: $1]"
8}
9debug() {
10    local MSG=$1
11    # to debug uncomment next line:
12    trace "DEBUG: $MSG"
13}
14
15send_to_launcher() {
16    local NAMED_PIPE=$1
17    local CMD=$2
18
19    debug "Sending '$CMD' to $NAMED_PIPE"
20    echo "$CMD" >$NAMED_PIPE
21    sleep 1
22    debug "send_to_launcher terminates"
23}
24
25pipe_command() {
26    local NAMED_PIPE=$1; shift
27    local CMD=$1; shift
28    local LOGDIR=${1:-}; shift # LOGDIR may be empty/undef -> dont signal crash
29
30    trace "Starting '$CMD'.."
31    $CMD
32    local EXITCODE=${PIPESTATUS[0]}
33    if [ $EXITCODE == 0 ]; then
34        trace "'$CMD' has terminated with success"
35    else
36        trace "'$CMD' has terminated with error $EXITCODE"
37        if [ -n "$LOGDIR" ]; then
38            if [ $EXITCODE = 1 ]; then
39                touch $LOGDIR/failed
40            else
41                touch $LOGDIR/crashed
42            fi
43        fi
44    fi
45
46    send_to_launcher $NAMED_PIPE 'cmd_terminated'
47    debug "pipe_command terminates"
48}
49
50read_line() {
51    local NAMED_PIPE=$1
52    local LINE=""
53
54    if read ATTEMPT <$NAMED_PIPE; then
55        LINE=$ATTEMPT
56    fi
57    echo $LINE
58}
59
60listen_pipe_unlogged() {
61    local NAMED_PIPE=$1; shift
62    local LOGDIR=${1:-}; shift # LOGDIR may be empty/undef -> dont log
63    local RUNNING=1
64    local STARTED=0
65    # RUNNING is set to 1 (otherwise listen_pipe would terminate instantly)
66
67    while (($RUNNING > 0))
68      do
69      LINE=`read_line $NAMED_PIPE 2>/dev/null`
70      if [[ ! -z "$LINE" ]]; then
71          debug "'$NAMED_PIPE' received '$LINE'"
72          if [[ "$LINE" == 'TERMINATE' ]]; then
73              trace "Received request to TERMINATE"
74              break;
75          else
76              if [[ "$LINE" == 'cmd_terminated' ]]; then
77                  RUNNING=$(($RUNNING - 1))
78                  if (($RUNNING>0)); then
79                      trace "Still have $RUNNING arb processes.."
80                  fi
81              else
82                  if [[ "$LINE" == 'allow_termination' ]]; then
83                      RUNNING=$(($RUNNING - 1))
84                  else
85                      pipe_command $NAMED_PIPE "$LINE" $LOGDIR &
86                      RUNNING=$(($RUNNING + 1))
87                      STARTED=$(($STARTED + 1))
88                      debug "RUNNING=$RUNNING"
89                      debug "STARTED=$STARTED"
90                  fi
91              fi
92          fi
93      fi
94    done
95
96    if (($RUNNING==0)); then
97        if (($STARTED>0)); then
98            trace "All launched processes terminated"
99        else
100            trace "Nothing was ever launched"
101        fi
102    else
103        trace "Still have $RUNNING arb-processes - terminating nevertheless"
104    fi
105
106    debug "listen_pipe_unlogged waits for subshells ..."
107    wait
108
109    trace "cleaning up arb session"
110    arb_clean show_session
111    arb_clean session
112
113    debug "listen_pipe_unlogged terminates"
114}
115
116shared_library_dependencies() {
117    case `uname` in
118        Linux)
119            LIST_DYNLIBS="ldd"
120            BINARIES="bin/arb_ntree lib/libARBDB.so lib/libCORE.so lib/libWINDOW.so"
121            ;;
122        Darwin)
123            LIST_DYNLIBS="otool -L"
124            # Darwin ARB links internal stuff static
125            BINARIES="bin/arb_ntree"
126            ;;
127        *)
128            LIST_DYNLIBS="echo UNSUPPORTED_OS "
129            ;;
130    esac
131    for binary in $BINARIES; do
132        echo -e "Library dependencies for $ARBHOME/$binary:"
133        $LIST_DYNLIBS $ARBHOME/$binary
134    done   
135}
136
137wrapped_info() {
138    local TAG=$1; shift
139    local CMD=$1; shift
140    echo "--------------------"
141    echo "[$TAG start]"
142    eval $CMD
143    echo "[$TAG end]"
144    echo ""
145}
146
147collect_system_information() {
148    echo "System information"
149    echo ""
150    echo "The information below has been collected by ARB."
151    echo "Please do not publish without being aware that it might contain personal information."
152    echo ""
153
154    local ARB_RELEVANT="| grep -i ARB"
155
156    wrapped_info "version" "$ARBHOME/bin/arb_ntree --help"
157    wrapped_info "environment" "printenv $ARB_RELEVANT"
158    wrapped_info "OS" "lsb_release -a"
159    wrapped_info "kernel" "uname -mrs ; uname -a ; cat /proc/version"
160    wrapped_info "shared libraries" "shared_library_dependencies"
161    wrapped_info "disk" "df -h"
162    wrapped_info "memory" "free -m ; cat /proc/meminfo"
163    wrapped_info "user limits" "ulimit -a"
164    wrapped_info "ARB processes" "ps aux $ARB_RELEVANT"
165    wrapped_info "KDE desktop version" "konqueror --version"
166    wrapped_info "Gnome desktop version" "gnome-panel --version"
167    wrapped_info "CPU" "cat /proc/cpuinfo"
168    wrapped_info "X server" "xdpyinfo"
169    # wrapped_info "X" "Y"
170}
171
172erase_old_logs() {
173    local LOGBASE=$1
174    if [ -d "$LOGBASE" ]; then
175        # remove files older than 7 days inside and below LOGBASE
176        local OLD=$(( 60 * 24 * 7 ))
177        find $LOGBASE -type f -cmin +$OLD -exec rm {} \;
178        # remove empty directories inside and below LOGBASE
179        find $LOGBASE -type d -depth -empty -mindepth 1 -exec rmdir {} \;
180    fi
181}
182
183listen_pipe() {
184    # this is just a wrapper around listen_pipe_unlogged.
185    # wrapper performs ARB session logging
186    local NAMED_PIPE=$1
187
188    if [ -z ${ARB_PROP:-} ]; then
189        # should never come here, if arb has been started via script 'arb'
190        # (e.g. happens when arb_ntree was started from debugger and then 'start second database' has been called)
191        listen_pipe_unlogged $NAMED_PIPE
192    else
193        local LOGBASE=$ARB_PROP/logs
194        local LOGDIRID=`date '+%Y%m%d_%H%M%S'`.$$
195        local LOGDIR=$LOGBASE/$LOGDIRID
196        local NTREE_STATUS=
197
198        mkdir -p $LOGDIR
199
200        if [ -d "$LOGDIR" ]; then
201            local RUNLOG=$LOGDIR/run.log
202            local SYSLOG=$LOGDIR/sys.info
203            local CRASHFLAG=$LOGDIR/crashed
204            local FAILFLAG=$LOGDIR/failed
205
206            ( ( collect_system_information 2>&1 ) > $SYSLOG ; erase_old_logs $LOGBASE ) &
207            ( listen_pipe_unlogged $NAMED_PIPE $LOGDIR ) 2>&1 | tee $RUNLOG
208
209            if [ -e $CRASHFLAG ]; then
210                # only detects crashes of arb_ntree
211                # (clients are not started via arb_launcher and they usually crash when server exits)
212                NTREE_STATUS=crash
213            else
214                if [ -e $FAILFLAG ]; then
215                    NTREE_STATUS=fail
216                fi
217            fi
218
219            local TARBALLNAME=session.$LOGDIRID.tgz
220            ( cd $LOGBASE ; tar -zcf $TARBALLNAME $LOGDIRID )
221            rm -f $RUNLOG $SYSLOG $CRASHFLAG $FAILFLAG
222            rmdir $LOGDIR
223
224            local FULLTARBALL=$LOGBASE/$TARBALLNAME
225            echo ""
226            echo "Session log has been stored in $FULLTARBALL"
227
228            local LATESTLINK=~/ARB_last_session.tgz
229            if [ -h $LATESTLINK ]; then
230                rm $LATESTLINK
231            fi
232            if [ -e $LATESTLINK ]; then
233                echo "$LATESTLINK already exists and is no symlink"
234            else
235                (cd ~; ln -s $FULLTARBALL $LATESTLINK )
236                echo "    and is also accessible via $LATESTLINK"
237            fi
238
239            if [ "$NTREE_STATUS" != "" ]; then
240                echo ""
241                if [ $NTREE_STATUS = "crash" ]; then
242                    echo "ARB crashed :-("
243                    echo "To report this goto http://bugs.arb-home.de/wiki/BugReport"
244                    echo "Please include the session log mentioned above"
245                    echo ""
246                else
247                    echo "ARB terminated abnormally"
248                fi
249                echo "[press ENTER]"
250                read A
251            fi
252
253            true
254        else
255            echo "Error creating directory '$LOGDIR'"
256            false
257        fi
258    fi
259}
260
261killtree() {
262    local _pid=$1
263    local _sig=${2:-TERM}
264
265    debug "killtree pid=${_pid} with sig=${_sig} pid=$$"
266    kill -stop ${_pid} # stop quickly forking parent from producing childs
267    killchilds ${_pid} ${_sig}
268    kill ${_sig} ${_pid}
269}
270killchilds() {
271    local _pid=$1
272    local _sig=${2:-TERM}
273
274    debug "killchilds pid=${_pid} with sig=${_sig} pid=$$"
275    for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
276        killtree ${_child} ${_sig}
277    done
278}
279
280term_handler() {
281    local NAMED_PIPE=$1
282
283    trace "Killing ARB session for ARB_PID=$ARB_PID"
284    arb_clean session
285    debug "arb_clean done - now killing process tree"
286    killchilds $$ -TERM
287    debug "killchilds done - exiting $$"
288    exit
289}
290
291create_pipe_reader() {
292    local NAMED_PIPE=$1
293    local PARENT_PID=$2
294
295    if [ -z "${ARB_LAUNCHER:-}" ]; then
296        export ARB_LAUNCHER=0
297    else
298        export ARB_LAUNCHER=$(($ARB_LAUNCHER+1))
299    fi
300
301    debug "Creating named pipe '$NAMED_PIPE'"
302
303    # (i did not manage to recover from SIGINT w/o termination of listen_pipe)
304    # => disable SIGINT handler
305    trap '' INT
306    trap "term_handler $NAMED_PIPE" TERM
307    trap "rm -f $NAMED_PIPE" EXIT
308
309    { mkfifo -m 600 $NAMED_PIPE && listen_pipe $NAMED_PIPE ; debug "listen_pipe done" ; } || \
310      { echo "Error creating pipe '$NAMED_PIPE'" ; kill $PARENT_PID ; }
311
312    debug "Pipe reader for '$NAMED_PIPE' terminates.."
313    rm -f $NAMED_PIPE
314    debug "Pipe '$NAMED_PIPE' removed"
315}
316
317initial_send_to_launcher() {
318    local NAMED_PIPE=$1
319    local CMD=$2
320
321    send_to_launcher $NAMED_PIPE "$CMD"
322
323    # now allow pipe reader to terminate:
324    send_to_launcher $NAMED_PIPE "allow_termination"
325}
326
327wait_for_pipe() {
328    local NAMED_PIPE=$1
329
330    while [[ ! -p $NAMED_PIPE ]];
331      do
332      echo "Waiting for '$NAMED_PIPE'.."
333      sleep 1
334    done
335    debug "pipe is open"
336}
337
338get_pipe_name() {
339    local SOCKETDIR="$HOME/.arb_tmp/sockets"
340    mkdir -p "$SOCKETDIR"
341    chmod 0700 "$SOCKETDIR"
342    echo "$SOCKETDIR/arb_launcher.$ARB_PID"
343
344    # instead of the above code, use the following to test a pipe-creation failure:
345    # echo "/arb_launcher.$ARB_PID"
346}
347
348launcher() {
349    local ASYNC=0
350    if [ "$1" = "--async" ]; then
351        ASYNC=1
352        shift
353    fi
354    local CMD="$*"
355
356    if [ -z "$ARB_PID" ]; then
357        echo "Error: environment variable ARB_PID is unset. terminating.."
358        false
359    else
360        if [ -z "$1" ]; then
361            echo "Usage: arb_launcher \"shellcommand\""
362            echo ""
363            echo "          runs 'shellcommand'"
364            echo "          "
365            echo "          The initial call to arb_launcher will block until 'shellcommand' terminates."
366            echo ""
367            echo "          Subsequent calls will not block. They are started from the context of the"
368            echo "          initial call. The initial call will wait for all started commands."
369            echo ""
370            echo "       arb_launcher \"TERMINATE\""
371            echo ""
372            echo "          terminate the launcher without waiting for spawned commands."
373            echo ""
374        else
375            debug "Using ARB_PID '$ARB_PID'"
376            local NAMED_PIPE=$(get_pipe_name)
377            debug "Using NAMED_PIPE '$NAMED_PIPE'"
378
379            if [[ ! -p $NAMED_PIPE ]]; then
380                ( wait_for_pipe $NAMED_PIPE ; initial_send_to_launcher $NAMED_PIPE "$CMD" ) &
381                if (( $ASYNC==1 )); then
382                    create_pipe_reader $NAMED_PIPE $$ &
383                else
384                    create_pipe_reader $NAMED_PIPE $$
385                fi
386            else
387                debug "pipe already was open"
388                send_to_launcher $NAMED_PIPE "$CMD"
389            fi
390
391            # if pipe-reader was started from current process
392            # -> blocks until all launched processes have terminated
393            if (( $ASYNC==0 )); then
394                wait
395            fi
396        fi
397    fi
398}
399
400launcher "$@"
401debug "arb_launcher exits!"
402
Note: See TracBrowser for help on using the repository browser.