1b69852d2a703cacf2619adeffe821ad5ddb3b94
[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 /* 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 #ifdef _GNU_SOURCE
646         lua_pushstring(L, uname_info.domainname);
647         lua_setfield(L, -2, "domainname");
648 #endif
649         return 1;
650 }
651
652 int lc_setenv(lua_State* L) {
653         const char* var = luaL_checkstring(L, 1);
654         const char* value;
655
656         /* If the second argument is nil or nothing, unset the var */
657         if(lua_isnoneornil(L, 2)) {
658                 if(unsetenv(var) != 0) {
659                         lua_pushnil(L);
660                         lua_pushstring(L, strerror(errno));
661                         return 2;
662                 }
663
664                 lua_pushboolean(L, 1);
665                 return 1;
666         }
667
668         value = luaL_checkstring(L, 2);
669
670         if(setenv(var, value, 1) != 0) {
671                 lua_pushnil(L);
672                 lua_pushstring(L, strerror(errno));
673                 return 2;
674         }
675
676         lua_pushboolean(L, 1);
677         return 1;
678 }
679
680 #ifdef WITH_MALLINFO
681 int lc_meminfo(lua_State* L) {
682         struct mallinfo info = mallinfo();
683         lua_newtable(L);
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");
700         return 1;
701 }
702 #endif
703
704 /* File handle extraction blatantly stolen from
705  * https://github.com/rrthomas/luaposix/blob/master/lposix.c#L631
706  * */
707
708 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
709 int lc_fallocate(lua_State* L) {
710         int ret;
711         off_t offset, len;
712         FILE* f = *(FILE**) luaL_checkudata(L, 1, LUA_FILEHANDLE);
713
714         if(f == NULL) {
715                 return luaL_error(L, "attempt to use a closed file");
716         }
717
718         offset = luaL_checkinteger(L, 2);
719         len = luaL_checkinteger(L, 3);
720
721 #if defined(__linux__) && defined(_GNU_SOURCE)
722         errno = 0;
723         ret = fallocate(fileno(f), FALLOC_FL_KEEP_SIZE, offset, len);
724
725         if(ret == 0) {
726                 lua_pushboolean(L, 1);
727                 return 1;
728         }
729
730         /* Some old versions of Linux apparently use the return value instead of errno */
731         if(errno == 0) {
732                 errno = ret;
733         }
734
735         if(errno != ENOSYS && errno != EOPNOTSUPP) {
736                 lua_pushnil(L);
737                 lua_pushstring(L, strerror(errno));
738                 return 2;
739         }
740
741 #else
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()
745 #endif
746
747         ret = posix_fallocate(fileno(f), offset, len);
748
749         if(ret == 0) {
750                 lua_pushboolean(L, 1);
751                 return 1;
752         } else {
753                 lua_pushnil(L);
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));
759                         return 3;
760                 }
761                 return 2;
762         }
763 }
764 #endif
765
766 /* Register functions */
767
768 int luaopen_util_pposix(lua_State* L) {
769         luaL_Reg exports[] = {
770                 { "abort", lc_abort },
771
772                 { "daemonize", lc_daemonize },
773
774                 { "syslog_open", lc_syslog_open },
775                 { "syslog_close", lc_syslog_close },
776                 { "syslog_log", lc_syslog_log },
777                 { "syslog_setminlevel", lc_syslog_setmask },
778
779                 { "getpid", lc_getpid },
780                 { "getuid", lc_getuid },
781                 { "getgid", lc_getgid },
782
783                 { "setuid", lc_setuid },
784                 { "setgid", lc_setgid },
785                 { "initgroups", lc_initgroups },
786
787                 { "umask", lc_umask },
788
789                 { "mkdir", lc_mkdir },
790
791                 { "setrlimit", lc_setrlimit },
792                 { "getrlimit", lc_getrlimit },
793
794                 { "uname", lc_uname },
795
796                 { "setenv", lc_setenv },
797
798 #ifdef WITH_MALLINFO
799                 { "meminfo", lc_meminfo },
800 #endif
801
802 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
803                 { "fallocate", lc_fallocate },
804 #endif
805
806                 { NULL, NULL }
807         };
808
809         lua_newtable(L);
810         luaL_setfuncs(L, exports, 0);
811
812         lua_pushliteral(L, "pposix");
813         lua_setfield(L, -2, "_NAME");
814
815         lua_pushliteral(L, MODULE_VERSION);
816         lua_setfield(L, -2, "_VERSION");
817
818         return 1;
819 }