Skip to content

Commit 9dacf30

Browse files
committed
Added ability to close open files to fix exec bug
1 parent cdeddb0 commit 9dacf30

File tree

5 files changed

+523
-189
lines changed

5 files changed

+523
-189
lines changed

pkg/script/stdlib/file/file.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func Library() script.Library {
3434
"remove": script.Func(remove),
3535
"chown": script.Func(chown),
3636
"chmod": script.Func(chmod),
37+
"close": script.Func(fclose),
3738
}
3839
}
3940

pkg/script/stdlib/file/operations.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,27 @@ func move(parser script.ArgParser) (script.Retval, error) {
3838
return Move(f, dstPath), nil
3939
}
4040

41+
// Close a file if possible, otherwise this operation is a no-op.
42+
//
43+
//go:generate go run ../gendoc.go -lib file -func close -param f@File -doc "Close a file if possible, otherwise this operation is a no-op."
44+
//
45+
// @callable: file.close
46+
// @param: file @File
47+
//
48+
// @usage: file.close(f)
49+
func Close(file Type) {
50+
Type.Close(file)
51+
}
52+
53+
func fclose(parser script.ArgParser) (script.Retval, error) {
54+
f, err := parseFileParam(parser, 0)
55+
if err != nil {
56+
return nil, err
57+
}
58+
Close(f)
59+
return nil, nil
60+
}
61+
4162
// Name returns file's basename.
4263
//
4364
//go:generate go run ../gendoc.go -lib file -func name -param f@File -retval name@String -doc "Name returns file's basename."

pkg/script/stdlib/file/starlark.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package file
22

33
import (
44
"fmt"
5+
"io"
56
"os"
67
"sync"
78

@@ -73,6 +74,16 @@ func (f Type) Move(dstPath string) error {
7374
return f.File.Move(dstPath)
7475
}
7576

77+
// Close provides a concurrency-safe method to close the underlying File.
78+
func (f Type) Close() error {
79+
f.mu.Lock()
80+
defer f.mu.Unlock()
81+
if closer, ok := f.File.(io.Closer); ok {
82+
return closer.Close()
83+
}
84+
return nil
85+
}
86+
7687
/*
7788
* Starlark.Value methods
7889
*/

www/assets.gen.go

Lines changed: 188 additions & 188 deletions
Large diffs are not rendered by default.

www/src/config/renegade/spec.json

Lines changed: 302 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,302 @@
1-
{"libraries":[{"name":"cdn","functions":[{"name":"openFile","doc":"OpenFile stored on the CDN. Writing to the file will cause an upload to the CDN, overwriting any previously stored contents. Reading the file will download it from the CDN. Since operations are performed lazily, openFile will never error however reading from or writing to the file may.","params":[{"name":"name","type":"String"}],"retvals":[{"name":"f","type":"File"}]}]},{"name":"file","functions":[{"name":"move","doc":"Move a file to the desired location.","params":[{"name":"f","type":"File"},{"name":"dstPath","type":"String"}],"retvals":[{"name":"err","type":"Error"}]},{"name":"name","doc":"Name returns file's basename.","params":[{"name":"f","type":"File"}],"retvals":[{"name":"name","type":"String"}]},{"name":"content","doc":"Content returns the file's content.","params":[{"name":"f","type":"File"}],"retvals":[{"name":"content","type":"String"},{"name":"err","type":"Error"}]},{"name":"write","doc":"Write sets the file's content.","params":[{"name":"f","type":"File"},{"name":"content","type":"String"}],"retvals":[{"name":"err","type":"Error"}]},{"name":"copy","doc":"Copy the file's content into another file.","params":[{"name":"src","type":"File"},{"name":"dst","type":"File"}],"retvals":[{"name":"err","type":"Error"}]},{"name":"remove","doc":"Remove the file. It will become unuseable after calling this operation.","params":[{"name":"f","type":"File"}],"retvals":[{"name":"err","type":"Error"}]},{"name":"chown","doc":"Chown modifies the file's ownership metadata. Passing an empty string for either the username or group parameter will result in a no-op. For example, file.chown(f, '', 'new_group') will change the file's group ownership to 'new_group' but will not affect the file's user ownership.","params":[{"name":"f","type":"File"},{"name":"username","type":"String"},{"name":"group","type":"String"}],"retvals":[{"name":"err","type":"Error"}]},{"name":"chmod","doc":"Chmod modifies the file's permission metadata. The strong passed is expected to be an octal representation of what os.FileMode you wish to set file to have.","params":[{"name":"f","type":"File"},{"name":"mode","type":"String"}],"retvals":[{"name":"err","type":"Error"}]}]},{"name":"http","functions":[{"name":"newRequest","doc":"NewRequest creates a new Request object to be passed around.","params":[{"name":"url","type":"String"}],"retvals":[{"name":"request","type":"Request"}]},{"name":"setMethod","doc":"SetMethod sets the http method on the request object.","params":[{"name":"r","type":"Request"},{"name":"method","type":"String"}],"retvals":null},{"name":"setHeader","doc":"SetHeader sets the http header to the value passed on the request object.","params":[{"name":"r","type":"Request"},{"name":"header","type":"String"},{"name":"value","type":"String"}],"retvals":null},{"name":"setBody","doc":"SetBody sets the http body to the value passed on the request object.","params":[{"name":"r","type":"Request"},{"name":"value","type":"String"}],"retvals":null},{"name":"exec","doc":"Exec sends the passed request object.","params":[{"name":"r","type":"Request"}],"retvals":[{"name":"response","type":"String"},{"name":"err","type":"Error"}]}]},{"name":"process","functions":[{"name":"kill","doc":"Kill a process (using SIGKILL).","params":[{"name":"proc","type":"Process"}],"retvals":[{"name":"err","type":"Error"}]},{"name":"name","doc":"Name gets the name of the passed process.","params":[{"name":"proc","type":"Process"}],"retvals":[{"name":"name","type":"String"},{"name":"err","type":"Error"}]}]},{"name":"regex","functions":[{"name":"replaceString","doc":"Replace uses the golang regex lib to replace all occurences of the pattern in the old string into the new strong.","params":[{"name":"oldString","type":"String"},{"name":"pattern","type":"String"},{"name":"newString","type":"String"}],"retvals":[{"name":"replacedString","type":"String"},{"name":"err","type":"Error"}]},{"name":"replace","doc":"Replace uses the golang regex lib to replace all occurences of the pattern in the old string into the new strong.","params":[{"name":"oldString","type":"String"},{"name":"pattern","type":"String"},{"name":"newString","type":"String"}],"retvals":[{"name":"replacedString","type":"String"},{"name":"err","type":"Error"}]}]},{"name":"ssh","functions":[{"name":"exec","doc":"Exec a command on the remote system using an underlying ssh session.","params":[{"name":"cmd","type":"String"},{"name":"disown","type":"?Bool"}],"retvals":[{"name":"output","type":"String"},{"name":"err","type":"Error"}]},{"name":"openFile","doc":"OpenFile on the remote system using SFTP over SSH. The file is created if it does not yet exist.","params":[{"name":"path","type":"String"}],"retvals":[{"name":"f","type":"File"},{"name":"err","type":"Error"}]}]},{"name":"assets","functions":[{"name":"openFile","doc":"OpenFile that was packed into the compiled binary. The resulting file does not support many operations such as Chown, Write, etc. but you may read it's contents or copy it to another file i.e. one opened by ssh or sys.","params":[{"name":"path","type":"String"}],"retvals":[{"name":"f","type":"File"},{"name":"err","type":"Error"}]}]},{"name":"sys","functions":[{"name":"openFile","doc":"OpenFile uses os.Open to Open a file.","params":[{"name":"path","type":"String"}],"retvals":[{"name":"f","type":"File"},{"name":"err","type":"Error"}]},{"name":"detectOS","doc":"DetectOS uses the GOOS variable to determine the OS.","params":null,"retvals":[{"name":"os","type":"String"}]},{"name":"exec","doc":"Exec uses the os/exec.command to execute the passed executable/params.","params":[{"name":"executable","type":"String"},{"name":"disown","type":"?Bool"}],"retvals":[{"name":"output","type":"String"},{"name":"err","type":"Error"}]},{"name":"connections","doc":"Connections uses the gopsutil/net to get all connections created by a process (or all by default).","params":[{"name":"parent","type":"?Process"}],"retvals":[{"name":"connections","type":"[]Connection"}]},{"name":"processes","doc":"Processes uses the gopsutil/process to get all processes.","params":null,"retvals":[{"name":"procs","type":"[]Process"}]},{"name":"files","doc":"Files uses the ioutil.ReadDir to get all files in a given path.","params":null,"retvals":[{"name":"files","type":"[]File"}]}]},{"name":"assert","functions":[{"name":"noError","doc":"NoError will check if the passed value is a starlark.NoneType, if not it will error out the script. This function may cause a fatal error if the assertion is incorrect.","params":[{"name":"err","type":"starlark.Value"}],"retvals":null},{"name":"equal","doc":"Equal will check if two values are equal. This function will result in a fatal error if the assertion is incorrect.","params":[{"name":"expected","type":"starlark.Value"},{"name":"actual","type":"starlark.Value"}],"retvals":null}]}]}
1+
{
2+
"libraries": [
3+
{
4+
"name": "cdn",
5+
"functions": [
6+
{
7+
"name": "openFile",
8+
"doc": "OpenFile stored on the CDN. Writing to the file will cause an upload to the CDN, overwriting any previously stored contents. Reading the file will download it from the CDN. Since operations are performed lazily, openFile will never error however reading from or writing to the file may.",
9+
"params": [{ "name": "name", "type": "String" }],
10+
"retvals": [{ "name": "f", "type": "File" }]
11+
}
12+
]
13+
},
14+
{
15+
"name": "file",
16+
"functions": [
17+
{
18+
"name": "move",
19+
"doc": "Move a file to the desired location.",
20+
"params": [
21+
{ "name": "f", "type": "File" },
22+
{ "name": "dstPath", "type": "String" }
23+
],
24+
"retvals": [{ "name": "err", "type": "Error" }]
25+
},
26+
{
27+
"name": "name",
28+
"doc": "Name returns file's basename.",
29+
"params": [{ "name": "f", "type": "File" }],
30+
"retvals": [{ "name": "name", "type": "String" }]
31+
},
32+
{
33+
"name": "content",
34+
"doc": "Content returns the file's content.",
35+
"params": [{ "name": "f", "type": "File" }],
36+
"retvals": [
37+
{ "name": "content", "type": "String" },
38+
{ "name": "err", "type": "Error" }
39+
]
40+
},
41+
{
42+
"name": "write",
43+
"doc": "Write sets the file's content.",
44+
"params": [
45+
{ "name": "f", "type": "File" },
46+
{ "name": "content", "type": "String" }
47+
],
48+
"retvals": [{ "name": "err", "type": "Error" }]
49+
},
50+
{
51+
"name": "copy",
52+
"doc": "Copy the file's content into another file.",
53+
"params": [
54+
{ "name": "src", "type": "File" },
55+
{ "name": "dst", "type": "File" }
56+
],
57+
"retvals": [{ "name": "err", "type": "Error" }]
58+
},
59+
{
60+
"name": "remove",
61+
"doc": "Remove the file. It will become unuseable after calling this operation.",
62+
"params": [{ "name": "f", "type": "File" }],
63+
"retvals": [{ "name": "err", "type": "Error" }]
64+
},
65+
{
66+
"name": "chown",
67+
"doc": "Chown modifies the file's ownership metadata. Passing an empty string for either the username or group parameter will result in a no-op. For example, file.chown(f, '', 'new_group') will change the file's group ownership to 'new_group' but will not affect the file's user ownership.",
68+
"params": [
69+
{ "name": "f", "type": "File" },
70+
{ "name": "username", "type": "String" },
71+
{ "name": "group", "type": "String" }
72+
],
73+
"retvals": [{ "name": "err", "type": "Error" }]
74+
},
75+
{
76+
"name": "chmod",
77+
"doc": "Chmod modifies the file's permission metadata. The strong passed is expected to be an octal representation of what os.FileMode you wish to set file to have.",
78+
"params": [
79+
{ "name": "f", "type": "File" },
80+
{ "name": "mode", "type": "String" }
81+
],
82+
"retvals": [{ "name": "err", "type": "Error" }]
83+
},
84+
{
85+
"name": "close",
86+
"doc": "Close a file if possible, otherwise this operation is a no-op.",
87+
"params": [{ "name": "f", "type": "File" }],
88+
"retvals": null
89+
}
90+
]
91+
},
92+
{
93+
"name": "http",
94+
"functions": [
95+
{
96+
"name": "newRequest",
97+
"doc": "NewRequest creates a new Request object to be passed around.",
98+
"params": [{ "name": "url", "type": "String" }],
99+
"retvals": [{ "name": "request", "type": "Request" }]
100+
},
101+
{
102+
"name": "setMethod",
103+
"doc": "SetMethod sets the http method on the request object.",
104+
"params": [
105+
{ "name": "r", "type": "Request" },
106+
{ "name": "method", "type": "String" }
107+
],
108+
"retvals": null
109+
},
110+
{
111+
"name": "setHeader",
112+
"doc": "SetHeader sets the http header to the value passed on the request object.",
113+
"params": [
114+
{ "name": "r", "type": "Request" },
115+
{ "name": "header", "type": "String" },
116+
{ "name": "value", "type": "String" }
117+
],
118+
"retvals": null
119+
},
120+
{
121+
"name": "setBody",
122+
"doc": "SetBody sets the http body to the value passed on the request object.",
123+
"params": [
124+
{ "name": "r", "type": "Request" },
125+
{ "name": "value", "type": "String" }
126+
],
127+
"retvals": null
128+
},
129+
{
130+
"name": "exec",
131+
"doc": "Exec sends the passed request object.",
132+
"params": [{ "name": "r", "type": "Request" }],
133+
"retvals": [
134+
{ "name": "response", "type": "String" },
135+
{ "name": "err", "type": "Error" }
136+
]
137+
}
138+
]
139+
},
140+
{
141+
"name": "process",
142+
"functions": [
143+
{
144+
"name": "kill",
145+
"doc": "Kill a process (using SIGKILL).",
146+
"params": [{ "name": "proc", "type": "Process" }],
147+
"retvals": [{ "name": "err", "type": "Error" }]
148+
},
149+
{
150+
"name": "name",
151+
"doc": "Name gets the name of the passed process.",
152+
"params": [{ "name": "proc", "type": "Process" }],
153+
"retvals": [
154+
{ "name": "name", "type": "String" },
155+
{ "name": "err", "type": "Error" }
156+
]
157+
}
158+
]
159+
},
160+
{
161+
"name": "regex",
162+
"functions": [
163+
{
164+
"name": "replaceString",
165+
"doc": "Replace uses the golang regex lib to replace all occurences of the pattern in the old string into the new strong.",
166+
"params": [
167+
{ "name": "oldString", "type": "String" },
168+
{ "name": "pattern", "type": "String" },
169+
{ "name": "newString", "type": "String" }
170+
],
171+
"retvals": [
172+
{ "name": "replacedString", "type": "String" },
173+
{ "name": "err", "type": "Error" }
174+
]
175+
},
176+
{
177+
"name": "replace",
178+
"doc": "Replace uses the golang regex lib to replace all occurences of the pattern in the old string into the new strong.",
179+
"params": [
180+
{ "name": "oldString", "type": "String" },
181+
{ "name": "pattern", "type": "String" },
182+
{ "name": "newString", "type": "String" }
183+
],
184+
"retvals": [
185+
{ "name": "replacedString", "type": "String" },
186+
{ "name": "err", "type": "Error" }
187+
]
188+
}
189+
]
190+
},
191+
{
192+
"name": "ssh",
193+
"functions": [
194+
{
195+
"name": "exec",
196+
"doc": "Exec a command on the remote system using an underlying ssh session.",
197+
"params": [
198+
{ "name": "cmd", "type": "String" },
199+
{ "name": "disown", "type": "?Bool" }
200+
],
201+
"retvals": [
202+
{ "name": "output", "type": "String" },
203+
{ "name": "err", "type": "Error" }
204+
]
205+
},
206+
{
207+
"name": "openFile",
208+
"doc": "OpenFile on the remote system using SFTP over SSH. The file is created if it does not yet exist.",
209+
"params": [{ "name": "path", "type": "String" }],
210+
"retvals": [
211+
{ "name": "f", "type": "File" },
212+
{ "name": "err", "type": "Error" }
213+
]
214+
}
215+
]
216+
},
217+
{
218+
"name": "assets",
219+
"functions": [
220+
{
221+
"name": "openFile",
222+
"doc": "OpenFile that was packed into the compiled binary. The resulting file does not support many operations such as Chown, Write, etc. but you may read it's contents or copy it to another file i.e. one opened by ssh or sys.",
223+
"params": [{ "name": "path", "type": "String" }],
224+
"retvals": [
225+
{ "name": "f", "type": "File" },
226+
{ "name": "err", "type": "Error" }
227+
]
228+
}
229+
]
230+
},
231+
{
232+
"name": "sys",
233+
"functions": [
234+
{
235+
"name": "openFile",
236+
"doc": "OpenFile uses os.Open to Open a file.",
237+
"params": [{ "name": "path", "type": "String" }],
238+
"retvals": [
239+
{ "name": "f", "type": "File" },
240+
{ "name": "err", "type": "Error" }
241+
]
242+
},
243+
{
244+
"name": "detectOS",
245+
"doc": "DetectOS uses the GOOS variable to determine the OS.",
246+
"params": null,
247+
"retvals": [{ "name": "os", "type": "String" }]
248+
},
249+
{
250+
"name": "exec",
251+
"doc": "Exec uses the os/exec.command to execute the passed executable/params.",
252+
"params": [
253+
{ "name": "executable", "type": "String" },
254+
{ "name": "disown", "type": "?Bool" }
255+
],
256+
"retvals": [
257+
{ "name": "output", "type": "String" },
258+
{ "name": "err", "type": "Error" }
259+
]
260+
},
261+
{
262+
"name": "connections",
263+
"doc": "Connections uses the gopsutil/net to get all connections created by a process (or all by default).",
264+
"params": [{ "name": "parent", "type": "?Process" }],
265+
"retvals": [{ "name": "connections", "type": "[]Connection" }]
266+
},
267+
{
268+
"name": "processes",
269+
"doc": "Processes uses the gopsutil/process to get all processes.",
270+
"params": null,
271+
"retvals": [{ "name": "procs", "type": "[]Process" }]
272+
},
273+
{
274+
"name": "files",
275+
"doc": "Files uses the ioutil.ReadDir to get all files in a given path.",
276+
"params": null,
277+
"retvals": [{ "name": "files", "type": "[]File" }]
278+
}
279+
]
280+
},
281+
{
282+
"name": "assert",
283+
"functions": [
284+
{
285+
"name": "noError",
286+
"doc": "NoError will check if the passed value is a starlark.NoneType, if not it will error out the script. This function may cause a fatal error if the assertion is incorrect.",
287+
"params": [{ "name": "err", "type": "starlark.Value" }],
288+
"retvals": null
289+
},
290+
{
291+
"name": "equal",
292+
"doc": "Equal will check if two values are equal. This function will result in a fatal error if the assertion is incorrect.",
293+
"params": [
294+
{ "name": "expected", "type": "starlark.Value" },
295+
{ "name": "actual", "type": "starlark.Value" }
296+
],
297+
"retvals": null
298+
}
299+
]
300+
}
301+
]
302+
}

0 commit comments

Comments
 (0)