1 /*- 2 * Copyright (c) 2019, 2023 Kyle Evans <kevans@FreeBSD.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <sys/stat.h> 8 #include <sys/utsname.h> 9 #include <sys/wait.h> 10 11 #include <errno.h> 12 #include <fnmatch.h> 13 #include <grp.h> 14 #include <libgen.h> 15 #include <pwd.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <unistd.h> 19 20 #include <lua.h> 21 #include "lauxlib.h" 22 #include "lposix.h" 23 24 static void 25 enforce_max_args(lua_State *L, int max) 26 { 27 int narg; 28 29 narg = lua_gettop(L); 30 luaL_argcheck(L, narg <= max, max + 1, "too many arguments"); 31 } 32 33 /* 34 * Minimal implementation of luaposix needed for internal FreeBSD bits. 35 */ 36 static int 37 lua__exit(lua_State *L) 38 { 39 int code; 40 41 enforce_max_args(L, 1); 42 code = luaL_checkinteger(L, 1); 43 44 _exit(code); 45 } 46 47 static int 48 lua_basename(lua_State *L) 49 { 50 char *inpath, *outpath; 51 52 enforce_max_args(L, 1); 53 inpath = strdup(luaL_checkstring(L, 1)); 54 if (inpath == NULL) { 55 lua_pushnil(L); 56 lua_pushstring(L, strerror(ENOMEM)); 57 lua_pushinteger(L, ENOMEM); 58 return (3); 59 } 60 61 outpath = basename(inpath); 62 lua_pushstring(L, outpath); 63 free(inpath); 64 return (1); 65 } 66 67 static int 68 lua_chmod(lua_State *L) 69 { 70 const char *path; 71 mode_t mode; 72 73 enforce_max_args(L, 2); 74 path = luaL_checkstring(L, 1); 75 mode = (mode_t)luaL_checkinteger(L, 2); 76 77 if (chmod(path, mode) == -1) { 78 lua_pushnil(L); 79 lua_pushstring(L, strerror(errno)); 80 lua_pushinteger(L, errno); 81 return (3); 82 } 83 lua_pushinteger(L, 0); 84 return (1); 85 } 86 87 static int 88 lua_chown(lua_State *L) 89 { 90 const char *path; 91 uid_t owner = (uid_t) -1; 92 gid_t group = (gid_t) -1; 93 94 enforce_max_args(L, 3); 95 96 path = luaL_checkstring(L, 1); 97 if (lua_isinteger(L, 2)) 98 owner = (uid_t) lua_tointeger(L, 2); 99 else if (lua_isstring(L, 2)) { 100 struct passwd *p = getpwnam(lua_tostring(L, 2)); 101 if (p != NULL) 102 owner = p->pw_uid; 103 else 104 return (luaL_argerror(L, 2, 105 lua_pushfstring(L, "unknown user %s", 106 lua_tostring(L, 2)))); 107 } else if (!lua_isnoneornil(L, 2)) { 108 const char *type = luaL_typename(L, 2); 109 return (luaL_argerror(L, 2, 110 lua_pushfstring(L, "integer or string expected, got %s", 111 type))); 112 } 113 114 if (lua_isinteger(L, 3)) 115 group = (gid_t) lua_tointeger(L, 3); 116 else if (lua_isstring(L, 3)) { 117 struct group *g = getgrnam(lua_tostring(L, 3)); 118 if (g != NULL) 119 group = g->gr_gid; 120 else 121 return (luaL_argerror(L, 3, 122 lua_pushfstring(L, "unknown group %s", 123 lua_tostring(L, 3)))); 124 } else if (!lua_isnoneornil(L, 3)) { 125 const char *type = luaL_typename(L, 3); 126 return (luaL_argerror(L, 3, 127 lua_pushfstring(L, "integer or string expected, got %s", 128 type))); 129 } 130 131 if (chown(path, owner, group) == -1) { 132 lua_pushnil(L); 133 lua_pushstring(L, strerror(errno)); 134 lua_pushinteger(L, errno); 135 return (3); 136 } 137 lua_pushinteger(L, 0); 138 return (1); 139 } 140 141 static int 142 lua_pclose(lua_State *L) 143 { 144 int error, fd; 145 146 enforce_max_args(L, 1); 147 148 fd = luaL_checkinteger(L, 1); 149 if (fd < 0) { 150 error = EBADF; 151 goto err; 152 } 153 154 if (close(fd) == 0) { 155 lua_pushinteger(L, 0); 156 return (1); 157 } 158 159 error = errno; 160 err: 161 lua_pushnil(L); 162 lua_pushstring(L, strerror(error)); 163 lua_pushinteger(L, error); 164 return (3); 165 166 } 167 168 static int 169 lua_dup2(lua_State *L) 170 { 171 int error, oldd, newd; 172 173 enforce_max_args(L, 2); 174 175 oldd = luaL_checkinteger(L, 1); 176 if (oldd < 0) { 177 error = EBADF; 178 goto err; 179 } 180 181 newd = luaL_checkinteger(L, 2); 182 if (newd < 0) { 183 error = EBADF; 184 goto err; 185 } 186 187 error = dup2(oldd, newd); 188 if (error >= 0) { 189 lua_pushinteger(L, error); 190 return (1); 191 } 192 193 error = errno; 194 err: 195 lua_pushnil(L); 196 lua_pushstring(L, strerror(error)); 197 lua_pushinteger(L, error); 198 return (3); 199 } 200 201 static int 202 lua_execp(lua_State *L) 203 { 204 int argc, error; 205 const char *file; 206 const char **argv; 207 208 enforce_max_args(L, 2); 209 210 file = luaL_checkstring(L, 1); 211 luaL_checktype(L, 2, LUA_TTABLE); 212 213 lua_len(L, 2); 214 argc = lua_tointeger(L, -1); 215 216 /* 217 * Use lua_newuserdatauv() to allocate a scratch buffer that is tracked 218 * and freed by lua's GC. This avoid any chance of a leak if a lua error 219 * is raised later in this function (e.g. by luaL_argerror()). 220 * The (argc + 2) size gives enough space in the buffer for argv[0] and 221 * the terminating NULL. 222 */ 223 argv = lua_newuserdatauv(L, (argc + 2) * sizeof(char *), 0); 224 225 /* 226 * Sequential tables in lua start at index 1 by convention. 227 * If there happens to be a string at index 0, use that to 228 * override the default argv[0]. This matches the lposix API. 229 */ 230 lua_pushinteger(L, 0); 231 lua_gettable(L, 2); 232 argv[0] = lua_tostring(L, -1); 233 if (argv[0] == NULL) { 234 argv[0] = file; 235 } 236 237 for (int i = 1; i <= argc; i++) { 238 lua_pushinteger(L, i); 239 lua_gettable(L, 2); 240 argv[i] = lua_tostring(L, -1); 241 if (argv[i] == NULL) { 242 luaL_argerror(L, 2, 243 "argv table must contain only strings"); 244 } 245 } 246 argv[argc + 1] = NULL; 247 248 execvp(file, (char **)argv); 249 error = errno; 250 251 lua_pushnil(L); 252 lua_pushstring(L, strerror(error)); 253 lua_pushinteger(L, error); 254 return (3); 255 } 256 257 static int 258 lua_fnmatch(lua_State *L) 259 { 260 const char *pattern, *string; 261 int flags; 262 263 enforce_max_args(L, 3); 264 pattern = luaL_checkstring(L, 1); 265 string = luaL_checkstring(L, 2); 266 flags = luaL_optinteger(L, 3, 0); 267 268 lua_pushinteger(L, fnmatch(pattern, string, flags)); 269 270 return (1); 271 } 272 273 static int 274 lua_uname(lua_State *L) 275 { 276 struct utsname name; 277 int error; 278 279 enforce_max_args(L, 0); 280 281 error = uname(&name); 282 if (error != 0) { 283 error = errno; 284 lua_pushnil(L); 285 lua_pushstring(L, strerror(error)); 286 lua_pushinteger(L, error); 287 return (3); 288 } 289 290 lua_newtable(L); 291 #define setkv(f) do { \ 292 lua_pushstring(L, name.f); \ 293 lua_setfield(L, -2, #f); \ 294 } while (0) 295 setkv(sysname); 296 setkv(nodename); 297 setkv(release); 298 setkv(version); 299 setkv(machine); 300 #undef setkv 301 302 return (1); 303 } 304 305 static int 306 lua_dirname(lua_State *L) 307 { 308 char *inpath, *outpath; 309 310 enforce_max_args(L, 1); 311 312 inpath = strdup(luaL_checkstring(L, 1)); 313 if (inpath == NULL) { 314 lua_pushnil(L); 315 lua_pushstring(L, strerror(ENOMEM)); 316 lua_pushinteger(L, ENOMEM); 317 return (3); 318 } 319 320 outpath = dirname(inpath); 321 lua_pushstring(L, outpath); 322 free(inpath); 323 return (1); 324 } 325 326 static int 327 lua_fork(lua_State *L) 328 { 329 pid_t pid; 330 331 enforce_max_args(L, 0); 332 333 pid = fork(); 334 if (pid < 0) { 335 lua_pushnil(L); 336 lua_pushstring(L, strerror(errno)); 337 lua_pushinteger(L, errno); 338 return (3); 339 } 340 341 lua_pushinteger(L, pid); 342 return (1); 343 } 344 345 static int 346 lua_getpid(lua_State *L) 347 { 348 enforce_max_args(L, 0); 349 350 lua_pushinteger(L, getpid()); 351 return (1); 352 } 353 354 static int 355 lua_pipe(lua_State *L) 356 { 357 int error, fd[2]; 358 359 enforce_max_args(L, 0); 360 361 error = pipe(fd); 362 if (error != 0) { 363 lua_pushnil(L); 364 lua_pushstring(L, strerror(errno)); 365 lua_pushinteger(L, errno); 366 return (1); 367 } 368 369 lua_pushinteger(L, fd[0]); 370 lua_pushinteger(L, fd[1]); 371 return (2); 372 } 373 374 static int 375 lua_read(lua_State *L) 376 { 377 char *buf; 378 ssize_t ret; 379 size_t sz; 380 int error, fd; 381 382 enforce_max_args(L, 2); 383 fd = luaL_checkinteger(L, 1); 384 sz = luaL_checkinteger(L, 2); 385 386 if (fd < 0) { 387 error = EBADF; 388 goto err; 389 } 390 391 buf = malloc(sz); 392 if (buf == NULL) 393 goto err; 394 395 /* 396 * For 0-byte reads, we'll still push the empty string and let the 397 * caller deal with EOF to match lposix semantics. 398 */ 399 ret = read(fd, buf, sz); 400 if (ret >= 0) 401 lua_pushlstring(L, buf, ret); 402 else if (ret < 0) 403 error = errno; /* Save to avoid clobber by free() */ 404 405 free(buf); 406 if (error != 0) 407 goto err; 408 409 /* Just the string pushed. */ 410 return (1); 411 err: 412 lua_pushnil(L); 413 lua_pushstring(L, strerror(error)); 414 lua_pushinteger(L, error); 415 return (3); 416 } 417 418 static int 419 lua_realpath(lua_State *L) 420 { 421 const char *inpath; 422 char *outpath; 423 424 enforce_max_args(L, 1); 425 inpath = luaL_checkstring(L, 1); 426 427 outpath = realpath(inpath, NULL); 428 if (outpath == NULL) { 429 lua_pushnil(L); 430 lua_pushstring(L, strerror(errno)); 431 lua_pushinteger(L, errno); 432 return (3); 433 } 434 435 lua_pushstring(L, outpath); 436 free(outpath); 437 return (1); 438 } 439 440 static int 441 lua_wait(lua_State *L) 442 { 443 pid_t pid; 444 int options, status; 445 446 enforce_max_args(L, 2); 447 pid = luaL_optinteger(L, 1, -1); 448 options = luaL_optinteger(L, 2, 0); 449 450 status = 0; 451 pid = waitpid(pid, &status, options); 452 if (pid < 0) { 453 lua_pushnil(L); 454 lua_pushstring(L, strerror(errno)); 455 lua_pushinteger(L, errno); 456 return (3); 457 } 458 459 lua_pushinteger(L, pid); 460 if (pid == 0) { 461 lua_pushliteral(L, "running"); 462 return (2); 463 } 464 465 if (WIFCONTINUED(status)) { 466 lua_pushliteral(L, "continued"); 467 return (2); 468 } else if(WIFSTOPPED(status)) { 469 lua_pushliteral(L, "stopped"); 470 lua_pushinteger(L, WSTOPSIG(status)); 471 return (3); 472 } else if (WIFEXITED(status)) { 473 lua_pushliteral(L, "exited"); 474 lua_pushinteger(L, WEXITSTATUS(status)); 475 return (3); 476 } else if (WIFSIGNALED(status)) { 477 lua_pushliteral(L, "killed"); 478 lua_pushinteger(L, WTERMSIG(status)); 479 return (3); 480 } 481 482 return (1); 483 } 484 485 static int 486 lua_write(lua_State *L) 487 { 488 const char *buf; 489 size_t bufsz, sz; 490 ssize_t ret; 491 off_t offset; 492 int error, fd; 493 494 enforce_max_args(L, 4); 495 496 fd = luaL_checkinteger(L, 1); 497 if (fd < 0) { 498 error = EBADF; 499 goto err; 500 } 501 502 buf = luaL_checkstring(L, 2); 503 504 bufsz = lua_rawlen(L, 2); 505 sz = luaL_optinteger(L, 3, bufsz); 506 507 offset = luaL_optinteger(L, 4, 0); 508 509 510 if ((size_t)offset > bufsz || offset + sz > bufsz) { 511 lua_pushnil(L); 512 lua_pushfstring(L, 513 "write: invalid access offset %zu, size %zu in a buffer size %zu", 514 offset, sz, bufsz); 515 lua_pushinteger(L, EINVAL); 516 return (3); 517 } 518 519 ret = write(fd, buf + offset, sz); 520 if (ret < 0) { 521 error = errno; 522 goto err; 523 } 524 525 lua_pushinteger(L, ret); 526 return (1); 527 err: 528 lua_pushnil(L); 529 lua_pushstring(L, strerror(error)); 530 lua_pushinteger(L, error); 531 return (3); 532 } 533 534 #define REG_DEF(n, func) { #n, func } 535 #define REG_SIMPLE(n) REG_DEF(n, lua_ ## n) 536 static const struct luaL_Reg libgenlib[] = { 537 REG_SIMPLE(basename), 538 REG_SIMPLE(dirname), 539 { NULL, NULL }, 540 }; 541 542 static const struct luaL_Reg stdliblib[] = { 543 REG_SIMPLE(realpath), 544 { NULL, NULL }, 545 }; 546 547 static const struct luaL_Reg fnmatchlib[] = { 548 REG_SIMPLE(fnmatch), 549 { NULL, NULL }, 550 }; 551 552 static const struct luaL_Reg sys_statlib[] = { 553 REG_SIMPLE(chmod), 554 { NULL, NULL }, 555 }; 556 557 static const struct luaL_Reg sys_utsnamelib[] = { 558 REG_SIMPLE(uname), 559 { NULL, NULL }, 560 }; 561 562 static const struct luaL_Reg sys_waitlib[] = { 563 REG_SIMPLE(wait), 564 {NULL, NULL}, 565 }; 566 567 static const struct luaL_Reg unistdlib[] = { 568 REG_SIMPLE(_exit), 569 REG_SIMPLE(chown), 570 REG_DEF(close, lua_pclose), 571 REG_SIMPLE(dup2), 572 REG_SIMPLE(execp), 573 REG_SIMPLE(fork), 574 REG_SIMPLE(getpid), 575 REG_SIMPLE(pipe), 576 REG_SIMPLE(read), 577 REG_SIMPLE(write), 578 { NULL, NULL }, 579 }; 580 581 #undef REG_SIMPLE 582 #undef REG_DEF 583 584 int 585 luaopen_posix_libgen(lua_State *L) 586 { 587 luaL_newlib(L, libgenlib); 588 return (1); 589 } 590 591 int 592 luaopen_posix_stdlib(lua_State *L) 593 { 594 luaL_newlib(L, stdliblib); 595 return (1); 596 } 597 598 int 599 luaopen_posix_fnmatch(lua_State *L) 600 { 601 luaL_newlib(L, fnmatchlib); 602 603 #define setkv(f) do { \ 604 lua_pushinteger(L, f); \ 605 lua_setfield(L, -2, #f); \ 606 } while (0) 607 setkv(FNM_PATHNAME); 608 setkv(FNM_NOESCAPE); 609 setkv(FNM_NOMATCH); 610 setkv(FNM_PERIOD); 611 #undef setkv 612 613 return 1; 614 } 615 616 int 617 luaopen_posix_sys_stat(lua_State *L) 618 { 619 luaL_newlib(L, sys_statlib); 620 return (1); 621 } 622 623 int 624 luaopen_posix_sys_wait(lua_State *L) 625 { 626 luaL_newlib(L, sys_waitlib); 627 628 #define lua_pushflag(L, flag) do { \ 629 lua_pushinteger(L, flag); \ 630 lua_setfield(L, -2, #flag); \ 631 } while(0) 632 633 /* Only these two exported by lposix */ 634 lua_pushflag(L, WNOHANG); 635 lua_pushflag(L, WUNTRACED); 636 637 lua_pushflag(L, WCONTINUED); 638 lua_pushflag(L, WSTOPPED); 639 #ifdef WTRAPPED 640 lua_pushflag(L, WTRAPPED); 641 #endif 642 lua_pushflag(L, WEXITED); 643 lua_pushflag(L, WNOWAIT); 644 #undef lua_pushflag 645 646 return (1); 647 } 648 649 int 650 luaopen_posix_sys_utsname(lua_State *L) 651 { 652 luaL_newlib(L, sys_utsnamelib); 653 return 1; 654 } 655 656 int 657 luaopen_posix_unistd(lua_State *L) 658 { 659 luaL_newlib(L, unistdlib); 660 return (1); 661 } 662