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 <grp.h> 13 #include <libgen.h> 14 #include <pwd.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <unistd.h> 18 19 #include <lua.h> 20 #include "lauxlib.h" 21 #include "lposix.h" 22 23 /* 24 * Minimal implementation of luaposix needed for internal FreeBSD bits. 25 */ 26 static int 27 lua__exit(lua_State *L) 28 { 29 int code, narg; 30 31 narg = lua_gettop(L); 32 luaL_argcheck(L, narg == 1, 1, "_exit takes exactly one argument"); 33 34 code = luaL_checkinteger(L, 1); 35 _exit(code); 36 } 37 38 static int 39 lua_basename(lua_State *L) 40 { 41 char *inpath, *outpath; 42 int narg; 43 44 narg = lua_gettop(L); 45 luaL_argcheck(L, narg > 0, 1, "at least one argument required"); 46 inpath = strdup(luaL_checkstring(L, 1)); 47 if (inpath == NULL) { 48 lua_pushnil(L); 49 lua_pushstring(L, strerror(ENOMEM)); 50 lua_pushinteger(L, ENOMEM); 51 return (3); 52 } 53 54 outpath = basename(inpath); 55 lua_pushstring(L, outpath); 56 free(inpath); 57 return (1); 58 } 59 60 static int 61 lua_chmod(lua_State *L) 62 { 63 int n; 64 const char *path; 65 mode_t mode; 66 67 n = lua_gettop(L); 68 luaL_argcheck(L, n == 2, n > 2 ? 3 : n, 69 "chmod takes exactly two arguments"); 70 path = luaL_checkstring(L, 1); 71 mode = (mode_t)luaL_checkinteger(L, 2); 72 if (chmod(path, mode) == -1) { 73 lua_pushnil(L); 74 lua_pushstring(L, strerror(errno)); 75 lua_pushinteger(L, errno); 76 return (3); 77 } 78 lua_pushinteger(L, 0); 79 return (1); 80 } 81 82 static int 83 lua_chown(lua_State *L) 84 { 85 int n; 86 const char *path; 87 uid_t owner = (uid_t) -1; 88 gid_t group = (gid_t) -1; 89 90 n = lua_gettop(L); 91 luaL_argcheck(L, n > 1, n, 92 "chown takes at least two arguments"); 93 path = luaL_checkstring(L, 1); 94 if (lua_isinteger(L, 2)) 95 owner = (uid_t) lua_tointeger(L, 2); 96 else if (lua_isstring(L, 2)) { 97 struct passwd *p = getpwnam(lua_tostring(L, 2)); 98 if (p != NULL) 99 owner = p->pw_uid; 100 else 101 return (luaL_argerror(L, 2, 102 lua_pushfstring(L, "unknown user %s", 103 lua_tostring(L, 2)))); 104 } else if (!lua_isnoneornil(L, 2)) { 105 const char *type = luaL_typename(L, 2); 106 return (luaL_argerror(L, 2, 107 lua_pushfstring(L, "integer or string expected, got %s", 108 type))); 109 } 110 111 if (lua_isinteger(L, 3)) 112 group = (gid_t) lua_tointeger(L, 3); 113 else if (lua_isstring(L, 3)) { 114 struct group *g = getgrnam(lua_tostring(L, 3)); 115 if (g != NULL) 116 group = g->gr_gid; 117 else 118 return (luaL_argerror(L, 3, 119 lua_pushfstring(L, "unknown group %s", 120 lua_tostring(L, 3)))); 121 } else if (!lua_isnoneornil(L, 3)) { 122 const char *type = luaL_typename(L, 3); 123 return (luaL_argerror(L, 3, 124 lua_pushfstring(L, "integer or string expected, got %s", 125 type))); 126 } 127 128 if (chown(path, owner, group) == -1) { 129 lua_pushnil(L); 130 lua_pushstring(L, strerror(errno)); 131 lua_pushinteger(L, errno); 132 return (3); 133 } 134 lua_pushinteger(L, 0); 135 return (1); 136 } 137 138 static int 139 lua_pclose(lua_State *L) 140 { 141 int error, fd, n; 142 143 n = lua_gettop(L); 144 luaL_argcheck(L, n == 1, 1, 145 "close takes exactly one argument (fd)"); 146 147 fd = luaL_checkinteger(L, 1); 148 if (fd < 0) { 149 error = EBADF; 150 goto err; 151 } 152 153 if (close(fd) == 0) { 154 lua_pushinteger(L, 0); 155 return (1); 156 } 157 158 error = errno; 159 err: 160 lua_pushnil(L); 161 lua_pushstring(L, strerror(error)); 162 lua_pushinteger(L, error); 163 return (3); 164 165 } 166 167 static int 168 lua_uname(lua_State *L) 169 { 170 struct utsname name; 171 int error, n; 172 173 n = lua_gettop(L); 174 luaL_argcheck(L, n == 0, 1, "too many arguments"); 175 176 error = uname(&name); 177 if (error != 0) { 178 error = errno; 179 lua_pushnil(L); 180 lua_pushstring(L, strerror(error)); 181 lua_pushinteger(L, error); 182 return (3); 183 } 184 185 lua_newtable(L); 186 #define setkv(f) do { \ 187 lua_pushstring(L, name.f); \ 188 lua_setfield(L, -2, #f); \ 189 } while (0) 190 setkv(sysname); 191 setkv(nodename); 192 setkv(release); 193 setkv(version); 194 setkv(machine); 195 #undef setkv 196 197 return (1); 198 } 199 200 static int 201 lua_dirname(lua_State *L) 202 { 203 char *inpath, *outpath; 204 int narg; 205 206 narg = lua_gettop(L); 207 luaL_argcheck(L, narg > 0, 1, 208 "dirname takes at least one argument (path)"); 209 inpath = strdup(luaL_checkstring(L, 1)); 210 if (inpath == NULL) { 211 lua_pushnil(L); 212 lua_pushstring(L, strerror(ENOMEM)); 213 lua_pushinteger(L, ENOMEM); 214 return (3); 215 } 216 217 outpath = dirname(inpath); 218 lua_pushstring(L, outpath); 219 free(inpath); 220 return (1); 221 } 222 223 static int 224 lua_fork(lua_State *L) 225 { 226 pid_t pid; 227 int narg; 228 229 narg = lua_gettop(L); 230 luaL_argcheck(L, narg == 0, 1, "too many arguments"); 231 232 pid = fork(); 233 if (pid < 0) { 234 lua_pushnil(L); 235 lua_pushstring(L, strerror(errno)); 236 lua_pushinteger(L, errno); 237 return (3); 238 } 239 240 lua_pushinteger(L, pid); 241 return (1); 242 } 243 244 static int 245 lua_getpid(lua_State *L) 246 { 247 int narg; 248 249 narg = lua_gettop(L); 250 luaL_argcheck(L, narg == 0, 1, "too many arguments"); 251 lua_pushinteger(L, getpid()); 252 return (1); 253 } 254 255 static int 256 lua_pipe(lua_State *L) 257 { 258 int error, fd[2], narg; 259 260 narg = lua_gettop(L); 261 luaL_argcheck(L, narg == 0, 1, "too many arguments"); 262 263 error = pipe(fd); 264 if (error != 0) { 265 lua_pushnil(L); 266 lua_pushstring(L, strerror(errno)); 267 lua_pushinteger(L, errno); 268 return (1); 269 } 270 271 lua_pushinteger(L, fd[0]); 272 lua_pushinteger(L, fd[1]); 273 return (2); 274 } 275 276 static int 277 lua_read(lua_State *L) 278 { 279 char *buf; 280 ssize_t ret; 281 size_t sz; 282 int error, fd, narg; 283 284 narg = lua_gettop(L); 285 luaL_argcheck(L, narg == 2, 1, 286 "read takes exactly two arguments (fd, size)"); 287 288 fd = luaL_checkinteger(L, 1); 289 sz = luaL_checkinteger(L, 2); 290 291 if (fd < 0) { 292 error = EBADF; 293 goto err; 294 } 295 296 buf = malloc(sz); 297 if (buf == NULL) 298 goto err; 299 300 /* 301 * For 0-byte reads, we'll still push the empty string and let the 302 * caller deal with EOF to match lposix semantics. 303 */ 304 ret = read(fd, buf, sz); 305 if (ret >= 0) 306 lua_pushlstring(L, buf, ret); 307 else if (ret < 0) 308 error = errno; /* Save to avoid clobber by free() */ 309 310 free(buf); 311 if (error != 0) 312 goto err; 313 314 /* Just the string pushed. */ 315 return (1); 316 err: 317 lua_pushnil(L); 318 lua_pushstring(L, strerror(error)); 319 lua_pushinteger(L, error); 320 return (3); 321 } 322 323 static int 324 lua_realpath(lua_State *L) 325 { 326 const char *inpath; 327 char *outpath; 328 int narg; 329 330 narg = lua_gettop(L); 331 luaL_argcheck(L, narg > 0, 1, "at least one argument required"); 332 inpath = luaL_checkstring(L, 1); 333 334 outpath = realpath(inpath, NULL); 335 if (outpath == NULL) { 336 lua_pushnil(L); 337 lua_pushstring(L, strerror(errno)); 338 lua_pushinteger(L, errno); 339 return (3); 340 } 341 342 lua_pushstring(L, outpath); 343 free(outpath); 344 return (1); 345 } 346 347 static int 348 lua_wait(lua_State *L) 349 { 350 pid_t pid; 351 int options, status; 352 int narg; 353 354 narg = lua_gettop(L); 355 356 pid = -1; 357 status = options = 0; 358 if (narg >= 1 && !lua_isnil(L, 1)) 359 pid = luaL_checkinteger(L, 1); 360 if (narg >= 2 && !lua_isnil(L, 2)) 361 options = luaL_checkinteger(L, 2); 362 363 pid = waitpid(pid, &status, options); 364 if (pid < 0) { 365 lua_pushnil(L); 366 lua_pushstring(L, strerror(errno)); 367 lua_pushinteger(L, errno); 368 return (3); 369 } 370 371 lua_pushinteger(L, pid); 372 if (pid == 0) { 373 lua_pushliteral(L, "running"); 374 return (2); 375 } 376 377 if (WIFCONTINUED(status)) { 378 lua_pushliteral(L, "continued"); 379 return (2); 380 } else if(WIFSTOPPED(status)) { 381 lua_pushliteral(L, "stopped"); 382 lua_pushinteger(L, WSTOPSIG(status)); 383 return (3); 384 } else if (WIFEXITED(status)) { 385 lua_pushliteral(L, "exited"); 386 lua_pushinteger(L, WEXITSTATUS(status)); 387 return (3); 388 } else if (WIFSIGNALED(status)) { 389 lua_pushliteral(L, "killed"); 390 lua_pushinteger(L, WTERMSIG(status)); 391 return (3); 392 } 393 394 return (1); 395 } 396 397 static int 398 lua_write(lua_State *L) 399 { 400 const char *buf; 401 size_t bufsz, sz; 402 ssize_t ret; 403 off_t offset; 404 int error, fd, narg; 405 406 narg = lua_gettop(L); 407 luaL_argcheck(L, narg >= 2, 1, 408 "write takes at least two arguments (fd, buf, sz, off)"); 409 luaL_argcheck(L, narg <= 4, 5, 410 "write takes no more than four arguments (fd, buf, sz, off)"); 411 412 fd = luaL_checkinteger(L, 1); 413 if (fd < 0) { 414 error = EBADF; 415 goto err; 416 } 417 418 buf = luaL_checkstring(L, 2); 419 420 bufsz = sz = lua_rawlen(L, 2); 421 if (narg >= 3 && !lua_isnil(L, 3)) 422 sz = luaL_checkinteger(L, 3); 423 424 offset = 0; 425 if (narg >= 4 && !lua_isnil(L, 4)) 426 offset = luaL_checkinteger(L, 4); 427 428 if ((size_t)offset > bufsz || offset + sz > bufsz) { 429 lua_pushnil(L); 430 lua_pushfstring(L, 431 "write: invalid access offset %zu, size %zu in a buffer size %zu", 432 offset, sz, bufsz); 433 lua_pushinteger(L, EINVAL); 434 return (3); 435 } 436 437 ret = write(fd, buf + offset, sz); 438 if (ret < 0) { 439 error = errno; 440 goto err; 441 } 442 443 lua_pushinteger(L, ret); 444 return (1); 445 err: 446 lua_pushnil(L); 447 lua_pushstring(L, strerror(error)); 448 lua_pushinteger(L, error); 449 return (3); 450 } 451 452 #define REG_DEF(n, func) { #n, func } 453 #define REG_SIMPLE(n) REG_DEF(n, lua_ ## n) 454 static const struct luaL_Reg libgenlib[] = { 455 REG_SIMPLE(basename), 456 REG_SIMPLE(dirname), 457 { NULL, NULL }, 458 }; 459 460 static const struct luaL_Reg stdliblib[] = { 461 REG_SIMPLE(realpath), 462 { NULL, NULL }, 463 }; 464 465 static const struct luaL_Reg sys_statlib[] = { 466 REG_SIMPLE(chmod), 467 { NULL, NULL }, 468 }; 469 470 static const struct luaL_Reg sys_utsnamelib[] = { 471 REG_SIMPLE(uname), 472 { NULL, NULL }, 473 }; 474 475 static const struct luaL_Reg sys_waitlib[] = { 476 REG_SIMPLE(wait), 477 {NULL, NULL}, 478 }; 479 480 static const struct luaL_Reg unistdlib[] = { 481 REG_SIMPLE(_exit), 482 REG_SIMPLE(chown), 483 REG_DEF(close, lua_pclose), 484 REG_SIMPLE(fork), 485 REG_SIMPLE(getpid), 486 REG_SIMPLE(pipe), 487 REG_SIMPLE(read), 488 REG_SIMPLE(write), 489 { NULL, NULL }, 490 }; 491 492 #undef REG_SIMPLE 493 #undef REG_DEF 494 495 int 496 luaopen_posix_libgen(lua_State *L) 497 { 498 luaL_newlib(L, libgenlib); 499 return (1); 500 } 501 502 int 503 luaopen_posix_stdlib(lua_State *L) 504 { 505 luaL_newlib(L, stdliblib); 506 return (1); 507 } 508 509 int 510 luaopen_posix_sys_stat(lua_State *L) 511 { 512 luaL_newlib(L, sys_statlib); 513 return (1); 514 } 515 516 int 517 luaopen_posix_sys_wait(lua_State *L) 518 { 519 luaL_newlib(L, sys_waitlib); 520 521 #define lua_pushflag(L, flag) do { \ 522 lua_pushinteger(L, flag); \ 523 lua_setfield(L, -2, #flag); \ 524 } while(0) 525 526 /* Only these two exported by lposix */ 527 lua_pushflag(L, WNOHANG); 528 lua_pushflag(L, WUNTRACED); 529 530 lua_pushflag(L, WCONTINUED); 531 lua_pushflag(L, WSTOPPED); 532 #ifdef WTRAPPED 533 lua_pushflag(L, WTRAPPED); 534 #endif 535 lua_pushflag(L, WEXITED); 536 lua_pushflag(L, WNOWAIT); 537 #undef lua_pushflag 538 539 return (1); 540 } 541 542 int 543 luaopen_posix_sys_utsname(lua_State *L) 544 { 545 luaL_newlib(L, sys_utsnamelib); 546 return 1; 547 } 548 549 int 550 luaopen_posix_unistd(lua_State *L) 551 { 552 luaL_newlib(L, unistdlib); 553 return (1); 554 } 555