1 // Copyright (C) 2006 Timothy Brownawell <tbrownaw@gmail.com>
2 //
3 // This program is made available under the GNU GPL version 2.0 or
4 // greater. See the accompanying file COPYING for details.
5 //
6 // This program is distributed WITHOUT ANY WARRANTY; without even the
7 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE.
9
10 #include "base.hh"
11 #include "lua.hh"
12
13 #include <signal.h>
14 #include <cstdlib>
15
16 #include "platform.hh"
17 #include "sanity.hh"
18
19 using std::string;
20 using std::malloc;
21 using std::free;
22
23 LUAEXT(get_ostype, )
24 {
25 std::string str;
26 get_system_flavour(str);
27 lua_pushstring(LS, str.c_str());
28 return 1;
29 }
30
31 LUAEXT(existsonpath, )
32 {
33 const char *exe = luaL_checkstring(LS, -1);
34 lua_pushnumber(LS, existsonpath(exe));
35 return 1;
36 }
37
38 LUAEXT(is_executable, )
39 {
40 const char *path = luaL_checkstring(LS, -1);
41 lua_pushboolean(LS, is_executable(path));
42 return 1;
43 }
44
45 LUAEXT(set_executable, )
46 {
47 const char *path = luaL_checkstring(LS, -1);
48 lua_pushnumber(LS, set_executable(path));
49 return 1;
50 }
51
52 LUAEXT(clear_executable, )
53 {
54 const char *path = luaL_checkstring(LS, -1);
55 lua_pushnumber(LS, clear_executable(path));
56 return 1;
57 }
58
59 LUAEXT(spawn, )
60 {
61 int n = lua_gettop(LS);
62 const char *path = luaL_checkstring(LS, 1);
63 char **argv = (char**)malloc((n+1)*sizeof(char*));
64 int i;
65 pid_t ret;
66 if (argv==NULL)
67 return 0;
68 argv[0] = (char*)path;
69 for (i=1; i<n; i++) argv[i] = (char*)luaL_checkstring(LS, i+1);
70 argv[i] = NULL;
71 ret = process_spawn(argv);
72 free(argv);
73 lua_pushnumber(LS, ret);
74 return 1;
75 }
76
77 LUAEXT(spawn_redirected, )
78 {
79 int n = lua_gettop(LS);
80 char const * infile = luaL_checkstring(LS, 1);
81 char const * outfile = luaL_checkstring(LS, 2);
82 char const * errfile = luaL_checkstring(LS, 3);
83 const char *path = luaL_checkstring(LS, 4);
84 n -= 3;
85 char **argv = (char**)malloc((n+1)*sizeof(char*));
86 int i;
87 pid_t ret;
88 if (argv==NULL)
89 return 0;
90 argv[0] = (char*)path;
91 for (i=1; i<n; i++) argv[i] = (char*)luaL_checkstring(LS, i+4);
92 argv[i] = NULL;
93 ret = process_spawn_redirected(infile, outfile, errfile, argv);
94 free(argv);
95 lua_pushnumber(LS, ret);
96 return 1;
97 }
98
99 // Making C functions that return FILE* in Lua is tricky. Especially if it
100 // actually needs to work with multiple lua versions.
101 //
102 // The following routines are inspired by lua/liolib.c from both versions.
103 // The mtn_lua_Stream struct is closer to the 5.2 variant, but the
104 // additional field compared to 5.1 (which only uses FILE*) shouldn't hurt
105 // in Lua 5.1.
106 //
107 // There is a Lua FAQ entitled:
108 // "Why does my library-created file segfault on :close() but work otherwise?"
109 //
110 // However, it's advice seems out-dated and applies more to 5.1.
111
112 typedef struct mtn_lua_Stream {
113 FILE *f;
114 lua_CFunction closef;
115 } mtn_lua_Stream;
116
117 #define topfile(LS) ((mtn_lua_Stream *)luaL_checkudata(LS, 1, LUA_FILEHANDLE))
118
io_fclose(lua_State * LS)119 static int io_fclose (lua_State *LS) {
120 mtn_lua_Stream *s = topfile(LS);
121
122 // Note that in Lua 5.2, aux_close() already resets s->closef to NULL and for
123 // Lua 5.1, it's not relevant, at all. But we set it to &io_fclose() in both
124 // cases, so contents of s->closef differs between Lua versions at this point
125 // in the code. However, it's not used, but only reset to NULL.
126
127 int ok = 1;
128 if (s->f != NULL)
129 ok = (fclose(s->f) == 0);
130
131 s->f = NULL;
132 s->closef = NULL; // just to be extra sure this won't do any harm
133
134 lua_pushboolean(LS, ok);
135
136 return 1;
137 }
138
newstream(lua_State * LS)139 static mtn_lua_Stream *newstream (lua_State *LS) {
140 mtn_lua_Stream *s = (mtn_lua_Stream *)lua_newuserdata(LS, sizeof(mtn_lua_Stream));
141 s->f = NULL; /* file handle is currently `closed' */
142 s->closef = NULL;
143 luaL_getmetatable(LS, LUA_FILEHANDLE);
144 lua_setmetatable(LS, -2);
145
146 #ifdef LUA_ENVIRONINDEX
147 // Lua 5.2 removes C function environments
148 lua_pushcfunction(LS, io_fclose);
149 lua_setfield(LS, LUA_ENVIRONINDEX, "__close");
150 #endif
151
152 return s;
153 }
154
155 LUAEXT(spawn_pipe, )
156 {
157 int n = lua_gettop(LS);
158 char **argv = (char**)malloc((n+1)*sizeof(char*));
159 int i;
160 pid_t pid;
161 if (argv==NULL)
162 return 0;
163 if (n<1)
164 return 0;
165 for (i=0; i<n; i++) argv[i] = (char*)luaL_checkstring(LS, i+1);
166 argv[i] = NULL;
167
168 mtn_lua_Stream *ins = newstream(LS);
169 ins->closef = &io_fclose;
170
171 mtn_lua_Stream *outs = newstream(LS);
172 outs->closef = &io_fclose;
173
174 pid = process_spawn_pipe(argv, &ins->f, &outs->f);
175 free(argv);
176
177 lua_pushnumber(LS, pid);
178
179 return 3;
180 }
181
182 LUAEXT(wait, )
183 {
184 pid_t pid = static_cast<pid_t>(luaL_checknumber(LS, -1));
185 int res;
186 int ret;
187 ret = process_wait(pid, &res);
188 lua_pushnumber(LS, res);
189 lua_pushnumber(LS, ret);
190 return 2;
191 }
192
193 LUAEXT(kill, )
194 {
195 int n = lua_gettop(LS);
196 pid_t pid = static_cast<pid_t>(luaL_checknumber(LS, -2));
197 int sig;
198 if (n>1)
199 sig = static_cast<int>(luaL_checknumber(LS, -1));
200 else
201 sig = SIGTERM;
202 lua_pushnumber(LS, process_kill(pid, sig));
203 return 1;
204 }
205
206 LUAEXT(sleep, )
207 {
208 int seconds = static_cast<int>(luaL_checknumber(LS, -1));
209 lua_pushnumber(LS, process_sleep(seconds));
210 return 1;
211 }
212
213 LUAEXT(get_pid, )
214 {
215 pid_t pid = get_process_id();
216 lua_pushnumber(LS, pid);
217 return 1;
218 }
219
220 // fs extensions
221
222 LUAEXT(mkdir, )
223 {
224 try
225 {
226 char const * dirname = luaL_checkstring(LS, -1);
227 do_mkdir(dirname);
228 lua_pushboolean(LS, true);
229 return 1;
230 }
231 catch(recoverable_failure & e)
232 {
233 lua_pushnil(LS);
234 return 1;
235 }
236 }
237
238 LUAEXT(exists, )
239 {
240 try
241 {
242 char const * name = luaL_checkstring(LS, -1);
243 switch (get_path_status(name))
244 {
245 case path::nonexistent: lua_pushboolean(LS, false); break;
246 case path::file:
247 case path::directory: lua_pushboolean(LS, true); break;
248 }
249 }
250 catch(recoverable_failure & e)
251 {
252 lua_pushnil(LS);
253 }
254 return 1;
255 }
256
257 LUAEXT(isdir, )
258 {
259 try
260 {
261 char const * name = luaL_checkstring(LS, -1);
262 switch (get_path_status(name))
263 {
264 case path::nonexistent:
265 case path::file: lua_pushboolean(LS, false); break;
266 case path::directory: lua_pushboolean(LS, true); break;
267 }
268 }
269 catch(recoverable_failure & e)
270 {
271 lua_pushnil(LS);
272 }
273 return 1;
274 }
275
276 namespace
277 {
278 struct build_table : public dirent_consumer
279 {
build_table__anon614d72e40111::build_table280 build_table(lua_State * st) : st(st), n(1)
281 {
282 lua_newtable(st);
283 }
consume__anon614d72e40111::build_table284 virtual void consume(const char *s)
285 {
286 lua_pushstring(st, s);
287 lua_rawseti(st, -2, n);
288 n++;
289 }
290 private:
291 lua_State * st;
292 unsigned int n;
293 };
294 }
295
296 LUAEXT(read_directory, )
297 {
298 int top = lua_gettop(LS);
299 try
300 {
301 string path(luaL_checkstring(LS, -1));
302 build_table tbl(LS);
303
304 read_directory(path, tbl, tbl, tbl);
305 }
306 catch(recoverable_failure &)
307 {
308 // discard the table and any pending path element
309 lua_settop(LS, top);
310 lua_pushnil(LS);
311 }
312 catch (...)
313 {
314 lua_settop(LS, top);
315 throw;
316 }
317 return 1;
318 }
319
320 // Local Variables:
321 // mode: C++
322 // fill-column: 76
323 // c-file-style: "gnu"
324 // indent-tabs-mode: nil
325 // End:
326 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
327