mod_s2s: Remove warning about hostname mismatch
[prosody.git] / util-src / pposix.c
blob169343b8d52b0678f8f11e81178ebe35d16452dd
1 /* Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 -- Copyright (C) 2009 Tobias Markmann
5 --
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
8 --
9 */
12 * pposix.c
13 * POSIX support functions for Lua
16 #define MODULE_VERSION "0.4.0"
19 #if defined(__linux__)
20 #ifndef _GNU_SOURCE
21 #define _GNU_SOURCE
22 #endif
23 #else
24 #ifndef _DEFAULT_SOURCE
25 #define _DEFAULT_SOURCE
26 #endif
27 #endif
29 #if defined(__APPLE__)
30 #ifndef _DARWIN_C_SOURCE
31 #define _DARWIN_C_SOURCE
32 #endif
33 #endif
35 #if ! defined(__FreeBSD__)
36 #ifndef _POSIX_C_SOURCE
37 #define _POSIX_C_SOURCE 200809L
38 #endif
39 #endif
41 #include <stdlib.h>
42 #include <math.h>
43 #include <unistd.h>
44 #include <libgen.h>
45 #include <sys/resource.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/utsname.h>
49 #include <fcntl.h>
51 #include <syslog.h>
52 #include <pwd.h>
53 #include <grp.h>
55 #include <string.h>
56 #include <errno.h>
57 #include "lua.h"
58 #include "lualib.h"
59 #include "lauxlib.h"
61 #if (LUA_VERSION_NUM == 501)
62 #define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R)
63 #endif
65 #include <fcntl.h>
66 #if defined(__linux__)
67 #include <linux/falloc.h>
68 #endif
70 #if !defined(WITHOUT_MALLINFO) && defined(__linux__) && defined(__GLIBC__)
71 #include <malloc.h>
72 #define WITH_MALLINFO
73 #endif
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
81 * messed up.
83 * See issue #412
85 #define fork() rfork(RFPROC)
86 #endif
88 /* Daemonization support */
90 static int lc_daemonize(lua_State *L) {
92 pid_t pid;
94 if(getppid() == 1) {
95 lua_pushboolean(L, 0);
96 lua_pushstring(L, "already-daemonized");
97 return 2;
100 /* Attempt initial fork */
101 if((pid = fork()) < 0) {
102 /* Forking failed */
103 lua_pushboolean(L, 0);
104 lua_pushstring(L, "fork-failed");
105 return 2;
106 } else if(pid != 0) {
107 /* We are the parent process */
108 lua_pushboolean(L, 1);
109 lua_pushnumber(L, pid);
110 return 2;
113 /* and we are the child process */
114 if(setsid() == -1) {
115 /* We failed to become session leader */
116 /* (we probably already were) */
117 lua_pushboolean(L, 0);
118 lua_pushstring(L, "setsid-failed");
119 return 2;
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 */
128 if(fork()) {
129 exit(0);
132 /* Show's over, let's continue */
133 lua_pushboolean(L, 1);
134 lua_pushnil(L);
135 return 2;
138 /* Syslog support */
140 const char *const facility_strings[] = {
141 "auth",
142 #if !(defined(sun) || defined(__sun))
143 "authpriv",
144 #endif
145 "cron",
146 "daemon",
147 #if !(defined(sun) || defined(__sun))
148 "ftp",
149 #endif
150 "kern",
151 "local0",
152 "local1",
153 "local2",
154 "local3",
155 "local4",
156 "local5",
157 "local6",
158 "local7",
159 "lpr",
160 "mail",
161 "syslog",
162 "user",
163 "uucp",
164 NULL
166 int facility_constants[] = {
167 LOG_AUTH,
168 #if !(defined(sun) || defined(__sun))
169 LOG_AUTHPRIV,
170 #endif
171 LOG_CRON,
172 LOG_DAEMON,
173 #if !(defined(sun) || defined(__sun))
174 LOG_FTP,
175 #endif
176 LOG_KERN,
177 LOG_LOCAL0,
178 LOG_LOCAL1,
179 LOG_LOCAL2,
180 LOG_LOCAL3,
181 LOG_LOCAL4,
182 LOG_LOCAL5,
183 LOG_LOCAL6,
184 LOG_LOCAL7,
185 LOG_LPR,
186 LOG_MAIL,
187 LOG_NEWS,
188 LOG_SYSLOG,
189 LOG_USER,
190 LOG_UUCP,
194 /* "
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
199 constant.
200 " -- syslog manpage
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);
210 if(syslog_ident) {
211 free(syslog_ident);
214 syslog_ident = strdup(lua_tostring(L, 1));
216 openlog(syslog_ident, LOG_PID, facility);
217 return 0;
220 const char *const level_strings[] = {
221 "debug",
222 "info",
223 "notice",
224 "warn",
225 "error",
226 NULL
228 int level_constants[] = {
229 LOG_DEBUG,
230 LOG_INFO,
231 LOG_NOTICE,
232 LOG_WARNING,
233 LOG_CRIT,
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));
241 } else {
242 syslog(level, "%s", lua_tostring(L, 2));
245 return 0;
248 int lc_syslog_close(lua_State *L) {
249 (void)L;
250 closelog();
252 if(syslog_ident) {
253 free(syslog_ident);
254 syslog_ident = NULL;
257 return 0;
260 int lc_syslog_setmask(lua_State *L) {
261 int level_idx = luaL_checkoption(L, 1, "notice", level_strings);
262 int mask = 0;
264 do {
265 mask |= LOG_MASK(level_constants[level_idx]);
266 } while(++level_idx <= 4);
268 setlogmask(mask);
269 return 0;
272 /* getpid */
274 int lc_getpid(lua_State *L) {
275 lua_pushinteger(L, getpid());
276 return 1;
279 /* UID/GID functions */
281 int lc_getuid(lua_State *L) {
282 lua_pushinteger(L, getuid());
283 return 1;
286 int lc_getgid(lua_State *L) {
287 lua_pushinteger(L, getgid());
288 return 1;
291 int lc_setuid(lua_State *L) {
292 int uid = -1;
294 if(lua_gettop(L) < 1) {
295 return 0;
298 if(!lua_isnumber(L, 1) && lua_tostring(L, 1)) {
299 /* Passed UID is actually a string, so look up the UID */
300 struct passwd *p;
301 p = getpwnam(lua_tostring(L, 1));
303 if(!p) {
304 lua_pushboolean(L, 0);
305 lua_pushstring(L, "no-such-user");
306 return 2;
309 uid = p->pw_uid;
310 } else {
311 uid = lua_tonumber(L, 1);
314 if(uid > -1) {
315 /* Ok, attempt setuid */
316 errno = 0;
318 if(setuid(uid)) {
319 /* Fail */
320 lua_pushboolean(L, 0);
322 switch(errno) {
323 case EINVAL:
324 lua_pushstring(L, "invalid-uid");
325 break;
327 case EPERM:
328 lua_pushstring(L, "permission-denied");
329 break;
331 default:
332 lua_pushstring(L, "unknown-error");
335 return 2;
336 } else {
337 /* Success! */
338 lua_pushboolean(L, 1);
339 return 1;
343 /* Seems we couldn't find a valid UID to switch to */
344 lua_pushboolean(L, 0);
345 lua_pushstring(L, "invalid-uid");
346 return 2;
349 int lc_setgid(lua_State *L) {
350 int gid = -1;
352 if(lua_gettop(L) < 1) {
353 return 0;
356 if(!lua_isnumber(L, 1) && lua_tostring(L, 1)) {
357 /* Passed GID is actually a string, so look up the GID */
358 struct group *g;
359 g = getgrnam(lua_tostring(L, 1));
361 if(!g) {
362 lua_pushboolean(L, 0);
363 lua_pushstring(L, "no-such-group");
364 return 2;
367 gid = g->gr_gid;
368 } else {
369 gid = lua_tonumber(L, 1);
372 if(gid > -1) {
373 /* Ok, attempt setgid */
374 errno = 0;
376 if(setgid(gid)) {
377 /* Fail */
378 lua_pushboolean(L, 0);
380 switch(errno) {
381 case EINVAL:
382 lua_pushstring(L, "invalid-gid");
383 break;
385 case EPERM:
386 lua_pushstring(L, "permission-denied");
387 break;
389 default:
390 lua_pushstring(L, "unknown-error");
393 return 2;
394 } else {
395 /* Success! */
396 lua_pushboolean(L, 1);
397 return 1;
401 /* Seems we couldn't find a valid GID to switch to */
402 lua_pushboolean(L, 0);
403 lua_pushstring(L, "invalid-gid");
404 return 2;
407 int lc_initgroups(lua_State *L) {
408 int ret;
409 gid_t gid;
410 struct passwd *p;
412 if(!lua_isstring(L, 1)) {
413 lua_pushnil(L);
414 lua_pushstring(L, "invalid-username");
415 return 2;
418 p = getpwnam(lua_tostring(L, 1));
420 if(!p) {
421 lua_pushnil(L);
422 lua_pushstring(L, "no-such-user");
423 return 2;
426 if(lua_gettop(L) < 2) {
427 lua_pushnil(L);
430 switch(lua_type(L, 2)) {
431 case LUA_TNIL:
432 gid = p->pw_gid;
433 break;
435 case LUA_TNUMBER:
436 gid = lua_tointeger(L, 2);
437 break;
439 default:
440 lua_pushnil(L);
441 lua_pushstring(L, "invalid-gid");
442 return 2;
445 ret = initgroups(lua_tostring(L, 1), gid);
447 if(ret) {
448 switch(errno) {
449 case ENOMEM:
450 lua_pushnil(L);
451 lua_pushstring(L, "no-memory");
452 break;
454 case EPERM:
455 lua_pushnil(L);
456 lua_pushstring(L, "permission-denied");
457 break;
459 default:
460 lua_pushnil(L);
461 lua_pushstring(L, "unknown-error");
463 } else {
464 lua_pushboolean(L, 1);
465 lua_pushnil(L);
468 return 2;
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);
479 return 1;
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);
489 if(ret) {
490 lua_pushstring(L, strerror(errno));
491 return 2;
494 return 1;
497 /* Like POSIX's setrlimit()/getrlimit() API functions.
499 * Syntax:
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().
504 * Example usage:
505 * pposix.setrlimit("NOFILE", 1000, 2000)
507 int string2resource(const char *s) {
508 if(!strcmp(s, "CORE")) {
509 return RLIMIT_CORE;
512 if(!strcmp(s, "CPU")) {
513 return RLIMIT_CPU;
516 if(!strcmp(s, "DATA")) {
517 return RLIMIT_DATA;
520 if(!strcmp(s, "FSIZE")) {
521 return RLIMIT_FSIZE;
524 if(!strcmp(s, "NOFILE")) {
525 return RLIMIT_NOFILE;
528 if(!strcmp(s, "STACK")) {
529 return RLIMIT_STACK;
532 #if !(defined(sun) || defined(__sun) || defined(__APPLE__))
534 if(!strcmp(s, "MEMLOCK")) {
535 return RLIMIT_MEMLOCK;
538 if(!strcmp(s, "NPROC")) {
539 return RLIMIT_NPROC;
542 if(!strcmp(s, "RSS")) {
543 return RLIMIT_RSS;
546 #endif
547 #ifdef RLIMIT_NICE
549 if(!strcmp(s, "NICE")) {
550 return RLIMIT_NICE;
553 #endif
554 return -1;
557 rlim_t arg_to_rlimit(lua_State *L, int idx, rlim_t current) {
558 switch(lua_type(L, idx)) {
559 case LUA_TSTRING:
561 if(strcmp(lua_tostring(L, idx), "unlimited") == 0) {
562 return RLIM_INFINITY;
564 return luaL_argerror(L, idx, "unexpected type");
566 case LUA_TNUMBER:
567 return lua_tointeger(L, idx);
569 case LUA_TNONE:
570 case LUA_TNIL:
571 return current;
573 default:
574 return luaL_argerror(L, idx, "unexpected type");
578 int lc_setrlimit(lua_State *L) {
579 struct rlimit lim;
580 int arguments = lua_gettop(L);
581 int rid = -1;
583 if(arguments < 1 || arguments > 3) {
584 lua_pushboolean(L, 0);
585 lua_pushstring(L, "incorrect-arguments");
586 return 2;
589 rid = string2resource(luaL_checkstring(L, 1));
591 if(rid == -1) {
592 lua_pushboolean(L, 0);
593 lua_pushstring(L, "invalid-resource");
594 return 2;
597 /* Fetch current values to use as defaults */
598 if(getrlimit(rid, &lim)) {
599 lua_pushboolean(L, 0);
600 lua_pushstring(L, "getrlimit-failed");
601 return 2;
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");
610 return 2;
613 lua_pushboolean(L, 1);
614 return 1;
617 int lc_getrlimit(lua_State *L) {
618 int arguments = lua_gettop(L);
619 const char *resource = NULL;
620 int rid = -1;
621 struct rlimit lim;
623 if(arguments != 1) {
624 lua_pushboolean(L, 0);
625 lua_pushstring(L, "invalid-arguments");
626 return 2;
629 resource = luaL_checkstring(L, 1);
630 rid = string2resource(resource);
632 if(rid != -1) {
633 if(getrlimit(rid, &lim)) {
634 lua_pushboolean(L, 0);
635 lua_pushstring(L, "getrlimit-failed.");
636 return 2;
638 } else {
639 /* Unsupported resource. Sorry I'm pretty limited by POSIX standard. */
640 lua_pushboolean(L, 0);
641 lua_pushstring(L, "invalid-resource");
642 return 2;
645 lua_pushboolean(L, 1);
647 if(lim.rlim_cur == RLIM_INFINITY) {
648 lua_pushstring(L, "unlimited");
649 } else {
650 lua_pushnumber(L, lim.rlim_cur);
653 if(lim.rlim_max == RLIM_INFINITY) {
654 lua_pushstring(L, "unlimited");
655 } else {
656 lua_pushnumber(L, lim.rlim_max);
659 return 3;
662 int lc_abort(lua_State *L) {
663 (void)L;
664 abort();
665 return 0;
668 int lc_uname(lua_State *L) {
669 struct utsname uname_info;
671 if(uname(&uname_info) != 0) {
672 lua_pushnil(L);
673 lua_pushstring(L, strerror(errno));
674 return 2;
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");
688 #ifdef __USE_GNU
689 lua_pushstring(L, uname_info.domainname);
690 lua_setfield(L, -2, "domainname");
691 #endif
692 return 1;
695 int lc_setenv(lua_State *L) {
696 const char *var = luaL_checkstring(L, 1);
697 const char *value;
699 /* If the second argument is nil or nothing, unset the var */
700 if(lua_isnoneornil(L, 2)) {
701 if(unsetenv(var) != 0) {
702 lua_pushnil(L);
703 lua_pushstring(L, strerror(errno));
704 return 2;
707 lua_pushboolean(L, 1);
708 return 1;
711 value = luaL_checkstring(L, 2);
713 if(setenv(var, value, 1) != 0) {
714 lua_pushnil(L);
715 lua_pushstring(L, strerror(errno));
716 return 2;
719 lua_pushboolean(L, 1);
720 return 1;
723 #ifdef WITH_MALLINFO
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");
743 return 1;
745 #endif
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) {
753 int err;
754 size_t len;
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))) {
764 if(errno != 0) {
765 /* Some old versions of Linux apparently use the return value instead of errno */
766 err = errno;
768 switch(err) {
769 case ENOSYS: /* Kernel doesn't implement fallocate */
770 case EOPNOTSUPP: /* Filesystem doesn't support it */
771 /* Ignore and proceed to try to write */
772 break;
774 case ENOSPC: /* No space left */
775 default: /* Other issues */
776 lua_pushnil(L);
777 lua_pushstring(L, strerror(err));
778 lua_pushinteger(L, err);
779 return 3;
782 #endif
784 if(fwrite(data, sizeof(char), len, f) == len) {
785 if(fflush(f) == 0) {
786 lua_pushboolean(L, 1); /* Great success! */
787 return 1;
788 } else {
789 err = errno;
791 } else {
792 err = ferror(f);
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));
803 lua_pushnil(L);
804 lua_pushstring(L, strerror(err));
805 lua_pushinteger(L, err);
806 return 3;
809 /* Register functions */
811 int luaopen_util_pposix(lua_State *L) {
812 #if (LUA_VERSION_NUM > 501)
813 luaL_checkversion(L);
814 #endif
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 },
844 #ifdef WITH_MALLINFO
845 { "meminfo", lc_meminfo },
846 #endif
848 { "atomic_append", lc_atomic_append },
850 { NULL, NULL }
853 lua_newtable(L);
854 luaL_setfuncs(L, exports, 0);
856 #ifdef ENOENT
857 lua_pushinteger(L, ENOENT);
858 lua_setfield(L, -2, "ENOENT");
859 #endif
861 lua_pushliteral(L, "pposix");
862 lua_setfield(L, -2, "_NAME");
864 lua_pushliteral(L, MODULE_VERSION);
865 lua_setfield(L, -2, "_VERSION");
867 return 1;