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
enforce_max_args(lua_State * L,int max)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
lua__exit(lua_State * L)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
lua_basename(lua_State * L)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
lua_chmod(lua_State * L)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
lua_chown(lua_State * L)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
lua_pclose(lua_State * L)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
lua_dup2(lua_State * L)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
lua_execp(lua_State * L)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
lua_fnmatch(lua_State * L)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
lua_uname(lua_State * L)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
lua_dirname(lua_State * L)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
lua_fork(lua_State * L)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
lua_getpid(lua_State * L)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
lua_pipe(lua_State * L)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
lua_read(lua_State * L)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
lua_realpath(lua_State * L)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
lua_wait(lua_State * L)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
lua_write(lua_State * L)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
luaopen_posix_libgen(lua_State * L)594 luaopen_posix_libgen(lua_State *L)
595 {
596 luaL_newlib(L, libgenlib);
597 return (1);
598 }
599
600 static int
luaopen_posix_stdlib(lua_State * L)601 luaopen_posix_stdlib(lua_State *L)
602 {
603 luaL_newlib(L, stdliblib);
604 return (1);
605 }
606
607 static int
luaopen_posix_fnmatch(lua_State * L)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
luaopen_posix_sys_stat(lua_State * L)626 luaopen_posix_sys_stat(lua_State *L)
627 {
628 luaL_newlib(L, sys_statlib);
629 return (1);
630 }
631
632 static int
luaopen_posix_sys_utsname(lua_State * L)633 luaopen_posix_sys_utsname(lua_State *L)
634 {
635 luaL_newlib(L, sys_utsnamelib);
636 return 1;
637 }
638
639 static int
luaopen_posix_sys_wait(lua_State * L)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
luaopen_posix_unistd(lua_State * L)666 luaopen_posix_unistd(lua_State *L)
667 {
668 luaL_newlib(L, unistdlib);
669 return (1);
670 }
671
672 int
luaopen_posix(lua_State * L)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