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