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.4.0"
19 #if defined(__linux__)
24 #ifndef _DEFAULT_SOURCE
25 #define _DEFAULT_SOURCE
29 #if defined(__APPLE__)
30 #ifndef _DARWIN_C_SOURCE
31 #define _DARWIN_C_SOURCE
35 #if ! defined(__FreeBSD__)
36 #ifndef _POSIX_C_SOURCE
37 #define _POSIX_C_SOURCE 200809L
45 #include <sys/resource.h>
46 #include <sys/types.h>
48 #include <sys/utsname.h>
61 #if (LUA_VERSION_NUM == 501)
62 #define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R)
66 #if defined(__linux__)
67 #include <linux/falloc.h>
70 #if !defined(WITHOUT_MALLINFO) && defined(__linux__) && defined(__GLIBC__)
75 #if defined(__FreeBSD__) && defined(RFPROC)
77 * On FreeBSD, calling fork() is equivalent to rfork(RFPROC | RFFDG).
79 * RFFDG being set means that the file descriptor table is copied,
80 * otherwise it's shared. We want the later, otherwise libevent gets
85 #define fork() rfork(RFPROC)
88 /* Daemonization support */
90 static int lc_daemonize(lua_State
*L
) {
95 lua_pushboolean(L
, 0);
96 lua_pushstring(L
, "already-daemonized");
100 /* Attempt initial fork */
101 if((pid
= fork()) < 0) {
103 lua_pushboolean(L
, 0);
104 lua_pushstring(L
, "fork-failed");
106 } else if(pid
!= 0) {
107 /* We are the parent process */
108 lua_pushboolean(L
, 1);
109 lua_pushnumber(L
, pid
);
113 /* and we are the child process */
115 /* We failed to become session leader */
116 /* (we probably already were) */
117 lua_pushboolean(L
, 0);
118 lua_pushstring(L
, "setsid-failed");
122 /* Make sure accidental use of FDs 0, 1, 2 don't cause weirdness */
123 freopen("/dev/null", "r", stdin
);
124 freopen("/dev/null", "w", stdout
);
125 freopen("/dev/null", "w", stderr
);
127 /* Final fork, use it wisely */
132 /* Show's over, let's continue */
133 lua_pushboolean(L
, 1);
140 const char *const facility_strings
[] = {
142 #if !(defined(sun) || defined(__sun))
147 #if !(defined(sun) || defined(__sun))
166 int facility_constants
[] = {
168 #if !(defined(sun) || defined(__sun))
173 #if !(defined(sun) || defined(__sun))
195 The parameter ident in the call of openlog() is probably stored as-is.
196 Thus, if the string it points to is changed, syslog() may start
197 prepending the changed string, and if the string it points to ceases to
198 exist, the results are undefined. Most portable is to use a string
202 char *syslog_ident
= NULL
;
204 int lc_syslog_open(lua_State
*L
) {
205 int facility
= luaL_checkoption(L
, 2, "daemon", facility_strings
);
206 facility
= facility_constants
[facility
];
208 luaL_checkstring(L
, 1);
214 syslog_ident
= strdup(lua_tostring(L
, 1));
216 openlog(syslog_ident
, LOG_PID
, facility
);
220 const char *const level_strings
[] = {
228 int level_constants
[] = {
236 int lc_syslog_log(lua_State
*L
) {
237 int level
= level_constants
[luaL_checkoption(L
, 1, "notice", level_strings
)];
239 if(lua_gettop(L
) == 3) {
240 syslog(level
, "%s: %s", luaL_checkstring(L
, 2), luaL_checkstring(L
, 3));
242 syslog(level
, "%s", lua_tostring(L
, 2));
248 int lc_syslog_close(lua_State
*L
) {
260 int lc_syslog_setmask(lua_State
*L
) {
261 int level_idx
= luaL_checkoption(L
, 1, "notice", level_strings
);
265 mask
|= LOG_MASK(level_constants
[level_idx
]);
266 } while(++level_idx
<= 4);
274 int lc_getpid(lua_State
*L
) {
275 lua_pushinteger(L
, getpid());
279 /* UID/GID functions */
281 int lc_getuid(lua_State
*L
) {
282 lua_pushinteger(L
, getuid());
286 int lc_getgid(lua_State
*L
) {
287 lua_pushinteger(L
, getgid());
291 int lc_setuid(lua_State
*L
) {
294 if(lua_gettop(L
) < 1) {
298 if(!lua_isnumber(L
, 1) && lua_tostring(L
, 1)) {
299 /* Passed UID is actually a string, so look up the UID */
301 p
= getpwnam(lua_tostring(L
, 1));
304 lua_pushboolean(L
, 0);
305 lua_pushstring(L
, "no-such-user");
311 uid
= lua_tonumber(L
, 1);
315 /* Ok, attempt setuid */
320 lua_pushboolean(L
, 0);
324 lua_pushstring(L
, "invalid-uid");
328 lua_pushstring(L
, "permission-denied");
332 lua_pushstring(L
, "unknown-error");
338 lua_pushboolean(L
, 1);
343 /* Seems we couldn't find a valid UID to switch to */
344 lua_pushboolean(L
, 0);
345 lua_pushstring(L
, "invalid-uid");
349 int lc_setgid(lua_State
*L
) {
352 if(lua_gettop(L
) < 1) {
356 if(!lua_isnumber(L
, 1) && lua_tostring(L
, 1)) {
357 /* Passed GID is actually a string, so look up the GID */
359 g
= getgrnam(lua_tostring(L
, 1));
362 lua_pushboolean(L
, 0);
363 lua_pushstring(L
, "no-such-group");
369 gid
= lua_tonumber(L
, 1);
373 /* Ok, attempt setgid */
378 lua_pushboolean(L
, 0);
382 lua_pushstring(L
, "invalid-gid");
386 lua_pushstring(L
, "permission-denied");
390 lua_pushstring(L
, "unknown-error");
396 lua_pushboolean(L
, 1);
401 /* Seems we couldn't find a valid GID to switch to */
402 lua_pushboolean(L
, 0);
403 lua_pushstring(L
, "invalid-gid");
407 int lc_initgroups(lua_State
*L
) {
412 if(!lua_isstring(L
, 1)) {
414 lua_pushstring(L
, "invalid-username");
418 p
= getpwnam(lua_tostring(L
, 1));
422 lua_pushstring(L
, "no-such-user");
426 if(lua_gettop(L
) < 2) {
430 switch(lua_type(L
, 2)) {
436 gid
= lua_tointeger(L
, 2);
441 lua_pushstring(L
, "invalid-gid");
445 ret
= initgroups(lua_tostring(L
, 1), gid
);
451 lua_pushstring(L
, "no-memory");
456 lua_pushstring(L
, "permission-denied");
461 lua_pushstring(L
, "unknown-error");
464 lua_pushboolean(L
, 1);
471 int lc_umask(lua_State
*L
) {
472 char old_mode_string
[7];
473 mode_t old_mode
= umask(strtoul(luaL_checkstring(L
, 1), NULL
, 8));
475 snprintf(old_mode_string
, sizeof(old_mode_string
), "%03o", old_mode
);
476 old_mode_string
[sizeof(old_mode_string
) - 1] = 0;
477 lua_pushstring(L
, old_mode_string
);
482 int lc_mkdir(lua_State
*L
) {
483 int ret
= mkdir(luaL_checkstring(L
, 1), S_IRUSR
| S_IWUSR
| S_IXUSR
484 | S_IRGRP
| S_IWGRP
| S_IXGRP
485 | S_IROTH
| S_IXOTH
); /* mode 775 */
487 lua_pushboolean(L
, ret
== 0);
490 lua_pushstring(L
, strerror(errno
));
497 /* Like POSIX's setrlimit()/getrlimit() API functions.
500 * pposix.setrlimit( resource, soft limit, hard limit)
502 * Any negative limit will be replace with the current limit by an additional call of getrlimit().
505 * pposix.setrlimit("NOFILE", 1000, 2000)
507 int string2resource(const char *s
) {
508 if(!strcmp(s
, "CORE")) {
512 if(!strcmp(s
, "CPU")) {
516 if(!strcmp(s
, "DATA")) {
520 if(!strcmp(s
, "FSIZE")) {
524 if(!strcmp(s
, "NOFILE")) {
525 return RLIMIT_NOFILE
;
528 if(!strcmp(s
, "STACK")) {
532 #if !(defined(sun) || defined(__sun) || defined(__APPLE__))
534 if(!strcmp(s
, "MEMLOCK")) {
535 return RLIMIT_MEMLOCK
;
538 if(!strcmp(s
, "NPROC")) {
542 if(!strcmp(s
, "RSS")) {
549 if(!strcmp(s
, "NICE")) {
557 rlim_t
arg_to_rlimit(lua_State
*L
, int idx
, rlim_t current
) {
558 switch(lua_type(L
, idx
)) {
561 if(strcmp(lua_tostring(L
, idx
), "unlimited") == 0) {
562 return RLIM_INFINITY
;
564 return luaL_argerror(L
, idx
, "unexpected type");
567 return lua_tointeger(L
, idx
);
574 return luaL_argerror(L
, idx
, "unexpected type");
578 int lc_setrlimit(lua_State
*L
) {
580 int arguments
= lua_gettop(L
);
583 if(arguments
< 1 || arguments
> 3) {
584 lua_pushboolean(L
, 0);
585 lua_pushstring(L
, "incorrect-arguments");
589 rid
= string2resource(luaL_checkstring(L
, 1));
592 lua_pushboolean(L
, 0);
593 lua_pushstring(L
, "invalid-resource");
597 /* Fetch current values to use as defaults */
598 if(getrlimit(rid
, &lim
)) {
599 lua_pushboolean(L
, 0);
600 lua_pushstring(L
, "getrlimit-failed");
604 lim
.rlim_cur
= arg_to_rlimit(L
, 2, lim
.rlim_cur
);
605 lim
.rlim_max
= arg_to_rlimit(L
, 3, lim
.rlim_max
);
607 if(setrlimit(rid
, &lim
)) {
608 lua_pushboolean(L
, 0);
609 lua_pushstring(L
, "setrlimit-failed");
613 lua_pushboolean(L
, 1);
617 int lc_getrlimit(lua_State
*L
) {
618 int arguments
= lua_gettop(L
);
619 const char *resource
= NULL
;
624 lua_pushboolean(L
, 0);
625 lua_pushstring(L
, "invalid-arguments");
629 resource
= luaL_checkstring(L
, 1);
630 rid
= string2resource(resource
);
633 if(getrlimit(rid
, &lim
)) {
634 lua_pushboolean(L
, 0);
635 lua_pushstring(L
, "getrlimit-failed.");
639 /* Unsupported resource. Sorry I'm pretty limited by POSIX standard. */
640 lua_pushboolean(L
, 0);
641 lua_pushstring(L
, "invalid-resource");
645 lua_pushboolean(L
, 1);
647 if(lim
.rlim_cur
== RLIM_INFINITY
) {
648 lua_pushstring(L
, "unlimited");
650 lua_pushnumber(L
, lim
.rlim_cur
);
653 if(lim
.rlim_max
== RLIM_INFINITY
) {
654 lua_pushstring(L
, "unlimited");
656 lua_pushnumber(L
, lim
.rlim_max
);
662 int lc_abort(lua_State
*L
) {
668 int lc_uname(lua_State
*L
) {
669 struct utsname uname_info
;
671 if(uname(&uname_info
) != 0) {
673 lua_pushstring(L
, strerror(errno
));
677 lua_createtable(L
, 0, 6);
678 lua_pushstring(L
, uname_info
.sysname
);
679 lua_setfield(L
, -2, "sysname");
680 lua_pushstring(L
, uname_info
.nodename
);
681 lua_setfield(L
, -2, "nodename");
682 lua_pushstring(L
, uname_info
.release
);
683 lua_setfield(L
, -2, "release");
684 lua_pushstring(L
, uname_info
.version
);
685 lua_setfield(L
, -2, "version");
686 lua_pushstring(L
, uname_info
.machine
);
687 lua_setfield(L
, -2, "machine");
689 lua_pushstring(L
, uname_info
.domainname
);
690 lua_setfield(L
, -2, "domainname");
695 int lc_setenv(lua_State
*L
) {
696 const char *var
= luaL_checkstring(L
, 1);
699 /* If the second argument is nil or nothing, unset the var */
700 if(lua_isnoneornil(L
, 2)) {
701 if(unsetenv(var
) != 0) {
703 lua_pushstring(L
, strerror(errno
));
707 lua_pushboolean(L
, 1);
711 value
= luaL_checkstring(L
, 2);
713 if(setenv(var
, value
, 1) != 0) {
715 lua_pushstring(L
, strerror(errno
));
719 lua_pushboolean(L
, 1);
724 int lc_meminfo(lua_State
*L
) {
725 struct mallinfo info
= mallinfo();
726 lua_createtable(L
, 0, 5);
727 /* This is the total size of memory allocated with sbrk by malloc, in bytes. */
728 lua_pushinteger(L
, info
.arena
);
729 lua_setfield(L
, -2, "allocated");
730 /* This is the total size of memory allocated with mmap, in bytes. */
731 lua_pushinteger(L
, info
.hblkhd
);
732 lua_setfield(L
, -2, "allocated_mmap");
733 /* This is the total size of memory occupied by chunks handed out by malloc. */
734 lua_pushinteger(L
, info
.uordblks
);
735 lua_setfield(L
, -2, "used");
736 /* This is the total size of memory occupied by free (not in use) chunks. */
737 lua_pushinteger(L
, info
.fordblks
);
738 lua_setfield(L
, -2, "unused");
739 /* This is the size of the top-most releasable chunk that normally borders the
740 end of the heap (i.e., the high end of the virtual address space's data segment). */
741 lua_pushinteger(L
, info
.keepcost
);
742 lua_setfield(L
, -2, "returnable");
748 * Append some data to a file handle
749 * Attempt to allocate space first
750 * Truncate to original size on failure
752 int lc_atomic_append(lua_State
*L
) {
756 FILE *f
= *(FILE **) luaL_checkudata(L
, 1, LUA_FILEHANDLE
);
757 const char *data
= luaL_checklstring(L
, 2, &len
);
759 off_t offset
= ftell(f
);
761 #if defined(__linux__)
762 /* Try to allocate space without changing the file size. */
763 if((err
= fallocate(fileno(f
), FALLOC_FL_KEEP_SIZE
, offset
, len
))) {
765 /* Some old versions of Linux apparently use the return value instead of errno */
769 case ENOSYS
: /* Kernel doesn't implement fallocate */
770 case EOPNOTSUPP
: /* Filesystem doesn't support it */
771 /* Ignore and proceed to try to write */
774 case ENOSPC
: /* No space left */
775 default: /* Other issues */
777 lua_pushstring(L
, strerror(err
));
778 lua_pushinteger(L
, err
);
784 if(fwrite(data
, sizeof(char), len
, f
) == len
) {
786 lua_pushboolean(L
, 1); /* Great success! */
795 fseek(f
, offset
, SEEK_SET
);
797 /* Cut partially written data */
798 if(ftruncate(fileno(f
), offset
)) {
799 /* The file is now most likely corrupted, throw hard error */
800 return luaL_error(L
, "atomic_append() failed in ftruncate(): %s", strerror(errno
));
804 lua_pushstring(L
, strerror(err
));
805 lua_pushinteger(L
, err
);
809 /* Register functions */
811 int luaopen_util_pposix(lua_State
*L
) {
812 #if (LUA_VERSION_NUM > 501)
813 luaL_checkversion(L
);
815 luaL_Reg exports
[] = {
816 { "abort", lc_abort
},
818 { "daemonize", lc_daemonize
},
820 { "syslog_open", lc_syslog_open
},
821 { "syslog_close", lc_syslog_close
},
822 { "syslog_log", lc_syslog_log
},
823 { "syslog_setminlevel", lc_syslog_setmask
},
825 { "getpid", lc_getpid
},
826 { "getuid", lc_getuid
},
827 { "getgid", lc_getgid
},
829 { "setuid", lc_setuid
},
830 { "setgid", lc_setgid
},
831 { "initgroups", lc_initgroups
},
833 { "umask", lc_umask
},
835 { "mkdir", lc_mkdir
},
837 { "setrlimit", lc_setrlimit
},
838 { "getrlimit", lc_getrlimit
},
840 { "uname", lc_uname
},
842 { "setenv", lc_setenv
},
845 { "meminfo", lc_meminfo
},
848 { "atomic_append", lc_atomic_append
},
854 luaL_setfuncs(L
, exports
, 0);
857 lua_pushinteger(L
, ENOENT
);
858 lua_setfield(L
, -2, "ENOENT");
861 lua_pushliteral(L
, "pposix");
862 lua_setfield(L
, -2, "_NAME");
864 lua_pushliteral(L
, MODULE_VERSION
);
865 lua_setfield(L
, -2, "_VERSION");