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