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