xref: /freebsd/libexec/flua/modules/lposix.c (revision b41b6fdb3a1635de4c2a9280aab12b83e3aeffc5)
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