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 */ 40 static int 41 lua_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 61 static int 62 lua_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 77 static int 78 lua_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 96 static int 97 lua_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 112 static int 113 lua_exit(lua_State *L) 114 { 115 exit(luaL_checkinteger(L, 1)); 116 return 0; 117 } 118 119 static int 120 lua_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 */ 131 static int 132 lua_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 146 static int 147 lua_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 164 static int 165 lua_getchar(lua_State *L) 166 { 167 168 lua_pushinteger(L, getchar()); 169 return 1; 170 } 171 172 static int 173 lua_ischar(lua_State *L) 174 { 175 176 lua_pushboolean(L, ischar()); 177 return 1; 178 } 179 180 static int 181 lua_gets(lua_State *L) 182 { 183 char buf[129]; 184 185 ngets(buf, 128); 186 lua_pushstring(L, buf); 187 return 1; 188 } 189 190 static int 191 lua_time(lua_State *L) 192 { 193 194 lua_pushinteger(L, time(NULL)); 195 return 1; 196 } 197 198 static int 199 lua_delay(lua_State *L) 200 { 201 202 delay((int)luaL_checknumber(L, 1)); 203 return 0; 204 } 205 206 static int 207 lua_getenv(lua_State *L) 208 { 209 lua_pushstring(L, getenv(luaL_checkstring(L, 1))); 210 211 return 1; 212 } 213 214 static int 215 lua_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 226 static int 227 lua_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 237 static int 238 lua_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 249 static int 250 lua_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 278 static int 279 lua_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 297 static int 298 lua_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 */ 339 static int 340 lua_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 } 389 static 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 407 static 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 419 static void 420 lua_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 432 static void 433 lua_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 446 int 447 luaopen_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 475 int 476 luaopen_io(lua_State *L) 477 { 478 luaL_newlib(L, iolib); 479 return 1; 480 } 481