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