2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 -- Copyright (C) 2009 Tobias Markmann
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
13 * POSIX support functions for Lua
16 #define MODULE_VERSION "0.3.6"
22 #include <sys/resource.h>
23 #include <sys/types.h>
25 #include <sys/utsname.h>
38 #if (LUA_VERSION_NUM == 501)
39 #define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R)
43 #if defined(__linux__) && defined(_GNU_SOURCE)
44 #include <linux/falloc.h>
47 #if (defined(_SVID_SOURCE) && !defined(WITHOUT_MALLINFO))
52 /* Daemonization support */
54 static int lc_daemonize(lua_State* L) {
59 lua_pushboolean(L, 0);
60 lua_pushstring(L, "already-daemonized");
64 /* Attempt initial fork */
65 if((pid = fork()) < 0) {
67 lua_pushboolean(L, 0);
68 lua_pushstring(L, "fork-failed");
71 /* We are the parent process */
72 lua_pushboolean(L, 1);
73 lua_pushnumber(L, pid);
77 /* and we are the child process */
79 /* We failed to become session leader */
80 /* (we probably already were) */
81 lua_pushboolean(L, 0);
82 lua_pushstring(L, "setsid-failed");
86 /* Close stdin, stdout, stderr */
90 /* Make sure accidental use of FDs 0, 1, 2 don't cause weirdness */
91 open("/dev/null", O_RDONLY);
92 open("/dev/null", O_WRONLY);
93 open("/dev/null", O_WRONLY);
95 /* Final fork, use it wisely */
100 /* Show's over, let's continue */
101 lua_pushboolean(L, 1);
108 const char* const facility_strings[] = {
110 #if !(defined(sun) || defined(__sun))
115 #if !(defined(sun) || defined(__sun))
134 int facility_constants[] = {
136 #if !(defined(sun) || defined(__sun))
141 #if !(defined(sun) || defined(__sun))
163 The parameter ident in the call of openlog() is probably stored as-is.
164 Thus, if the string it points to is changed, syslog() may start
165 prepending the changed string, and if the string it points to ceases to
166 exist, the results are undefined. Most portable is to use a string
170 char* syslog_ident = NULL;
172 int lc_syslog_open(lua_State* L) {
173 int facility = luaL_checkoption(L, 2, "daemon", facility_strings);
174 facility = facility_constants[facility];
176 luaL_checkstring(L, 1);
182 syslog_ident = strdup(lua_tostring(L, 1));
184 openlog(syslog_ident, LOG_PID, facility);
188 const char* const level_strings[] = {
196 int level_constants[] = {
204 int lc_syslog_log(lua_State* L) {
205 int level = level_constants[luaL_checkoption(L, 1, "notice", level_strings)];
207 if(lua_gettop(L) == 3) {
208 syslog(level, "%s: %s", luaL_checkstring(L, 2), luaL_checkstring(L, 3));
210 syslog(level, "%s", lua_tostring(L, 2));
216 int lc_syslog_close(lua_State* L) {
227 int lc_syslog_setmask(lua_State* L) {
228 int level_idx = luaL_checkoption(L, 1, "notice", level_strings);
232 mask |= LOG_MASK(level_constants[level_idx]);
233 } while(++level_idx <= 4);
241 int lc_getpid(lua_State* L) {
242 lua_pushinteger(L, getpid());
246 /* UID/GID functions */
248 int lc_getuid(lua_State* L) {
249 lua_pushinteger(L, getuid());
253 int lc_getgid(lua_State* L) {
254 lua_pushinteger(L, getgid());
258 int lc_setuid(lua_State* L) {
261 if(lua_gettop(L) < 1) {
265 if(!lua_isnumber(L, 1) && lua_tostring(L, 1)) {
266 /* Passed UID is actually a string, so look up the UID */
268 p = getpwnam(lua_tostring(L, 1));
271 lua_pushboolean(L, 0);
272 lua_pushstring(L, "no-such-user");
278 uid = lua_tonumber(L, 1);
282 /* Ok, attempt setuid */
287 lua_pushboolean(L, 0);
291 lua_pushstring(L, "invalid-uid");
294 lua_pushstring(L, "permission-denied");
297 lua_pushstring(L, "unknown-error");
303 lua_pushboolean(L, 1);
308 /* Seems we couldn't find a valid UID to switch to */
309 lua_pushboolean(L, 0);
310 lua_pushstring(L, "invalid-uid");
314 int lc_setgid(lua_State* L) {
317 if(lua_gettop(L) < 1) {
321 if(!lua_isnumber(L, 1) && lua_tostring(L, 1)) {
322 /* Passed GID is actually a string, so look up the GID */
324 g = getgrnam(lua_tostring(L, 1));
327 lua_pushboolean(L, 0);
328 lua_pushstring(L, "no-such-group");
334 gid = lua_tonumber(L, 1);
338 /* Ok, attempt setgid */
343 lua_pushboolean(L, 0);
347 lua_pushstring(L, "invalid-gid");
350 lua_pushstring(L, "permission-denied");
353 lua_pushstring(L, "unknown-error");
359 lua_pushboolean(L, 1);
364 /* Seems we couldn't find a valid GID to switch to */
365 lua_pushboolean(L, 0);
366 lua_pushstring(L, "invalid-gid");
370 int lc_initgroups(lua_State* L) {
375 if(!lua_isstring(L, 1)) {
377 lua_pushstring(L, "invalid-username");
381 p = getpwnam(lua_tostring(L, 1));
385 lua_pushstring(L, "no-such-user");
389 if(lua_gettop(L) < 2) {
393 switch(lua_type(L, 2)) {
398 gid = lua_tointeger(L, 2);
402 lua_pushstring(L, "invalid-gid");
406 ret = initgroups(lua_tostring(L, 1), gid);
412 lua_pushstring(L, "no-memory");
416 lua_pushstring(L, "permission-denied");
420 lua_pushstring(L, "unknown-error");
423 lua_pushboolean(L, 1);
430 int lc_umask(lua_State* L) {
431 char old_mode_string[7];
432 mode_t old_mode = umask(strtoul(luaL_checkstring(L, 1), NULL, 8));
434 snprintf(old_mode_string, sizeof(old_mode_string), "%03o", old_mode);
435 old_mode_string[sizeof(old_mode_string) - 1] = 0;
436 lua_pushstring(L, old_mode_string);
441 int lc_mkdir(lua_State* L) {
442 int ret = mkdir(luaL_checkstring(L, 1), S_IRUSR | S_IWUSR | S_IXUSR
443 | S_IRGRP | S_IWGRP | S_IXGRP
444 | S_IROTH | S_IXOTH); /* mode 775 */
446 lua_pushboolean(L, ret == 0);
449 lua_pushstring(L, strerror(errno));
456 /* Like POSIX's setrlimit()/getrlimit() API functions.
459 * pposix.setrlimit( resource, soft limit, hard limit)
461 * Any negative limit will be replace with the current limit by an additional call of getrlimit().
464 * pposix.setrlimit("NOFILE", 1000, 2000)
466 int string2resource(const char* s) {
467 if(!strcmp(s, "CORE")) {
471 if(!strcmp(s, "CPU")) {
475 if(!strcmp(s, "DATA")) {
479 if(!strcmp(s, "FSIZE")) {
483 if(!strcmp(s, "NOFILE")) {
484 return RLIMIT_NOFILE;
487 if(!strcmp(s, "STACK")) {
491 #if !(defined(sun) || defined(__sun))
493 if(!strcmp(s, "MEMLOCK")) {
494 return RLIMIT_MEMLOCK;
497 if(!strcmp(s, "NPROC")) {
501 if(!strcmp(s, "RSS")) {
508 if(!strcmp(s, "NICE")) {
516 unsigned long int arg_to_rlimit(lua_State* L, int idx, rlim_t current) {
517 switch(lua_type(L, idx)) {
520 if(strcmp(lua_tostring(L, idx), "unlimited") == 0) {
521 return RLIM_INFINITY;
525 return lua_tointeger(L, idx);
530 return luaL_argerror(L, idx, "unexpected type");
534 int lc_setrlimit(lua_State* L) {
536 int arguments = lua_gettop(L);
539 if(arguments < 1 || arguments > 3) {
540 lua_pushboolean(L, 0);
541 lua_pushstring(L, "incorrect-arguments");
545 rid = string2resource(luaL_checkstring(L, 1));
548 lua_pushboolean(L, 0);
549 lua_pushstring(L, "invalid-resource");
553 /* Fetch current values to use as defaults */
554 if(getrlimit(rid, &lim)) {
555 lua_pushboolean(L, 0);
556 lua_pushstring(L, "getrlimit-failed");
560 lim.rlim_cur = arg_to_rlimit(L, 2, lim.rlim_cur);
561 lim.rlim_max = arg_to_rlimit(L, 3, lim.rlim_max);
563 if(setrlimit(rid, &lim)) {
564 lua_pushboolean(L, 0);
565 lua_pushstring(L, "setrlimit-failed");
569 lua_pushboolean(L, 1);
573 int lc_getrlimit(lua_State* L) {
574 int arguments = lua_gettop(L);
575 const char* resource = NULL;
580 lua_pushboolean(L, 0);
581 lua_pushstring(L, "invalid-arguments");
587 resource = luaL_checkstring(L, 1);
588 rid = string2resource(resource);
591 if(getrlimit(rid, &lim)) {
592 lua_pushboolean(L, 0);
593 lua_pushstring(L, "getrlimit-failed.");
597 /* Unsupported resoucrce. Sorry I'm pretty limited by POSIX standard. */
598 lua_pushboolean(L, 0);
599 lua_pushstring(L, "invalid-resource");
603 lua_pushboolean(L, 1);
605 if(lim.rlim_cur == RLIM_INFINITY) {
606 lua_pushstring(L, "unlimited");
608 lua_pushnumber(L, lim.rlim_cur);
611 if(lim.rlim_max == RLIM_INFINITY) {
612 lua_pushstring(L, "unlimited");
614 lua_pushnumber(L, lim.rlim_max);
620 int lc_abort(lua_State* L) {
625 int lc_uname(lua_State* L) {
626 struct utsname uname_info;
628 if(uname(&uname_info) != 0) {
630 lua_pushstring(L, strerror(errno));
635 lua_pushstring(L, uname_info.sysname);
636 lua_setfield(L, -2, "sysname");
637 lua_pushstring(L, uname_info.nodename);
638 lua_setfield(L, -2, "nodename");
639 lua_pushstring(L, uname_info.release);
640 lua_setfield(L, -2, "release");
641 lua_pushstring(L, uname_info.version);
642 lua_setfield(L, -2, "version");
643 lua_pushstring(L, uname_info.machine);
644 lua_setfield(L, -2, "machine");
646 lua_pushstring(L, uname_info.domainname);
647 lua_setfield(L, -2, "domainname");
652 int lc_setenv(lua_State* L) {
653 const char* var = luaL_checkstring(L, 1);
656 /* If the second argument is nil or nothing, unset the var */
657 if(lua_isnoneornil(L, 2)) {
658 if(unsetenv(var) != 0) {
660 lua_pushstring(L, strerror(errno));
664 lua_pushboolean(L, 1);
668 value = luaL_checkstring(L, 2);
670 if(setenv(var, value, 1) != 0) {
672 lua_pushstring(L, strerror(errno));
676 lua_pushboolean(L, 1);
681 int lc_meminfo(lua_State* L) {
682 struct mallinfo info = mallinfo();
684 /* This is the total size of memory allocated with sbrk by malloc, in bytes. */
685 lua_pushinteger(L, info.arena);
686 lua_setfield(L, -2, "allocated");
687 /* This is the total size of memory allocated with mmap, in bytes. */
688 lua_pushinteger(L, info.hblkhd);
689 lua_setfield(L, -2, "allocated_mmap");
690 /* This is the total size of memory occupied by chunks handed out by malloc. */
691 lua_pushinteger(L, info.uordblks);
692 lua_setfield(L, -2, "used");
693 /* This is the total size of memory occupied by free (not in use) chunks. */
694 lua_pushinteger(L, info.fordblks);
695 lua_setfield(L, -2, "unused");
696 /* This is the size of the top-most releasable chunk that normally borders the
697 end of the heap (i.e., the high end of the virtual address space's data segment). */
698 lua_pushinteger(L, info.keepcost);
699 lua_setfield(L, -2, "returnable");
704 /* File handle extraction blatantly stolen from
705 * https://github.com/rrthomas/luaposix/blob/master/lposix.c#L631
708 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
709 int lc_fallocate(lua_State* L) {
712 FILE* f = *(FILE**) luaL_checkudata(L, 1, LUA_FILEHANDLE);
715 return luaL_error(L, "attempt to use a closed file");
718 offset = luaL_checkinteger(L, 2);
719 len = luaL_checkinteger(L, 3);
721 #if defined(__linux__) && defined(_GNU_SOURCE)
723 ret = fallocate(fileno(f), FALLOC_FL_KEEP_SIZE, offset, len);
726 lua_pushboolean(L, 1);
730 /* Some old versions of Linux apparently use the return value instead of errno */
735 if(errno != ENOSYS && errno != EOPNOTSUPP) {
737 lua_pushstring(L, strerror(errno));
742 #warning Only using posix_fallocate() fallback.
743 #warning Linux fallocate() is strongly recommended if available: recompile with -D_GNU_SOURCE
744 #warning Note that posix_fallocate() will still be used on filesystems that dont support fallocate()
747 ret = posix_fallocate(fileno(f), offset, len);
750 lua_pushboolean(L, 1);
754 lua_pushstring(L, strerror(ret));
755 /* posix_fallocate() can leave a bunch of NULs at the end, so we cut that
756 * this assumes that offset == length of the file */
757 if(ftruncate(fileno(f), offset) != 0) {
758 lua_pushstring(L, strerror(errno));
766 /* Register functions */
768 int luaopen_util_pposix(lua_State* L) {
769 luaL_Reg exports[] = {
770 { "abort", lc_abort },
772 { "daemonize", lc_daemonize },
774 { "syslog_open", lc_syslog_open },
775 { "syslog_close", lc_syslog_close },
776 { "syslog_log", lc_syslog_log },
777 { "syslog_setminlevel", lc_syslog_setmask },
779 { "getpid", lc_getpid },
780 { "getuid", lc_getuid },
781 { "getgid", lc_getgid },
783 { "setuid", lc_setuid },
784 { "setgid", lc_setgid },
785 { "initgroups", lc_initgroups },
787 { "umask", lc_umask },
789 { "mkdir", lc_mkdir },
791 { "setrlimit", lc_setrlimit },
792 { "getrlimit", lc_getrlimit },
794 { "uname", lc_uname },
796 { "setenv", lc_setenv },
799 { "meminfo", lc_meminfo },
802 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
803 { "fallocate", lc_fallocate },
810 luaL_setfuncs(L, exports, 0);
812 lua_pushliteral(L, "pposix");
813 lua_setfield(L, -2, "_NAME");
815 lua_pushliteral(L, MODULE_VERSION);
816 lua_setfield(L, -2, "_VERSION");