1/*-
2 * Copyright (c) 2014 Pedro Souza <pedrosouza@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/param.h>
29
30#include "lua.h"
31#include "lauxlib.h"
32#include "lstd.h"
33#include "lutils.h"
34#include "bootstrap.h"
35
36/*
37 * Like loader.perform, except args are passed already parsed
38 * on the stack.
39 */
40static int
41lua_command(lua_State *L)
42{
43	int	i;
44	int	res = 1;
45	int 	argc = lua_gettop(L);
46	char	**argv;
47
48	argv = malloc(sizeof(char *) * (argc + 1));
49	if (argv == NULL)
50		return 0;
51	for (i = 0; i < argc; i++)
52		argv[i] = (char *)(intptr_t)luaL_checkstring(L, i + 1);
53	argv[argc] = NULL;
54	res = interp_builtin_cmd(argc, argv);
55	free(argv);
56	lua_pushinteger(L, res);
57
58	return 1;
59}
60
61static int
62lua_has_command(lua_State *L)
63{
64	const char	*cmd;
65
66	cmd = luaL_checkstring(L, 1);
67	if (interp_has_builtin_cmd(cmd)) {
68		lua_pushboolean(L, 1);
69		return 1;
70	}
71
72	lua_pushnil(L);
73	lua_pushstring(L, "Builtin command not found");
74	return 2;
75}
76
77static int
78lua_has_feature(lua_State *L)
79{
80	const char	*feature;
81	char *msg;
82
83	feature = luaL_checkstring(L, 1);
84
85	if (feature_name_is_enabled(feature)) {
86		lua_pushboolean(L, 1);
87		return 1;
88	}
89
90	lua_pushnil(L);
91	lua_pushstring(L, "Feature not enabled");
92	return 2;
93}
94
95
96static int
97lua_perform(lua_State *L)
98{
99	int	argc;
100	char	**argv;
101	int	res = 1;
102
103	if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) {
104		res = interp_builtin_cmd(argc, argv);
105		free(argv);
106	}
107	lua_pushinteger(L, res);
108
109	return 1;
110}
111
112static int
113lua_exit(lua_State *L)
114{
115	exit(luaL_checkinteger(L, 1));
116	return 0;
117}
118
119static int
120lua_command_error(lua_State *L)
121{
122
123	lua_pushstring(L, command_errbuf);
124	return 1;
125}
126
127/*
128 * Accepts a space-delimited loader command and runs it through the standard
129 * loader parsing, as if it were executed at the loader prompt by the user.
130 */
131static int
132lua_interpret(lua_State *L)
133{
134	const char	*interp_string;
135
136	if (lua_gettop(L) != 1) {
137		lua_pushnil(L);
138		return 1;
139	}
140
141	interp_string = luaL_checkstring(L, 1);
142	lua_pushinteger(L, interp_run(interp_string));
143	return 1;
144}
145
146static int
147lua_parse(lua_State *L)
148{
149	int	argc, nargc;
150	char	**argv;
151
152	if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) {
153		for (nargc = 0; nargc < argc; ++nargc) {
154			lua_pushstring(L, argv[nargc]);
155		}
156		free(argv);
157		return nargc;
158	}
159
160	lua_pushnil(L);
161	return 1;
162}
163
164static int
165lua_getchar(lua_State *L)
166{
167
168	lua_pushinteger(L, getchar());
169	return 1;
170}
171
172static int
173lua_ischar(lua_State *L)
174{
175
176	lua_pushboolean(L, ischar());
177	return 1;
178}
179
180static int
181lua_gets(lua_State *L)
182{
183	char	buf[129];
184
185	ngets(buf, 128);
186	lua_pushstring(L, buf);
187	return 1;
188}
189
190static int
191lua_time(lua_State *L)
192{
193
194	lua_pushinteger(L, time(NULL));
195	return 1;
196}
197
198static int
199lua_delay(lua_State *L)
200{
201
202	delay((int)luaL_checknumber(L, 1));
203	return 0;
204}
205
206static int
207lua_getenv(lua_State *L)
208{
209	lua_pushstring(L, getenv(luaL_checkstring(L, 1)));
210
211	return 1;
212}
213
214static int
215lua_setenv(lua_State *L)
216{
217	const char *key, *val;
218
219	key = luaL_checkstring(L, 1);
220	val = luaL_checkstring(L, 2);
221	lua_pushinteger(L, setenv(key, val, 1));
222
223	return 1;
224}
225
226static int
227lua_unsetenv(lua_State *L)
228{
229	const char	*ev;
230
231	ev = luaL_checkstring(L, 1);
232	lua_pushinteger(L, unsetenv(ev));
233
234	return 1;
235}
236
237static int
238lua_printc(lua_State *L)
239{
240	ssize_t cur, l;
241	const char *s = luaL_checklstring(L, 1, &l);
242
243	for (cur = 0; cur < l; ++cur)
244		putchar((unsigned char)*(s++));
245
246	return 1;
247}
248
249static int
250lua_openfile(lua_State *L)
251{
252	const char	*mode, *str;
253	int	nargs;
254
255	nargs = lua_gettop(L);
256	if (nargs < 1 || nargs > 2) {
257		lua_pushnil(L);
258		return 1;
259	}
260	str = lua_tostring(L, 1);
261	mode = "r";
262	if (nargs > 1) {
263		mode = lua_tostring(L, 2);
264		if (mode == NULL) {
265			lua_pushnil(L);
266			return 1;
267		}
268	}
269	FILE * f = fopen(str, mode);
270	if (f != NULL) {
271		FILE ** ptr = (FILE**)lua_newuserdata(L, sizeof(FILE**));
272		*ptr = f;
273	} else
274		lua_pushnil(L);
275	return 1;
276}
277
278static int
279lua_closefile(lua_State *L)
280{
281	FILE ** f;
282	if (lua_gettop(L) != 1) {
283		lua_pushboolean(L, 0);
284		return 1;
285	}
286
287	f = (FILE**)lua_touserdata(L, 1);
288	if (f != NULL && *f != NULL) {
289		lua_pushboolean(L, fclose(*f) == 0 ? 1 : 0);
290		*f = NULL;
291	} else
292		lua_pushboolean(L, 0);
293
294	return 1;
295}
296
297static int
298lua_readfile(lua_State *L)
299{
300	FILE	**f;
301	size_t	size, r;
302	char * buf;
303
304	if (lua_gettop(L) < 1 || lua_gettop(L) > 2) {
305		lua_pushnil(L);
306		lua_pushinteger(L, 0);
307		return 2;
308	}
309
310	f = (FILE**)lua_touserdata(L, 1);
311
312	if (f == NULL || *f == NULL) {
313		lua_pushnil(L);
314		lua_pushinteger(L, 0);
315		return 2;
316	}
317
318	if (lua_gettop(L) == 2)
319		size = (size_t)lua_tonumber(L, 2);
320	else
321		size = (*f)->size;
322
323
324	buf = (char*)malloc(size);
325	r = fread(buf, 1, size, *f);
326	lua_pushlstring(L, buf, r);
327	free(buf);
328	lua_pushinteger(L, r);
329
330	return 2;
331}
332
333/*
334 * Implements io.write(file, ...)
335 * Any number of string and number arguments may be passed to it,
336 * and it will return the number of bytes written, or nil, an error string, and
337 * the errno.
338 */
339static int
340lua_writefile(lua_State *L)
341{
342	FILE	**f;
343	const char	*buf;
344	int	i, nargs;
345	size_t	bufsz, w, wrsz;
346
347	buf = NULL;
348	bufsz = 0;
349	w = 0;
350	wrsz = 0;
351	nargs = lua_gettop(L);
352	if (nargs < 2) {
353		errno = EINVAL;
354		return luaL_fileresult(L, 0, NULL);
355	}
356
357	f = (FILE**)lua_touserdata(L, 1);
358
359	if (f == NULL || *f == NULL) {
360		errno = EINVAL;
361		return luaL_fileresult(L, 0, NULL);
362	}
363
364	/* Do a validation pass first */
365	for (i = 0; i < nargs - 1; i++) {
366		/*
367		 * With Lua's API, lua_isstring really checks if the argument
368		 * is a string or a number.  The latter will be implicitly
369		 * converted to a string by our later call to lua_tolstring.
370		 */
371		if (!lua_isstring(L, i + 2)) {
372			errno = EINVAL;
373			return luaL_fileresult(L, 0, NULL);
374		}
375	}
376	for (i = 0; i < nargs - 1; i++) {
377		/* We've already validated; there's no chance of failure */
378		buf = lua_tolstring(L, i + 2, &bufsz);
379		wrsz = fwrite(buf, 1, bufsz, *f);
380		if (wrsz < bufsz)
381			return luaL_fileresult(L, 0, NULL);
382		w += wrsz;
383	}
384	lua_pushinteger(L, w);
385	return 1;
386}
387
388#define REG_SIMPLE(n)	{ #n, lua_ ## n }
389static const struct luaL_Reg loaderlib[] = {
390	REG_SIMPLE(command),
391	REG_SIMPLE(command_error),
392	REG_SIMPLE(delay),
393	REG_SIMPLE(exit),
394	REG_SIMPLE(getenv),
395	REG_SIMPLE(has_command),
396	REG_SIMPLE(has_feature),
397	REG_SIMPLE(interpret),
398	REG_SIMPLE(parse),
399	REG_SIMPLE(perform),
400	REG_SIMPLE(printc),	/* Also registered as the global 'printc' */
401	REG_SIMPLE(setenv),
402	REG_SIMPLE(time),
403	REG_SIMPLE(unsetenv),
404	{ NULL, NULL },
405};
406
407static const struct luaL_Reg iolib[] = {
408	{ "close", lua_closefile },
409	REG_SIMPLE(getchar),
410	REG_SIMPLE(gets),
411	REG_SIMPLE(ischar),
412	{ "open", lua_openfile },
413	{ "read", lua_readfile },
414	{ "write", lua_writefile },
415	{ NULL, NULL },
416};
417#undef REG_SIMPLE
418
419static void
420lua_add_feature(void *cookie, const char *name, const char *desc, bool enabled)
421{
422	lua_State *L = cookie;
423
424	/*
425	 * The feature table consists solely of features that are enabled, and
426	 * their associated descriptions for debugging purposes.
427	 */
428	lua_pushstring(L, desc);
429	lua_setfield(L, -2, name);
430}
431
432static void
433lua_add_features(lua_State *L)
434{
435
436	lua_newtable(L);
437	feature_iter(&lua_add_feature, L);
438
439	/*
440	 * We should still have just the table on the stack after we're done
441	 * iterating.
442	 */
443	lua_setfield(L, -2, "features");
444}
445
446int
447luaopen_loader(lua_State *L)
448{
449	luaL_newlib(L, loaderlib);
450	/* Add loader.machine and loader.machine_arch properties */
451	lua_pushstring(L, MACHINE);
452	lua_setfield(L, -2, "machine");
453	lua_pushstring(L, MACHINE_ARCH);
454	lua_setfield(L, -2, "machine_arch");
455	lua_pushstring(L, LUA_PATH);
456	lua_setfield(L, -2, "lua_path");
457	lua_pushinteger(L, bootprog_rev);
458	lua_setfield(L, -2, "version");
459	lua_pushinteger(L, CMD_OK);
460	lua_setfield(L, -2, "CMD_OK");
461	lua_pushinteger(L, CMD_WARN);
462	lua_setfield(L, -2, "CMD_WARN");
463	lua_pushinteger(L, CMD_ERROR);
464	lua_setfield(L, -2, "CMD_ERROR");
465	lua_pushinteger(L, CMD_CRIT);
466	lua_setfield(L, -2, "CMD_CRIT");
467	lua_pushinteger(L, CMD_FATAL);
468	lua_setfield(L, -2, "CMD_FATAL");
469	lua_add_features(L);
470	/* Set global printc to loader.printc */
471	lua_register(L, "printc", lua_printc);
472	return 1;
473}
474
475int
476luaopen_io(lua_State *L)
477{
478	luaL_newlib(L, iolib);
479	return 1;
480}
481