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