summaryrefslogtreecommitdiff
path: root/lib/std/spork.myr
blob: 28707c86b7d95826b529608a6c83c038c0a4589e (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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
use "die"
use "errno"
use "execvp"
use "fmt"
use "result"
use "syswrap"
use "wait"

pkg std =
	const run	: (cmd : byte[:][:]	-> waitstatus)
	const spork	: (cmd : byte[:][:]	-> result((pid, fd, fd), errno))
	const espork	: (cmd : byte[:][:]	-> result((pid, fd, fd, fd), errno))
	const sporkdir	: (cmd : byte[:][:], dir : byte[:]	-> result((pid, fd, fd), errno))
	const esporkdir	: (cmd : byte[:][:], dir : byte[:]	-> result((pid, fd, fd, fd), errno))
;;

const run = {cmd
	var pid

	pid = fork()
	/* error  */
	if pid < 0
		-> `Waiterror
	elif pid == 0
		execvp(cmd[0], cmd)
		suicide()
	else
		-> wait(pid)
	;;
}

const spork = {cmd
	-> sporkdir(cmd, "")
}

const espork = {cmd
	-> esporkdir(cmd, "")
}

const sporkdir = {cmd, dir
	var infds : fd[2], outfds : fd[2]
	var err

	/* init for safe close */
	infds = [-1, -1]
	outfds = [-1, -1]
	/* open up pipes */
	err = pipe(&infds) 
	if err != Enone
		goto sporkerr
	;;
	err = pipe(&outfds)
	if err != Enone
		goto sporkerr
	;;

	match sporkfd(cmd, dir, infds, outfds, [-1, 2])
	| `Ok pid:
		/* close unused fd ends */
		close(infds[0]);
		close(outfds[1]);
		-> `Ok (pid, infds[1], outfds[0])
	| `Err m:
		err = m
		goto sporkerr
	;;
:sporkerr
	close(infds[0])
	close(infds[1])
	close(outfds[0])
	close(outfds[1])
	-> `Err err
}

const esporkdir = {cmd, dir
	var infds : fd[2], outfds : fd[2], errfds : fd[2]
	var err

	/* init for safe close */
	infds = [-1, -1]
	outfds = [-1, -1]
	errfds = [-1, -1]
	/* open up pipes */
	err = pipe(&infds) 
	if err != Enone
		goto sporkerr
	;;
	err = pipe(&outfds)
	if err != Enone
		goto sporkerr
	;;
	err = pipe(&errfds)
	if err != Enone
		goto sporkerr
	;;

	match sporkfd(cmd, dir, infds, outfds, errfds)
	| `Ok pid:
		/* close unused fd ends */
		close(infds[0]);
		close(outfds[1]);
		close(errfds[1]);
		-> `Ok (pid, infds[1], outfds[0], errfds[0])
	| `Err m:
		err = m
		goto sporkerr
	;;
:sporkerr
	/* close on a bad fd is ok. */
	close(infds[0])
	close(infds[1])
	close(outfds[0])
	close(outfds[1])
	close(errfds[0])
	close(errfds[1])
	-> `Err err
}

const sporkfd = {cmd, dir, infds, outfds, errfds
	var pid

	pid = fork()
	/* error  */
	if pid < 0
		/*
		  so we don't leak fds on error,
		  close the child's ends. The parent
		  handles closing its fds.
		*/
		if infds[1] != 0
			close(infds[1]);
		;;
		if outfds[0] != 1
			close(outfds[0]);
		;;
		if errfds[0] != 2
			close(errfds[0]);
		;;
		-> `Err (pid : errno)
	/* child */
	elif pid == 0
		/* stdin/stdout for our communication. */
		match dup2(infds[0], 0)
		| `Ok _:	/* nothing */
		| `Err e:	-> `Err e
		;;
		match dup2(outfds[1], 1)
		| `Ok _:	/* nothing */
		| `Err e:	-> `Err e
		;;
		match dup2(errfds[1], 2)
		| `Ok _:	/* nothing */
		| `Err e:	-> `Err e
		;;

		/* close the fds we duped */
		if infds[0] != 0
			close(infds[0])
		;;
		if outfds[1] != 1
			close(outfds[1])
		;;
		if errfds[1] != 2
			close(errfds[1])
		;;

		/* close the unused ends */
		close(infds[1])
		close(outfds[0])
		close(errfds[0])

		if dir.len != 0 && !chdir(dir)
			std.die("could not chdir")
		;;
		execvp(cmd[0], cmd)
		/* if fork succeeds, we never return */
		suicide()
	/* parent */
	else
		-> `Ok pid
	;;
}