summaryrefslogtreecommitdiffstats
path: root/hyper/process/spawn
blob: a0043ec72b81892e82b3228eef0c8c147c5409ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#! /bin/sh
#
# spawn [command [argument ...]]
#
spawn() {
  set -euf

  # create and change working directory
  wd=`mktemp -d`
  defer rmdir $wd
  cd $wd

  # create named pipes for the child process's stdio
  mkfifo 0 1 2
  defer rm 0 1 2

  # spawn child process
  (exec 0>&- 1>&- 2>&- 0<>0 1<>1 2<>2 "$@") &
  pid=$!

  # setup a trap to kill the child process if this (parent) process dies
  defer kill $pid

  # write child process's pid 
  echo $pid >pid
  defer rm pid

  # create dummy directory for easier debugging
  mkdir -vp /tmp/dummy
  ln -vsnf $wd/0 $wd/1 $wd/2 $wd/pid /tmp/dummy/
  defer rm -v /tmp/dummy/0 /tmp/dummy/1 /tmp/dummy/2 /tmp/dummy/pid

  # wait for the child process's 
  set +e
  wait $pid
  code=$?
  set -e

  # the child is already dead
  cancel kill $pid

  # return the same way wait did
  (exit $code)
}

#
# defer [command [argument ...]]
#
# Defer execution of a command.  Deferred commands are executed in LIFO
# order immediately before the script terminates.  See (golang's defer
# statement for more information how this should work).
#
defer() {
  defer="$*${defer+
$defer}"
  trap "$defer" EXIT
}

#
# cancel [command [argument ...]]
#
# Cancel a deferred command.  The arguments have to match exactly a
# prior defer call or else chaos and mayhem shall haunt thee and shi-
#
cancel() {
  defer="`echo "$defer" | grep -Fxv "$*"`"
  trap "$defer" EXIT
}

spawn "$@"