xref: /freebsd/libexec/flua/modules/lposix.c (revision f35ccf46c7c6cb7d26994ec5dc5926ea53be0116)
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 /*
25  * Minimal implementation of luaposix needed for internal FreeBSD bits.
26  */
27 static int
28 lua__exit(lua_State *L)
29 {
30 	int code, narg;
31 
32 	narg = lua_gettop(L);
33 	luaL_argcheck(L, narg == 1, 1, "_exit takes exactly one argument");
34 
35 	code = luaL_checkinteger(L, 1);
36 	_exit(code);
37 }
38 
39 static int
40 lua_basename(lua_State *L)
41 {
42 	char *inpath, *outpath;
43 	int narg;
44 
45 	narg = lua_gettop(L);
46 	luaL_argcheck(L, narg > 0, 1, "at least one argument required");
47 	inpath = strdup(luaL_checkstring(L, 1));
48 	if (inpath == NULL) {
49 		lua_pushnil(L);
50 		lua_pushstring(L, strerror(ENOMEM));
51 		lua_pushinteger(L, ENOMEM);
52 		return (3);
53 	}
54 
55 	outpath = basename(inpath);
56 	lua_pushstring(L, outpath);
57 	free(inpath);
58 	return (1);
59 }
60 
61 static int
62 lua_chmod(lua_State *L)
63 {
64 	int n;
65 	const char *path;
66 	mode_t mode;
67 
68 	n = lua_gettop(L);
69 	luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
70 	    "chmod takes exactly two arguments");
71 	path = luaL_checkstring(L, 1);
72 	mode = (mode_t)luaL_checkinteger(L, 2);
73 	if (chmod(path, mode) == -1) {
74 		lua_pushnil(L);
75 		lua_pushstring(L, strerror(errno));
76 		lua_pushinteger(L, errno);
77 		return (3);
78 	}
79 	lua_pushinteger(L, 0);
80 	return (1);
81 }
82 
83 static int
84 lua_chown(lua_State *L)
85 {
86 	int n;
87 	const char *path;
88 	uid_t owner = (uid_t) -1;
89 	gid_t group = (gid_t) -1;
90 
91 	n = lua_gettop(L);
92 	luaL_argcheck(L, n > 1, n,
93 	   "chown takes at least two arguments");
94 	path = luaL_checkstring(L, 1);
95 	if (lua_isinteger(L, 2))
96 		owner = (uid_t) lua_tointeger(L, 2);
97 	else if (lua_isstring(L, 2)) {
98 		struct passwd *p = getpwnam(lua_tostring(L, 2));
99 		if (p != NULL)
100 			owner = p->pw_uid;
101 		else
102 			return (luaL_argerror(L, 2,
103 			    lua_pushfstring(L, "unknown user %s",
104 			    lua_tostring(L, 2))));
105 	} else if (!lua_isnoneornil(L, 2)) {
106 		const char *type = luaL_typename(L, 2);
107 		return (luaL_argerror(L, 2,
108 		    lua_pushfstring(L, "integer or string expected, got %s",
109 		    type)));
110 	}
111 
112 	if (lua_isinteger(L, 3))
113 		group = (gid_t) lua_tointeger(L, 3);
114 	else if (lua_isstring(L, 3)) {
115 		struct group *g = getgrnam(lua_tostring(L, 3));
116 		if (g != NULL)
117 			group = g->gr_gid;
118 		else
119 			return (luaL_argerror(L, 3,
120 			    lua_pushfstring(L, "unknown group %s",
121 			    lua_tostring(L, 3))));
122 	} else if (!lua_isnoneornil(L, 3)) {
123 		const char *type = luaL_typename(L, 3);
124 		return (luaL_argerror(L, 3,
125 		    lua_pushfstring(L, "integer or string expected, got %s",
126 		    type)));
127 	}
128 
129 	if (chown(path, owner, group) == -1) {
130 		lua_pushnil(L);
131 		lua_pushstring(L, strerror(errno));
132 		lua_pushinteger(L, errno);
133 		return (3);
134 	}
135 	lua_pushinteger(L, 0);
136 	return (1);
137 }
138 
139 static int
140 lua_pclose(lua_State *L)
141 {
142 	int error, fd, n;
143 
144 	n = lua_gettop(L);
145 	luaL_argcheck(L, n == 1, 1,
146 	    "close takes exactly one argument (fd)");
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
169 lua_fnmatch(lua_State *L)
170 {
171 	const char *pattern, *string;
172 	int flags, n;
173 
174 	n = lua_gettop(L);
175 	luaL_argcheck(L, n == 2 || n == 3, 4, "need 2 or 3 arguments");
176 
177 	pattern = luaL_checkstring(L, 1);
178 	string = luaL_checkstring(L, 2);
179 	flags = luaL_optinteger(L, 3, 0);
180 	lua_pushinteger(L, fnmatch(pattern, string, flags));
181 
182 	return (1);
183 }
184 
185 static int
186 lua_uname(lua_State *L)
187 {
188 	struct utsname name;
189 	int error, n;
190 
191 	n = lua_gettop(L);
192 	luaL_argcheck(L, n == 0, 1, "too many arguments");
193 
194 	error = uname(&name);
195 	if (error != 0) {
196 		error = errno;
197 		lua_pushnil(L);
198 		lua_pushstring(L, strerror(error));
199 		lua_pushinteger(L, error);
200 		return (3);
201 	}
202 
203 	lua_newtable(L);
204 #define	setkv(f) do {			\
205 	lua_pushstring(L, name.f);	\
206 	lua_setfield(L, -2, #f);	\
207 } while (0)
208 	setkv(sysname);
209 	setkv(nodename);
210 	setkv(release);
211 	setkv(version);
212 	setkv(machine);
213 #undef setkv
214 
215 	return (1);
216 }
217 
218 static int
219 lua_dirname(lua_State *L)
220 {
221 	char *inpath, *outpath;
222 	int narg;
223 
224 	narg = lua_gettop(L);
225 	luaL_argcheck(L, narg > 0, 1,
226 	    "dirname takes at least one argument (path)");
227 	inpath = strdup(luaL_checkstring(L, 1));
228 	if (inpath == NULL) {
229 		lua_pushnil(L);
230 		lua_pushstring(L, strerror(ENOMEM));
231 		lua_pushinteger(L, ENOMEM);
232 		return (3);
233 	}
234 
235 	outpath = dirname(inpath);
236 	lua_pushstring(L, outpath);
237 	free(inpath);
238 	return (1);
239 }
240 
241 static int
242 lua_fork(lua_State *L)
243 {
244 	pid_t pid;
245 	int narg;
246 
247 	narg = lua_gettop(L);
248 	luaL_argcheck(L, narg == 0, 1, "too many arguments");
249 
250 	pid = fork();
251 	if (pid < 0) {
252 		lua_pushnil(L);
253 		lua_pushstring(L, strerror(errno));
254 		lua_pushinteger(L, errno);
255 		return (3);
256 	}
257 
258 	lua_pushinteger(L, pid);
259 	return (1);
260 }
261 
262 static int
263 lua_getpid(lua_State *L)
264 {
265 	int narg;
266 
267 	narg = lua_gettop(L);
268 	luaL_argcheck(L, narg == 0, 1, "too many arguments");
269 	lua_pushinteger(L, getpid());
270 	return (1);
271 }
272 
273 static int
274 lua_pipe(lua_State *L)
275 {
276 	int error, fd[2], narg;
277 
278 	narg = lua_gettop(L);
279 	luaL_argcheck(L, narg == 0, 1, "too many arguments");
280 
281 	error = pipe(fd);
282 	if (error != 0) {
283 		lua_pushnil(L);
284 		lua_pushstring(L, strerror(errno));
285 		lua_pushinteger(L, errno);
286 		return (1);
287 	}
288 
289 	lua_pushinteger(L, fd[0]);
290 	lua_pushinteger(L, fd[1]);
291 	return (2);
292 }
293 
294 static int
295 lua_read(lua_State *L)
296 {
297 	char *buf;
298 	ssize_t ret;
299 	size_t sz;
300 	int error, fd, narg;
301 
302 	narg = lua_gettop(L);
303 	luaL_argcheck(L, narg == 2, 1,
304 	    "read takes exactly two arguments (fd, size)");
305 
306 	fd = luaL_checkinteger(L, 1);
307 	sz = luaL_checkinteger(L, 2);
308 
309 	if (fd < 0) {
310 		error = EBADF;
311 		goto err;
312 	}
313 
314 	buf = malloc(sz);
315 	if (buf == NULL)
316 		goto err;
317 
318 	/*
319 	 * For 0-byte reads, we'll still push the empty string and let the
320 	 * caller deal with EOF to match lposix semantics.
321 	 */
322 	ret = read(fd, buf, sz);
323 	if (ret >= 0)
324 		lua_pushlstring(L, buf, ret);
325 	else if (ret < 0)
326 		error = errno; /* Save to avoid clobber by free() */
327 
328 	free(buf);
329 	if (error != 0)
330 		goto err;
331 
332 	/* Just the string pushed. */
333 	return (1);
334 err:
335 	lua_pushnil(L);
336 	lua_pushstring(L, strerror(error));
337 	lua_pushinteger(L, error);
338 	return (3);
339 }
340 
341 static int
342 lua_realpath(lua_State *L)
343 {
344 	const char *inpath;
345 	char *outpath;
346 	int narg;
347 
348 	narg = lua_gettop(L);
349 	luaL_argcheck(L, narg > 0, 1, "at least one argument required");
350 	inpath = luaL_checkstring(L, 1);
351 
352 	outpath = realpath(inpath, NULL);
353 	if (outpath == NULL) {
354 		lua_pushnil(L);
355 		lua_pushstring(L, strerror(errno));
356 		lua_pushinteger(L, errno);
357 		return (3);
358 	}
359 
360 	lua_pushstring(L, outpath);
361 	free(outpath);
362 	return (1);
363 }
364 
365 static int
366 lua_wait(lua_State *L)
367 {
368 	pid_t pid;
369 	int options, status;
370 	int narg;
371 
372 	narg = lua_gettop(L);
373 
374 	pid = -1;
375 	status = options = 0;
376 	if (narg >= 1 && !lua_isnil(L, 1))
377 		pid = luaL_checkinteger(L, 1);
378 	if (narg >= 2 && !lua_isnil(L, 2))
379 		options = luaL_checkinteger(L, 2);
380 
381 	pid = waitpid(pid, &status, options);
382 	if (pid < 0) {
383 		lua_pushnil(L);
384 		lua_pushstring(L, strerror(errno));
385 		lua_pushinteger(L, errno);
386 		return (3);
387 	}
388 
389 	lua_pushinteger(L, pid);
390 	if (pid == 0) {
391 		lua_pushliteral(L, "running");
392 		return (2);
393 	}
394 
395 	if (WIFCONTINUED(status)) {
396 		lua_pushliteral(L, "continued");
397 		return (2);
398 	} else if(WIFSTOPPED(status)) {
399 		lua_pushliteral(L, "stopped");
400 		lua_pushinteger(L, WSTOPSIG(status));
401 		return (3);
402 	} else if (WIFEXITED(status)) {
403 		lua_pushliteral(L, "exited");
404 		lua_pushinteger(L, WEXITSTATUS(status));
405 		return (3);
406 	} else if (WIFSIGNALED(status)) {
407 		lua_pushliteral(L, "killed");
408 		lua_pushinteger(L, WTERMSIG(status));
409 		return (3);
410 	}
411 
412 	return (1);
413 }
414 
415 static int
416 lua_write(lua_State *L)
417 {
418 	const char *buf;
419 	size_t bufsz, sz;
420 	ssize_t ret;
421 	off_t offset;
422 	int error, fd, narg;
423 
424 	narg = lua_gettop(L);
425 	luaL_argcheck(L, narg >= 2, 1,
426 	    "write takes at least two arguments (fd, buf, sz, off)");
427 	luaL_argcheck(L, narg <= 4, 5,
428 	    "write takes no more than four arguments (fd, buf, sz, off)");
429 
430 	fd = luaL_checkinteger(L, 1);
431 	if (fd < 0) {
432 		error = EBADF;
433 		goto err;
434 	}
435 
436 	buf = luaL_checkstring(L, 2);
437 
438 	bufsz = sz = lua_rawlen(L, 2);
439 	if (narg >= 3 && !lua_isnil(L, 3))
440 		sz = luaL_checkinteger(L, 3);
441 
442 	offset = 0;
443 	if (narg >= 4 && !lua_isnil(L, 4))
444 		offset = luaL_checkinteger(L, 4);
445 
446 	if ((size_t)offset > bufsz || offset + sz > bufsz) {
447 		lua_pushnil(L);
448 		lua_pushfstring(L,
449 		    "write: invalid access offset %zu, size %zu in a buffer size %zu",
450 		    offset, sz, bufsz);
451 		lua_pushinteger(L, EINVAL);
452 		return (3);
453 	}
454 
455 	ret = write(fd, buf + offset, sz);
456 	if (ret < 0) {
457 		error = errno;
458 		goto err;
459 	}
460 
461 	lua_pushinteger(L, ret);
462 	return (1);
463 err:
464 	lua_pushnil(L);
465 	lua_pushstring(L, strerror(error));
466 	lua_pushinteger(L, error);
467 	return (3);
468 }
469 
470 #define	REG_DEF(n, func)	{ #n, func }
471 #define REG_SIMPLE(n)		REG_DEF(n, lua_ ## n)
472 static const struct luaL_Reg libgenlib[] = {
473 	REG_SIMPLE(basename),
474 	REG_SIMPLE(dirname),
475 	{ NULL, NULL },
476 };
477 
478 static const struct luaL_Reg stdliblib[] = {
479 	REG_SIMPLE(realpath),
480 	{ NULL, NULL },
481 };
482 
483 static const struct luaL_Reg fnmatchlib[] = {
484 	REG_SIMPLE(fnmatch),
485 	{ NULL, NULL },
486 };
487 
488 static const struct luaL_Reg sys_statlib[] = {
489 	REG_SIMPLE(chmod),
490 	{ NULL, NULL },
491 };
492 
493 static const struct luaL_Reg sys_utsnamelib[] = {
494 	REG_SIMPLE(uname),
495 	{ NULL, NULL },
496 };
497 
498 static const struct luaL_Reg sys_waitlib[] = {
499 	REG_SIMPLE(wait),
500 	{NULL, NULL},
501 };
502 
503 static const struct luaL_Reg unistdlib[] = {
504 	REG_SIMPLE(_exit),
505 	REG_SIMPLE(chown),
506 	REG_DEF(close, lua_pclose),
507 	REG_SIMPLE(fork),
508 	REG_SIMPLE(getpid),
509 	REG_SIMPLE(pipe),
510 	REG_SIMPLE(read),
511 	REG_SIMPLE(write),
512 	{ NULL, NULL },
513 };
514 
515 #undef REG_SIMPLE
516 #undef REG_DEF
517 
518 int
519 luaopen_posix_libgen(lua_State *L)
520 {
521 	luaL_newlib(L, libgenlib);
522 	return (1);
523 }
524 
525 int
526 luaopen_posix_stdlib(lua_State *L)
527 {
528 	luaL_newlib(L, stdliblib);
529 	return (1);
530 }
531 
532 int
533 luaopen_posix_fnmatch(lua_State *L)
534 {
535 	luaL_newlib(L, fnmatchlib);
536 
537 #define	setkv(f) do {			\
538 	lua_pushinteger(L, f);		\
539 	lua_setfield(L, -2, #f);	\
540 } while (0)
541 	setkv(FNM_PATHNAME);
542 	setkv(FNM_NOESCAPE);
543 	setkv(FNM_NOMATCH);
544 	setkv(FNM_PERIOD);
545 #undef setkv
546 
547 	return 1;
548 }
549 
550 int
551 luaopen_posix_sys_stat(lua_State *L)
552 {
553 	luaL_newlib(L, sys_statlib);
554 	return (1);
555 }
556 
557 int
558 luaopen_posix_sys_wait(lua_State *L)
559 {
560 	luaL_newlib(L, sys_waitlib);
561 
562 #define	lua_pushflag(L, flag) do {	\
563 	lua_pushinteger(L, flag);	\
564 	lua_setfield(L, -2, #flag);	\
565 } while(0)
566 
567 	/* Only these two exported by lposix */
568 	lua_pushflag(L, WNOHANG);
569 	lua_pushflag(L, WUNTRACED);
570 
571 	lua_pushflag(L, WCONTINUED);
572 	lua_pushflag(L, WSTOPPED);
573 #ifdef WTRAPPED
574 	lua_pushflag(L, WTRAPPED);
575 #endif
576 	lua_pushflag(L, WEXITED);
577 	lua_pushflag(L, WNOWAIT);
578 #undef lua_pushflag
579 
580 	return (1);
581 }
582 
583 int
584 luaopen_posix_sys_utsname(lua_State *L)
585 {
586 	luaL_newlib(L, sys_utsnamelib);
587 	return 1;
588 }
589 
590 int
591 luaopen_posix_unistd(lua_State *L)
592 {
593 	luaL_newlib(L, unistdlib);
594 	return (1);
595 }
596