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