1#!/bin/bash
2#
3# Demo of a shell frontend that communicates with a xorriso slave via
4# two named pipes.
5#
6# This script creates two named pipes and starts xorriso with command
7#  -named_pipes_loop cleanup /tmp/xorriso_stdin_pipe_$$ xorriso_stdin_pipe_$$ -
8# Its main loop prompts the user for commands, sends them to xorriso,
9# receives the replies, and parses them by xorriso command
10# -msg_op parse_silently. The resulting words are printed to stdout.
11#
12# xorriso removes the two pipes when it finishes execution of -named_pipes_loop
13# regularly. (E.g. because of commands -end or -rollback_end or because of
14# name loop control message "end_named_pipe_loop".)
15# The vanishing of the pipe files tells this script that xorriso is gone.
16#
17#
18# Copyright (C) 2013
19# Thomas Schmitt <scdbackup@gmx.net>, libburnia-project.org
20# Provided under BSD license: Use, modify, and distribute as you like.
21#
22
23# What xorriso program to use
24xorriso=xorriso
25if test o"$1" = o"-xorriso"
26then
27  xorriso="$2"
28fi
29
30# Version of xorriso and minimum requirement by this script
31export xorriso_version=
32export xorriso_version_req=1.3.1
33
34# Info about the xorriso slave process
35export xorriso_is_running=0
36export xorriso_pid=0
37export xorriso_will_end=0
38
39# Will be set to 1 before this script ends normally
40export normal_end=0
41
42
43# ---------------- An interpreter for quoted xorriso replies ----------------
44
45# xorriso commands like -lsl wrap filenames into quotation marks in order
46# to unambigously represent any character byte except the 0-byte.
47# This piece of code parses input strings into words by letting xorriso
48# command -msg_op "parse_silently" do the hard work.
49# The input strings should be composed by concatenating input lines with
50# newline characters between them. Begin by submitting a single line (without
51# newline at its end) and retry with an appended further line, if
52#   xorriso_parse
53# returns 1. See below xorriso_cmd_and_handle_result() for an example.
54
55
56# The parsed reply words.
57# Valid are reply_array[0] to reply_array[reply_count-1)]
58export reply_array
59export reply_count
60
61
62# Interpret reply of -msg_op parse
63xorriso_recv_parse_reply() {
64  reply_count=0
65  unset reply_array
66  export reply_array
67  ret=-1
68  read ret
69  if test "$ret" -lt 0 -o -z "$ret"
70  then
71    echo "Unexpected text as first reply line of -msg_op parse" >&2
72    xorriso_is_running=0
73    return 2
74  fi
75  test "$ret" = 0 && return "1"
76  read num_strings
77  string_count=0
78  while true
79  do
80    test "$string_count" -ge "$num_strings" && break
81    read num_lines
82    line_count=0
83    acc=
84    while true
85    do
86      test "$line_count" -ge "$num_lines" && break
87      read line
88      test "$line_count" -gt 0 && acc="$acc"$'\n'
89      acc="$acc""$line"
90      line_count=$(($line_count + 1))
91    done
92    reply_array["$string_count"]="$acc"
93    string_count=$(($string_count + 1))
94  done
95  reply_count="$num_strings"
96  return 0
97}
98
99
100# Parse a quoted multi-line string into words
101xorriso_parse() {
102  # $1 : The string which shall be parsed
103  # $2 : The number of concatenated input lines (= number of newlines + 1)
104  # return: 0= array is valid , 1= line incomplete , 2= other error
105
106  test "$xorriso_is_running" = 0 && return 1
107  xorriso_send_cmd "msg_op parse_silently "'"'"'' '' 0 0 $2"'"'$'\n'"$1" || \
108    return 2
109  xorriso_recv_parse_reply <"$result_pipe" || xorriso_is_running=0
110  ret=$?
111  test "$xorriso_is_running" = 0 && ret=2
112  return "$ret"
113}
114
115
116# ------------- End of interpreter for quoted xorriso replies --------------
117
118
119# Send one or more command lines to xorriso
120xorriso_send_cmd() {
121  # $1 : the lines to send
122
123  # >>> is it possible to have a timeout on echo ?
124
125  if test -p "$cmd_pipe"
126  then
127    echo -E "$1" >"$cmd_pipe"
128  else
129    xorriso_is_running=0
130    return 1
131  fi
132}
133
134
135# Make filenames safe for transport by wrapping them in quotes and
136# escaping quotes in their text
137xorriso_esc() {
138  echo -n "'"
139  echo -n "$1" | sed -e "s/'/'"'"'"'"'"'"'/g"
140  echo -n "'"
141}
142
143
144# A handler function for xorriso_cmd_and_handle_result
145xorriso_reply_to_stdout() {
146    echo "${reply_array[*]}"
147}
148
149
150# Let a handler inspect the result lines of a xorriso command line
151xorriso_cmd_and_handle_result() {
152  # $1: handler command word and possibly argument words
153  # $2: command line for xorriso
154
155  if test "$xorriso_is_running" = 0
156  then
157    return 1
158  fi
159
160  handler="$1"
161  xorriso_send_cmd "$2" || return 1
162  res=$(cat "$result_pipe")
163  ret=$?
164  if test "$xorriso_will_end" = 1 -o "$xorriso_is_running" = 0 -o "$ret" -ne 0
165  then
166    test -n "$res" && echo -n "$res"
167    xorriso_is_running=0
168    test "$ret" = 0 || return 1
169    return 0
170  fi
171  test -z "$res" && return 0
172  echo "$res" | \
173  while read line
174  do
175    line_count=1
176    while true
177    do
178      xorriso_parse "$line" "$line_count"
179      ret=$?
180      test "$ret" = 0 && break
181      if test "$ret" = 2
182      then
183        return 1
184      fi
185      read addon
186      line="$line"$'\n'"$addon"
187      line_count=$(expr "$line_count" + 1)
188    done
189    # One can now make use of reply_array[0...(reply_count-1)]
190    $handler
191  done
192  return 0
193}
194
195
196# Execute -version and let xorriso_version_handler interpret reply
197xorriso_check_version() {
198  lookfor='^xorriso version   :  '
199  xorriso_version=$("$xorriso" -version 2>/dev/null | grep "$lookfor" | \
200                    sed -e "s/${lookfor}//")
201  ret=$?
202  if test "$ret" -ne 0 -o "$xorriso_version" = ""
203  then
204    echo "SORRY: Program run '${xorriso}' -version did not yield a result." >&2
205    echo >&2
206    exit 2
207  fi
208  smallest=$((echo "$xorriso_version_req" ; echo "$xorriso_version" ) | \
209             sort | head -1)
210  test "$smallest" = "$xorriso_version_req" && return 0
211  echo "SORRY: xorriso version too old: ${xorriso_version} . Need at least xorriso-${xorriso_version_req} ." >&2
212  echo >&2
213  exit 2
214}
215
216
217# To be executed on exit
218xorriso_cleanup() {
219
220  send_end_cmd=0
221  if test -p "$cmd_pipe" -a "$xorriso_is_running" = 1
222  then
223    if test "$normal_end" = 0
224    then
225      echo "Checking whether xorriso is still running ..." >&2
226      set -x
227      # Give xorriso time to abort
228      sleep 1
229      if ps  | grep '^'"$xorriso_pid" >/dev/null
230      then
231
232        # >>> try to further confirm xorriso identity
233
234        send_end_cmd=1
235      fi
236    else
237      send_end_cmd=1
238    fi
239  fi
240  test "$normal_end" = 0 && set -x
241  if test "$send_end_cmd" = 1
242  then
243    echo "Sending xorriso an -end command ..." >&2
244    xorriso_send_cmd "end" && \
245    test -p "$result_pipe" && cat "$result_pipe" >/dev/null
246  fi
247  test -p "$cmd_pipe" && rm "$cmd_pipe"
248  test -p "$result_pipe" && rm "$result_pipe"
249}
250
251
252# ---------------------------------- main ---------------------------------
253
254# Choose pipe names
255export cmd_pipe=/tmp/xorriso_stdin_pipe_$$
256export result_pipe=/tmp/xorriso_stdout_pipe_$$
257
258# Check the program whether it is modern enough
259xorriso_check_version "$xorriso"
260
261# Prepare for then end of this script
262trap xorriso_cleanup EXIT
263
264# Create the pipes and start xorriso
265mknod "$cmd_pipe" p
266mknod "$result_pipe" p
267"$xorriso" -abort_on NEVER -for_backup \
268           -named_pipe_loop cleanup:buffered "$cmd_pipe" "$result_pipe" "-" &
269xorriso_pid=$!
270xorriso_is_running=1
271
272# Get a sign of life from xorriso before issuing the loop prompt
273xorriso_cmd_and_handle_result xorriso_reply_to_stdout \
274                    "print_info 'xorriso process ${xorriso_pid} started by $0'"
275echo >&2
276
277
278# Now get commands from the user, send them to xorriso and display them
279# via the simple handler xorriso_reply_to_stdout()
280while test "$xorriso_is_running" = 1
281do
282  if test -p "$cmd_pipe"
283  then
284    echo -n "xorriso> " >&2
285  else
286    echo "$0 : Lost contact to xorriso process $xorriso_pid" >&2
287    xorriso_is_running=0
288    break
289  fi
290  read line
291  if echo "$line" | grep '^-*end$' >/dev/null
292  then
293    break
294  fi
295  if echo "$line" | grep '^-*rollback_end$' >/dev/null
296  then
297    xorriso_will_end=1
298  fi
299  xorriso_cmd_and_handle_result xorriso_reply_to_stdout "$line"
300done
301
302# Prevent set -x in the exit handler
303normal_end=1
304
305