Merge 0.9->0.10
[prosody.git] / util-src / pposix.c
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 */
10
11 /*
12 * pposix.c
13 * POSIX support functions for Lua
14 */
15
16 #define MODULE_VERSION "0.3.6"
17
18 #include <stdlib.h>
19 #include <math.h>
20 #include <unistd.h>
21 #include <libgen.h>
22 #include <sys/resource.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/utsname.h>
26 #include <fcntl.h>
27
28 #include <syslog.h>
29 #include <pwd.h>
30 #include <grp.h>
31
32 #include <string.h>
33 #include <errno.h>
34 #include "lua.h"
35 #include "lualib.h"
36 #include "lauxlib.h"
37
38 #if (LUA_VERSION_NUM == 502)
39 #define luaL_register(L, N, R) luaL_setfuncs(L, R, 0)
40 #endif
41
42 #include <fcntl.h>
43 #if defined(__linux__) && defined(_GNU_SOURCE)
44 #include <linux/falloc.h>
45 #endif
46
47 #if (defined(_SVID_SOURCE) && !defined(WITHOUT_MALLINFO))
48 #include <malloc.h>
49 #define WITH_MALLINFO
50 #endif
51
52 /* Daemonization support */
53
54 static int lc_daemonize(lua_State* L) {
55
56         pid_t pid;
57
58         if(getppid() == 1) {
59                 lua_pushboolean(L, 0);
60                 lua_pushstring(L, "already-daemonized");
61                 return 2;
62         }
63
64         /* Attempt initial fork */
65         if((pid = fork()) < 0) {
66                 /* Forking failed */
67                 lua_pushboolean(L, 0);
68                 lua_pushstring(L, "fork-failed");
69                 return 2;
70         } else if(pid != 0) {
71                 /* We are the parent process */
72                 lua_pushboolean(L, 1);
73                 lua_pushnumber(L, pid);
74                 return 2;
75         }
76
77         /* and we are the child process */
78         if(setsid() == -1) {
79                 /* We failed to become session leader */
80                 /* (we probably already were) */
81                 lua_pushboolean(L, 0);
82                 lua_pushstring(L, "setsid-failed");
83                 return 2;
84         }
85
86         /* Close stdin, stdout, stderr */
87         close(0);
88         close(1);
89         close(2);
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);
94
95         /* Final fork, use it wisely */
96         if(fork()) {
97                 exit(0);
98         }
99
100         /* Show's over, let's continue */
101         lua_pushboolean(L, 1);
102         lua_pushnil(L);
103         return 2;
104 }
105
106 /* Syslog support */
107
108 const char* const facility_strings[] = {
109         "auth",
110 #if !(defined(sun) || defined(__sun))
111         "authpriv",
112 #endif
113         "cron",
114         "daemon",
115 #if !(defined(sun) || defined(__sun))
116         "ftp",
117 #endif
118         "kern",
119         "local0",
120         "local1",
121         "local2",
122         "local3",
123         "local4",
124         "local5",
125         "local6",
126         "local7",
127         "lpr",
128         "mail",
129         "syslog",
130         "user",
131         "uucp",
132         NULL
133 };
134 int facility_constants[] =      {
135         LOG_AUTH,
136 #if !(defined(sun) || defined(__sun))
137         LOG_AUTHPRIV,
138 #endif
139         LOG_CRON,
140         LOG_DAEMON,
141 #if !(defined(sun) || defined(__sun))
142         LOG_FTP,
143 #endif
144         LOG_KERN,
145         LOG_LOCAL0,
146         LOG_LOCAL1,
147         LOG_LOCAL2,
148         LOG_LOCAL3,
149         LOG_LOCAL4,
150         LOG_LOCAL5,
151         LOG_LOCAL6,
152         LOG_LOCAL7,
153         LOG_LPR,
154         LOG_MAIL,
155         LOG_NEWS,
156         LOG_SYSLOG,
157         LOG_USER,
158         LOG_UUCP,
159         -1
160 };
161
162 /* "
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
167        constant.
168    " -- syslog manpage
169 */
170 char* syslog_ident = NULL;
171
172 int lc_syslog_open(lua_State* L) {
173         int facility = luaL_checkoption(L, 2, "daemon", facility_strings);
174         facility = facility_constants[facility];
175
176         luaL_checkstring(L, 1);
177
178         if(syslog_ident) {
179                 free(syslog_ident);
180         }
181
182         syslog_ident = strdup(lua_tostring(L, 1));
183
184         openlog(syslog_ident, LOG_PID, facility);
185         return 0;
186 }
187
188 const char* const level_strings[] = {
189         "debug",
190         "info",
191         "notice",
192         "warn",
193         "error",
194         NULL
195 };
196 int level_constants[] =         {
197         LOG_DEBUG,
198         LOG_INFO,
199         LOG_NOTICE,
200         LOG_WARNING,
201         LOG_CRIT,
202         -1
203 };
204 int lc_syslog_log(lua_State* L) {
205         int level = level_constants[luaL_checkoption(L, 1, "notice", level_strings)];
206
207         if(lua_gettop(L) == 3) {
208                 syslog(level, "%s: %s", luaL_checkstring(L, 2), luaL_checkstring(L, 3));
209         } else {
210                 syslog(level, "%s", lua_tostring(L, 2));
211         }
212
213         return 0;
214 }
215
216 int lc_syslog_close(lua_State* L) {
217         closelog();
218
219         if(syslog_ident) {
220                 free(syslog_ident);
221                 syslog_ident = NULL;
222         }
223
224         return 0;
225 }
226
227 int lc_syslog_setmask(lua_State* L) {
228         int level_idx = luaL_checkoption(L, 1, "notice", level_strings);
229         int mask = 0;
230
231         do {
232                 mask |= LOG_MASK(level_constants[level_idx]);
233         } while(++level_idx <= 4);
234
235         setlogmask(mask);
236         return 0;
237 }
238
239 /* getpid */
240
241 int lc_getpid(lua_State* L) {
242         lua_pushinteger(L, getpid());
243         return 1;
244 }
245
246 /* UID/GID functions */
247
248 int lc_getuid(lua_State* L) {
249         lua_pushinteger(L, getuid());
250         return 1;
251 }
252
253 int lc_getgid(lua_State* L) {
254         lua_pushinteger(L, getgid());
255         return 1;
256 }
257
258 int lc_setuid(lua_State* L) {
259         int uid = -1;
260
261         if(lua_gettop(L) < 1) {
262                 return 0;
263         }
264
265         if(!lua_isnumber(L, 1) && lua_tostring(L, 1)) {
266                 /* Passed UID is actually a string, so look up the UID */
267                 struct passwd* p;
268                 p = getpwnam(lua_tostring(L, 1));
269
270                 if(!p) {
271                         lua_pushboolean(L, 0);
272                         lua_pushstring(L, "no-such-user");
273                         return 2;
274                 }
275
276                 uid = p->pw_uid;
277         } else {
278                 uid = lua_tonumber(L, 1);
279         }
280
281         if(uid > -1) {
282                 /* Ok, attempt setuid */
283                 errno = 0;
284
285                 if(setuid(uid)) {
286                         /* Fail */
287                         lua_pushboolean(L, 0);
288
289                         switch(errno) {
290                                 case EINVAL:
291                                         lua_pushstring(L, "invalid-uid");
292                                         break;
293                                 case EPERM:
294                                         lua_pushstring(L, "permission-denied");
295                                         break;
296                                 default:
297                                         lua_pushstring(L, "unknown-error");
298                         }
299
300                         return 2;
301                 } else {
302                         /* Success! */
303                         lua_pushboolean(L, 1);
304                         return 1;
305                 }
306         }
307
308         /* Seems we couldn't find a valid UID to switch to */
309         lua_pushboolean(L, 0);
310         lua_pushstring(L, "invalid-uid");
311         return 2;
312 }
313
314 int lc_setgid(lua_State* L) {
315         int gid = -1;
316
317         if(lua_gettop(L) < 1) {
318                 return 0;
319         }
320
321         if(!lua_isnumber(L, 1) && lua_tostring(L, 1)) {
322                 /* Passed GID is actually a string, so look up the GID */
323                 struct group* g;
324                 g = getgrnam(lua_tostring(L, 1));
325
326                 if(!g) {
327                         lua_pushboolean(L, 0);
328                         lua_pushstring(L, "no-such-group");
329                         return 2;
330                 }
331
332                 gid = g->gr_gid;
333         } else {
334                 gid = lua_tonumber(L, 1);
335         }
336
337         if(gid > -1) {
338                 /* Ok, attempt setgid */
339                 errno = 0;
340
341                 if(setgid(gid)) {
342                         /* Fail */
343                         lua_pushboolean(L, 0);
344
345                         switch(errno) {
346                                 case EINVAL:
347                                         lua_pushstring(L, "invalid-gid");
348                                         break;
349                                 case EPERM:
350                                         lua_pushstring(L, "permission-denied");
351                                         break;
352                                 default:
353                                         lua_pushstring(L, "unknown-error");
354                         }
355
356                         return 2;
357                 } else {
358                         /* Success! */
359                         lua_pushboolean(L, 1);
360                         return 1;
361                 }
362         }
363
364         /* Seems we couldn't find a valid GID to switch to */
365         lua_pushboolean(L, 0);
366         lua_pushstring(L, "invalid-gid");
367         return 2;
368 }
369
370 int lc_initgroups(lua_State* L) {
371         int ret;
372         gid_t gid;
373         struct passwd* p;
374
375         if(!lua_isstring(L, 1)) {
376                 lua_pushnil(L);
377                 lua_pushstring(L, "invalid-username");
378                 return 2;
379         }
380
381         p = getpwnam(lua_tostring(L, 1));
382
383         if(!p) {
384                 lua_pushnil(L);
385                 lua_pushstring(L, "no-such-user");
386                 return 2;
387         }
388
389         if(lua_gettop(L) < 2) {
390                 lua_pushnil(L);
391         }
392
393         switch(lua_type(L, 2)) {
394                 case LUA_TNIL:
395                         gid = p->pw_gid;
396                         break;
397                 case LUA_TNUMBER:
398                         gid = lua_tointeger(L, 2);
399                         break;
400                 default:
401                         lua_pushnil(L);
402                         lua_pushstring(L, "invalid-gid");
403                         return 2;
404         }
405
406         ret = initgroups(lua_tostring(L, 1), gid);
407
408         if(ret) {
409                 switch(errno) {
410                         case ENOMEM:
411                                 lua_pushnil(L);
412                                 lua_pushstring(L, "no-memory");
413                                 break;
414                         case EPERM:
415                                 lua_pushnil(L);
416                                 lua_pushstring(L, "permission-denied");
417                                 break;
418                         default:
419                                 lua_pushnil(L);
420                                 lua_pushstring(L, "unknown-error");
421                 }
422         } else {
423                 lua_pushboolean(L, 1);
424                 lua_pushnil(L);
425         }
426
427         return 2;
428 }
429
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));
433
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);
437
438         return 1;
439 }
440
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 */
445
446         lua_pushboolean(L, ret == 0);
447
448         if(ret) {
449                 lua_pushstring(L, strerror(errno));
450                 return 2;
451         }
452
453         return 1;
454 }
455
456 /*      Like POSIX's setrlimit()/getrlimit() API functions.
457  *
458  *      Syntax:
459  *      pposix.setrlimit( resource, soft limit, hard limit)
460  *
461  *      Any negative limit will be replace with the current limit by an additional call of getrlimit().
462  *
463  *      Example usage:
464  *      pposix.setrlimit("NOFILE", 1000, 2000)
465  */
466 int string2resource(const char* s) {
467         if(!strcmp(s, "CORE")) {
468                 return RLIMIT_CORE;
469         }
470
471         if(!strcmp(s, "CPU")) {
472                 return RLIMIT_CPU;
473         }
474
475         if(!strcmp(s, "DATA")) {
476                 return RLIMIT_DATA;
477         }
478
479         if(!strcmp(s, "FSIZE")) {
480                 return RLIMIT_FSIZE;
481         }
482
483         if(!strcmp(s, "NOFILE")) {
484                 return RLIMIT_NOFILE;
485         }
486
487         if(!strcmp(s, "STACK")) {
488                 return RLIMIT_STACK;
489         }
490
491 #if !(defined(sun) || defined(__sun))
492
493         if(!strcmp(s, "MEMLOCK")) {
494                 return RLIMIT_MEMLOCK;
495         }
496
497         if(!strcmp(s, "NPROC")) {
498                 return RLIMIT_NPROC;
499         }
500
501         if(!strcmp(s, "RSS")) {
502                 return RLIMIT_RSS;
503         }
504
505 #endif
506 #ifdef RLIMIT_NICE
507
508         if(!strcmp(s, "NICE")) {
509                 return RLIMIT_NICE;
510         }
511
512 #endif
513         return -1;
514 }
515
516 unsigned long int arg_to_rlimit(lua_State* L, int idx, rlim_t current) {
517         switch(lua_type(L, idx)) {
518                 case LUA_TSTRING:
519
520                         if(strcmp(lua_tostring(L, idx), "unlimited") == 0) {
521                                 return RLIM_INFINITY;
522                         }
523
524                 case LUA_TNUMBER:
525                         return lua_tointeger(L, idx);
526                 case LUA_TNONE:
527                 case LUA_TNIL:
528                         return current;
529                 default:
530                         return luaL_argerror(L, idx, "unexpected type");
531         }
532 }
533
534 int lc_setrlimit(lua_State* L) {
535         struct rlimit lim;
536         int arguments = lua_gettop(L);
537         int rid = -1;
538
539         if(arguments < 1 || arguments > 3) {
540                 lua_pushboolean(L, 0);
541                 lua_pushstring(L, "incorrect-arguments");
542                 return 2;
543         }
544
545         rid = string2resource(luaL_checkstring(L, 1));
546
547         if(rid == -1) {
548                 lua_pushboolean(L, 0);
549                 lua_pushstring(L, "invalid-resource");
550                 return 2;
551         }
552
553         /* Fetch current values to use as defaults */
554         if(getrlimit(rid, &lim)) {
555                 lua_pushboolean(L, 0);
556                 lua_pushstring(L, "getrlimit-failed");
557                 return 2;
558         }
559
560         lim.rlim_cur = arg_to_rlimit(L, 2, lim.rlim_cur);
561         lim.rlim_max = arg_to_rlimit(L, 3, lim.rlim_max);
562
563         if(setrlimit(rid, &lim)) {
564                 lua_pushboolean(L, 0);
565                 lua_pushstring(L, "setrlimit-failed");
566                 return 2;
567         }
568
569         lua_pushboolean(L, 1);
570         return 1;
571 }
572
573 int lc_getrlimit(lua_State* L) {
574         int arguments = lua_gettop(L);
575         const char* resource = NULL;
576         int rid = -1;
577         struct rlimit lim;
578
579         if(arguments != 1) {
580                 lua_pushboolean(L, 0);
581                 lua_pushstring(L, "invalid-arguments");
582                 return 2;
583         }
584
585
586
587         resource = luaL_checkstring(L, 1);
588         rid = string2resource(resource);
589
590         if(rid != -1) {
591                 if(getrlimit(rid, &lim)) {
592                         lua_pushboolean(L, 0);
593                         lua_pushstring(L, "getrlimit-failed.");
594                         return 2;
595                 }
596         } else {
597                 /* Unsupported resoucrce. Sorry I'm pretty limited by POSIX standard. */
598                 lua_pushboolean(L, 0);
599                 lua_pushstring(L, "invalid-resource");
600                 return 2;
601         }
602
603         lua_pushboolean(L, 1);
604
605         if(lim.rlim_cur == RLIM_INFINITY) {
606                 lua_pushstring(L, "unlimited");
607         } else {
608                 lua_pushnumber(L, lim.rlim_cur);
609         }
610
611         if(lim.rlim_max == RLIM_INFINITY) {
612                 lua_pushstring(L, "unlimited");
613         } else {
614                 lua_pushnumber(L, lim.rlim_max);
615         }
616
617         return 3;
618 }
619
620 int lc_abort(lua_State* L) {
621         abort();
622         return 0;
623 }
624
625 int lc_uname(lua_State* L) {
626         struct utsname uname_info;
627
628         if(uname(&uname_info) != 0) {
629                 lua_pushnil(L);
630                 lua_pushstring(L, strerror(errno));
631                 return 2;
632         }
633
634         lua_newtable(L);
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");
645         return 1;
646 }
647
648 int lc_setenv(lua_State* L) {
649         const char* var = luaL_checkstring(L, 1);
650         const char* value;
651
652         /* If the second argument is nil or nothing, unset the var */
653         if(lua_isnoneornil(L, 2)) {
654                 if(unsetenv(var) != 0) {
655                         lua_pushnil(L);
656                         lua_pushstring(L, strerror(errno));
657                         return 2;
658                 }
659
660                 lua_pushboolean(L, 1);
661                 return 1;
662         }
663
664         value = luaL_checkstring(L, 2);
665
666         if(setenv(var, value, 1) != 0) {
667                 lua_pushnil(L);
668                 lua_pushstring(L, strerror(errno));
669                 return 2;
670         }
671
672         lua_pushboolean(L, 1);
673         return 1;
674 }
675
676 #ifdef WITH_MALLINFO
677 int lc_meminfo(lua_State* L) {
678         struct mallinfo info = mallinfo();
679         lua_newtable(L);
680         /* This is the total size of memory allocated with sbrk by malloc, in bytes. */
681         lua_pushinteger(L, info.arena);
682         lua_setfield(L, -2, "allocated");
683         /* This is the total size of memory allocated with mmap, in bytes. */
684         lua_pushinteger(L, info.hblkhd);
685         lua_setfield(L, -2, "allocated_mmap");
686         /* This is the total size of memory occupied by chunks handed out by malloc. */
687         lua_pushinteger(L, info.uordblks);
688         lua_setfield(L, -2, "used");
689         /* This is the total size of memory occupied by free (not in use) chunks. */
690         lua_pushinteger(L, info.fordblks);
691         lua_setfield(L, -2, "unused");
692         /* This is the size of the top-most releasable chunk that normally borders the
693            end of the heap (i.e., the high end of the virtual address space's data segment). */
694         lua_pushinteger(L, info.keepcost);
695         lua_setfield(L, -2, "returnable");
696         return 1;
697 }
698 #endif
699
700 /* File handle extraction blatantly stolen from
701  * https://github.com/rrthomas/luaposix/blob/master/lposix.c#L631
702  * */
703
704 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
705 int lc_fallocate(lua_State* L) {
706         int ret;
707         off_t offset, len;
708         FILE* f = *(FILE**) luaL_checkudata(L, 1, LUA_FILEHANDLE);
709
710         if(f == NULL) {
711                 return luaL_error(L, "attempt to use a closed file");
712         }
713
714         offset = luaL_checkinteger(L, 2);
715         len = luaL_checkinteger(L, 3);
716
717 #if defined(__linux__) && defined(_GNU_SOURCE)
718         errno = 0;
719         ret = fallocate(fileno(f), FALLOC_FL_KEEP_SIZE, offset, len);
720
721         if(ret == 0) {
722                 lua_pushboolean(L, 1);
723                 return 1;
724         }
725
726         /* Some old versions of Linux apparently use the return value instead of errno */
727         if(errno == 0) {
728                 errno = ret;
729         }
730
731         if(errno != ENOSYS && errno != EOPNOTSUPP) {
732                 lua_pushnil(L);
733                 lua_pushstring(L, strerror(errno));
734                 return 2;
735         }
736
737 #else
738 #warning Only using posix_fallocate() fallback.
739 #warning Linux fallocate() is strongly recommended if available: recompile with -D_GNU_SOURCE
740 #warning Note that posix_fallocate() will still be used on filesystems that dont support fallocate()
741 #endif
742
743         ret = posix_fallocate(fileno(f), offset, len);
744
745         if(ret == 0) {
746                 lua_pushboolean(L, 1);
747                 return 1;
748         } else {
749                 lua_pushnil(L);
750                 lua_pushstring(L, strerror(ret));
751                 /* posix_fallocate() can leave a bunch of NULs at the end, so we cut that
752                  * this assumes that offset == length of the file */
753                 ftruncate(fileno(f), offset);
754                 return 2;
755         }
756 }
757 #endif
758
759 /* Register functions */
760
761 int luaopen_util_pposix(lua_State* L) {
762         luaL_Reg exports[] = {
763                 { "abort", lc_abort },
764
765                 { "daemonize", lc_daemonize },
766
767                 { "syslog_open", lc_syslog_open },
768                 { "syslog_close", lc_syslog_close },
769                 { "syslog_log", lc_syslog_log },
770                 { "syslog_setminlevel", lc_syslog_setmask },
771
772                 { "getpid", lc_getpid },
773                 { "getuid", lc_getuid },
774                 { "getgid", lc_getgid },
775
776                 { "setuid", lc_setuid },
777                 { "setgid", lc_setgid },
778                 { "initgroups", lc_initgroups },
779
780                 { "umask", lc_umask },
781
782                 { "mkdir", lc_mkdir },
783
784                 { "setrlimit", lc_setrlimit },
785                 { "getrlimit", lc_getrlimit },
786
787                 { "uname", lc_uname },
788
789                 { "setenv", lc_setenv },
790
791 #ifdef WITH_MALLINFO
792                 { "meminfo", lc_meminfo },
793 #endif
794
795 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
796                 { "fallocate", lc_fallocate },
797 #endif
798
799                 { NULL, NULL }
800         };
801
802         lua_newtable(L);
803         luaL_register(L, NULL,  exports);
804
805         lua_pushliteral(L, "pposix");
806         lua_setfield(L, -2, "_NAME");
807
808         lua_pushliteral(L, MODULE_VERSION);
809         lua_setfield(L, -2, "_VERSION");
810
811         return 1;
812 }