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
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
lua_pclose(lua_State * L)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
lua_dup2(lua_State * L)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
lua_execp(lua_State * L)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
lua_fnmatch(lua_State * L)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
lua_uname(lua_State * L)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
lua_dirname(lua_State * L)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
lua_fork(lua_State * L)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
lua_getpid(lua_State * L)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
lua_pipe(lua_State * L)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
lua_read(lua_State * L)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
lua_realpath(lua_State * L)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
lua_wait(lua_State * L)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
lua_write(lua_State * L)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
luaopen_posix_libgen(lua_State * L)585 luaopen_posix_libgen(lua_State *L)
586 {
587 luaL_newlib(L, libgenlib);
588 return (1);
589 }
590
591 int
luaopen_posix_stdlib(lua_State * L)592 luaopen_posix_stdlib(lua_State *L)
593 {
594 luaL_newlib(L, stdliblib);
595 return (1);
596 }
597
598 int
luaopen_posix_fnmatch(lua_State * L)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
luaopen_posix_sys_stat(lua_State * L)617 luaopen_posix_sys_stat(lua_State *L)
618 {
619 luaL_newlib(L, sys_statlib);
620 return (1);
621 }
622
623 int
luaopen_posix_sys_wait(lua_State * L)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
luaopen_posix_sys_utsname(lua_State * L)650 luaopen_posix_sys_utsname(lua_State *L)
651 {
652 luaL_newlib(L, sys_utsnamelib);
653 return 1;
654 }
655
656 int
luaopen_posix_unistd(lua_State * L)657 luaopen_posix_unistd(lua_State *L)
658 {
659 luaL_newlib(L, unistdlib);
660 return (1);
661 }
662