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