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