Reduce differences with root_skels in contrib.
[dragonfly.git] / contrib / bsdinstaller-1.1.6 / src / lib / lua / pty / pty.c
1 /*
2  * pty.c - pty bindings for Lua
3  * $Id: pty.c,v 1.21 2005/04/04 13:56:37 den Exp $
4  *
5  * This file was derived in part from DragonFly BSD's
6  * src/usr.bin/script/script.c, which contains the following license:
7  */
8 /*
9  * Copyright (c) 1980, 1992, 1993
10  *      The Regents of the University of California.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40
41 #include <sys/types.h>
42 #include <sys/time.h>
43 #include <sys/wait.h>
44
45 #include <errno.h>
46 #include <libutil.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52
53 #include "lua50/lua.h"
54 #include "lua50/lauxlib.h"
55 #include "lua50/lualib.h"
56
57 #ifdef WEXITSTATUS
58 #define WEXIT_TYPE int
59 #else
60 #define WEXIT_TYPE union wait
61 #endif
62
63 struct lua_pty {
64         FILE            *stream;
65         pid_t            child;
66 };
67
68 #define PTY_TIMEOUT     -1
69 #define PTY_EOF         -2
70
71 LUA_API int luaopen_lpty(lua_State *);
72
73 /*** UTILTIES ***/
74
75 /*
76  * Given the names of two global variables, the first a regular
77  * 'Class' table, and the second a metatable which will be attached
78  * to all object 'instances':
79  * - add an __index property to the metatable that redirects
80  *   all accesses on the instance to the class table;
81  * - add a __metatable propery to the metatable, to hide it.
82  */
83 static void
84 lua_set_instance_handler(lua_State *L,
85                          const char *table_name, const char *metatable_name)
86 {
87         int metatable_idx, methods_idx;
88
89         lua_pushstring(L, table_name);          /* name of our 'class' table */
90         lua_gettable(L, LUA_GLOBALSINDEX);      /* get it from globals */
91         methods_idx = lua_gettop(L);            /* Find its position on the stack */
92
93         lua_pushstring(L, metatable_name);      /* name of our metatable */
94         lua_gettable(L, LUA_GLOBALSINDEX);      /* get it from globals */
95         metatable_idx = lua_gettop(L);          /* Find its position on the stack */
96
97         /*
98          * Add __index event to metatable (metatable.__index = methods).
99          * This lets the Lua script refer to the methods by indexing
100          * the instance variable like so: x:y(z).
101          */
102         lua_pushliteral(L, "__index");
103         lua_pushvalue(L, methods_idx);
104         lua_settable(L, metatable_idx);
105
106         lua_pushliteral(L, "__metatable");      /* hide metatable */
107         lua_pushvalue(L, methods_idx);
108         lua_settable(L, metatable_idx);         /* metatable.__metatable = methods */
109
110         lua_pop(L, 2);
111 }
112
113 /*** STACK ACCESS ***/
114
115 static struct lua_pty *
116 lua_check_pty(lua_State *L, int ch_index)
117 {
118         luaL_checktype(L, ch_index, LUA_TUSERDATA);
119         lua_getmetatable(L, ch_index);
120         lua_pushliteral(L, "PtyMeta");
121         lua_rawget(L, LUA_GLOBALSINDEX);
122         if (!lua_rawequal(L, -1, -2))
123                 luaL_typerror(L, ch_index, "Pty");
124         lua_pop(L, 2);
125         return((struct lua_pty *)lua_unboxpointer(L, ch_index));
126 }
127
128 static struct lua_pty *
129 lua_push_pty(lua_State *L, struct lua_pty *x)
130 {
131         lua_boxpointer(L, x);
132         lua_pushliteral(L, "PtyMeta");
133         lua_gettable(L, LUA_GLOBALSINDEX);
134         lua_setmetatable(L, -2);
135         return(x);
136 }
137
138 /*** CONSTRUCTOR/DESTRUCTOR ***/
139
140 static int 
141 lua_pty_open(lua_State *L)
142 {
143         struct lua_pty *pty;
144         int master, slave;
145
146         pty = malloc(sizeof(struct lua_pty));
147         if (pty == NULL) {
148                 lua_pushnil(L);
149                 lua_pushnumber(L, ENOMEM);
150                 return(2);
151         }
152         pty->stream = NULL;
153         if (openpty(&master, &slave, NULL, NULL, NULL) == -1) {
154                 lua_pushnil(L);
155                 lua_pushnumber(L, errno);
156                 return(2);
157         }
158
159         pty->child = fork();
160         if (pty->child < 0) {
161                 lua_pushnil(L);
162                 lua_pushnumber(L, errno);
163                 return(2);
164         }
165         if (pty->child == 0) {
166                 const char *shell = "/bin/sh";
167
168                 close(master);
169                 login_tty(slave);
170                 execl(shell, shell, "-c", luaL_checkstring(L, 1), NULL);
171                 /* if we made it here, an error occurred! */
172         }
173         close(slave);
174
175         /*
176          * Convert the file descriptor into a stream, or die trying.
177          */
178         if ((pty->stream = fdopen(master, "r+")) == NULL) {
179                 WEXIT_TYPE status;
180
181                 kill(pty->child, SIGTERM);
182                 if (waitpid(pty->child, (int *)&status, 0) != pty->child) {
183                         lua_pushnil(L);
184                         lua_pushnumber(L, errno);
185                         return(2);
186                 }
187                 lua_pushnil(L);
188                 lua_pushnumber(L, errno);
189                 return(2);
190         }
191
192         lua_push_pty(L, pty);
193         return(1);
194 }
195
196 /******* METHODS *******/
197
198 static int
199 lua_pty_readline(lua_State *L)
200 {
201         struct lua_pty *pty;
202         long msec = 0;
203         int n, len;
204         char line[4096];
205         fd_set rfd;
206         struct timeval tv;
207         struct timeval *tvp = NULL;
208
209         pty = lua_check_pty(L, 1);
210         if (lua_isnumber(L, 2)) {
211                 msec = lua_tonumber(L, 2);
212                 tvp = &tv;
213         }
214         lua_pop(L, 2);
215
216         FD_ZERO(&rfd);
217         FD_SET(fileno(pty->stream), &rfd);
218         if (tvp != NULL) {
219                 tv.tv_sec = msec / 1000;
220                 tv.tv_usec = (msec % 1000) * 1000;
221         }
222         n = select(fileno(pty->stream) + 1, &rfd, 0, 0, tvp);
223         if (n < 0) {
224                 lua_pushnil(L);
225                 lua_pushnumber(L, errno);
226                 return(2);
227         } else if (n > 0 && FD_ISSET(fileno(pty->stream), &rfd)) {
228                 if (fgets(line, sizeof(line) - 1, pty->stream) == NULL) {
229                         lua_pushnil(L);
230                         if (feof(pty->stream))
231                                 lua_pushnumber(L, PTY_EOF);
232                         else
233                                 lua_pushnumber(L, errno);                       
234                         return(2);
235                 } else {
236                         len = strlen(line);
237                         while (len > 0 && (line[len - 1] == '\n' ||
238                                            line[len - 1] == '\r')) {
239                                 line[--len] = '\0';
240                         }
241                         lua_pushstring(L, line);
242                         return(1);
243                 }
244         } else {
245                 lua_pushnil(L);
246                 lua_pushnumber(L, PTY_TIMEOUT);
247                 return(2);
248         }
249 }
250
251 static int
252 lua_pty_write(lua_State *L)
253 {
254         struct lua_pty *pty;
255         const char *string;
256
257         pty = lua_check_pty(L, 1);
258         string = luaL_checkstring(L, 2);
259         lua_pop(L, 2);
260
261         fwrite(string, 1, strlen(string), pty->stream);
262         return(0);
263 }
264
265 static int
266 lua_pty_flush(lua_State *L)
267 {
268         struct lua_pty *pty;
269         int result;
270
271         pty = lua_check_pty(L, 1);
272
273         result = fflush(pty->stream);
274         lua_pushnumber(L, result);
275         return(1);
276 }
277
278 static int
279 lua_pty_close(lua_State *L)
280 {
281         struct lua_pty *pty;
282         WEXIT_TYPE status;
283         int e = 0;
284
285         pty = lua_check_pty(L, 1);
286
287         if (pty->stream == NULL) {
288                 /*
289                  * It's already been closed.
290                  * Don't try to close it again.
291                  */
292                 lua_pushnumber(L, -1);
293                 return(1);
294         }
295
296         fclose(pty->stream);
297         pty->stream = NULL;
298
299         if (waitpid(pty->child, (int *)&status, 0) != pty->child) {
300                 lua_pushnil(L);
301                 lua_pushnumber(L, errno);
302                 return(2);
303         }
304
305         if (WIFEXITED(status)) {
306                 e = WEXITSTATUS(status);
307         } else if (WIFSIGNALED(status)) {
308                 e = WTERMSIG(status);
309         } else {
310                 /* Only happens when system is out of file descriptors */
311                 e = 1;
312         }
313
314         lua_pushnumber(L, e);
315         return(1);
316 }
317
318 static int
319 lua_pty_signal(lua_State *L)
320 {
321         struct lua_pty *pty;
322         int signo, result;
323
324         pty = lua_check_pty(L, 1);
325         signo = luaL_checkint(L, 2);
326         result = kill(pty->child, signo);
327         lua_pushnumber(L, result);
328
329         return(1);
330 }
331
332 /**** Binding Tables ****/
333
334 const luaL_reg pty_methods[] = {
335         {"open",        lua_pty_open },
336         {"readline",    lua_pty_readline },
337         {"write",       lua_pty_write },
338         {"flush",       lua_pty_flush },
339         {"close",       lua_pty_close },
340         {"signal",      lua_pty_signal },
341         {0, 0}
342 };
343
344 const luaL_reg pty_meta_methods[] = {
345         {"__gc",        lua_pty_close },
346         {0, 0}
347 };
348
349 /*** REGISTER ***/
350
351 LUA_API int
352 luaopen_lpty(lua_State *L)
353 {
354         int methods_idx;
355
356         luaL_openlib(L, "Pty", pty_methods, 0);             /* fill methods table */
357         luaL_openlib(L, "PtyMeta", pty_meta_methods,  0);   /* fill metatable */
358         lua_pop(L, 1);
359
360         lua_set_instance_handler(L, "Pty", "PtyMeta");
361
362         /*
363          * Add some symbolic constants.
364          */
365         methods_idx = lua_gettop(L);
366
367         lua_pushliteral(L, "TIMEOUT");
368         lua_pushnumber(L, PTY_TIMEOUT);
369         lua_settable(L, methods_idx);
370
371         lua_pushliteral(L, "EOF");
372         lua_pushnumber(L, PTY_EOF);
373         lua_settable(L, methods_idx);
374
375         lua_pushliteral(L, "SIGTERM");
376         lua_pushnumber(L, SIGTERM);
377         lua_settable(L, methods_idx);
378
379         return(1);
380 }