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