util.pposix: Allow fetching RLIMIT_NICE when available
[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.5"
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(_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 = luaL_checkoption(L, 1, "notice", level_strings);
208         level = level_constants[level];
209
210         luaL_checkstring(L, 2);
211
212         syslog(level, "%s", lua_tostring(L, 2));
213         return 0;
214 }
215
216 int lc_syslog_close(lua_State* L)
217 {
218         closelog();
219         if(syslog_ident)
220         {
221                 free(syslog_ident);
222                 syslog_ident = NULL;
223         }
224         return 0;
225 }
226
227 int lc_syslog_setmask(lua_State* L)
228 {
229         int level_idx = luaL_checkoption(L, 1, "notice", level_strings);
230         int mask = 0;
231         do
232         {
233                 mask |= LOG_MASK(level_constants[level_idx]);
234         } while (++level_idx<=4);
235
236         setlogmask(mask);
237         return 0;
238 }
239
240 /* getpid */
241
242 int lc_getpid(lua_State* L)
243 {
244         lua_pushinteger(L, getpid());
245         return 1;
246 }
247
248 /* UID/GID functions */
249
250 int lc_getuid(lua_State* L)
251 {
252         lua_pushinteger(L, getuid());
253         return 1;
254 }
255
256 int lc_getgid(lua_State* L)
257 {
258         lua_pushinteger(L, getgid());
259         return 1;
260 }
261
262 int lc_setuid(lua_State* L)
263 {
264         int uid = -1;
265         if(lua_gettop(L) < 1)
266                 return 0;
267         if(!lua_isnumber(L, 1) && lua_tostring(L, 1))
268         {
269                 /* Passed UID is actually a string, so look up the UID */
270                 struct passwd *p;
271                 p = getpwnam(lua_tostring(L, 1));
272                 if(!p)
273                 {
274                         lua_pushboolean(L, 0);
275                         lua_pushstring(L, "no-such-user");
276                         return 2;
277                 }
278                 uid = p->pw_uid;
279         }
280         else
281         {
282                 uid = lua_tonumber(L, 1);
283         }
284
285         if(uid>-1)
286         {
287                 /* Ok, attempt setuid */
288                 errno = 0;
289                 if(setuid(uid))
290                 {
291                         /* Fail */
292                         lua_pushboolean(L, 0);
293                         switch(errno)
294                         {
295                         case EINVAL:
296                                 lua_pushstring(L, "invalid-uid");
297                                 break;
298                         case EPERM:
299                                 lua_pushstring(L, "permission-denied");
300                                 break;
301                         default:
302                                 lua_pushstring(L, "unknown-error");
303                         }
304                         return 2;
305                 }
306                 else
307                 {
308                         /* Success! */
309                         lua_pushboolean(L, 1);
310                         return 1;
311                 }
312         }
313
314         /* Seems we couldn't find a valid UID to switch to */
315         lua_pushboolean(L, 0);
316         lua_pushstring(L, "invalid-uid");
317         return 2;
318 }
319
320 int lc_setgid(lua_State* L)
321 {
322         int gid = -1;
323         if(lua_gettop(L) < 1)
324                 return 0;
325         if(!lua_isnumber(L, 1) && lua_tostring(L, 1))
326         {
327                 /* Passed GID is actually a string, so look up the GID */
328                 struct group *g;
329                 g = getgrnam(lua_tostring(L, 1));
330                 if(!g)
331                 {
332                         lua_pushboolean(L, 0);
333                         lua_pushstring(L, "no-such-group");
334                         return 2;
335                 }
336                 gid = g->gr_gid;
337         }
338         else
339         {
340                 gid = lua_tonumber(L, 1);
341         }
342
343         if(gid>-1)
344         {
345                 /* Ok, attempt setgid */
346                 errno = 0;
347                 if(setgid(gid))
348                 {
349                         /* Fail */
350                         lua_pushboolean(L, 0);
351                         switch(errno)
352                         {
353                         case EINVAL:
354                                 lua_pushstring(L, "invalid-gid");
355                                 break;
356                         case EPERM:
357                                 lua_pushstring(L, "permission-denied");
358                                 break;
359                         default:
360                                 lua_pushstring(L, "unknown-error");
361                         }
362                         return 2;
363                 }
364                 else
365                 {
366                         /* Success! */
367                         lua_pushboolean(L, 1);
368                         return 1;
369                 }
370         }
371
372         /* Seems we couldn't find a valid GID to switch to */
373         lua_pushboolean(L, 0);
374         lua_pushstring(L, "invalid-gid");
375         return 2;
376 }
377
378 int lc_initgroups(lua_State* L)
379 {
380         int ret;
381         gid_t gid;
382         struct passwd *p;
383
384         if(!lua_isstring(L, 1))
385         {
386                 lua_pushnil(L);
387                 lua_pushstring(L, "invalid-username");
388                 return 2;
389         }
390         p = getpwnam(lua_tostring(L, 1));
391         if(!p)
392         {
393                 lua_pushnil(L);
394                 lua_pushstring(L, "no-such-user");
395                 return 2;
396         }
397         if(lua_gettop(L) < 2)
398                 lua_pushnil(L);
399         switch(lua_type(L, 2))
400         {
401         case LUA_TNIL:
402                 gid = p->pw_gid;
403                 break;
404         case LUA_TNUMBER:
405                 gid = lua_tointeger(L, 2);
406                 break;
407         default:
408                 lua_pushnil(L);
409                 lua_pushstring(L, "invalid-gid");
410                 return 2;
411         }
412         ret = initgroups(lua_tostring(L, 1), gid);
413         if(ret)
414         {
415                 switch(errno)
416                 {
417                 case ENOMEM:
418                         lua_pushnil(L);
419                         lua_pushstring(L, "no-memory");
420                         break;
421                 case EPERM:
422                         lua_pushnil(L);
423                         lua_pushstring(L, "permission-denied");
424                         break;
425                 default:
426                         lua_pushnil(L);
427                         lua_pushstring(L, "unknown-error");
428                 }
429         }
430         else
431         {
432                 lua_pushboolean(L, 1);
433                 lua_pushnil(L);
434         }
435         return 2;
436 }
437
438 int lc_umask(lua_State* L)
439 {
440         char old_mode_string[7];
441         mode_t old_mode = umask(strtoul(luaL_checkstring(L, 1), NULL, 8));
442
443         snprintf(old_mode_string, sizeof(old_mode_string), "%03o", old_mode);
444         old_mode_string[sizeof(old_mode_string)-1] = 0;
445         lua_pushstring(L, old_mode_string);
446
447         return 1;
448 }
449
450 int lc_mkdir(lua_State* L)
451 {
452         int ret = mkdir(luaL_checkstring(L, 1), S_IRUSR | S_IWUSR | S_IXUSR
453                 | S_IRGRP | S_IWGRP | S_IXGRP
454                 | S_IROTH | S_IXOTH); /* mode 775 */
455
456         lua_pushboolean(L, ret==0);
457         if(ret)
458         {
459                 lua_pushstring(L, strerror(errno));
460                 return 2;
461         }
462         return 1;
463 }
464
465 /*      Like POSIX's setrlimit()/getrlimit() API functions.
466  *
467  *      Syntax:
468  *      pposix.setrlimit( resource, soft limit, hard limit)
469  *
470  *      Any negative limit will be replace with the current limit by an additional call of getrlimit().
471  *
472  *      Example usage:
473  *      pposix.setrlimit("NOFILE", 1000, 2000)
474  */
475 int string2resource(const char *s) {
476         if (!strcmp(s, "CORE")) return RLIMIT_CORE;
477         if (!strcmp(s, "CPU")) return RLIMIT_CPU;
478         if (!strcmp(s, "DATA")) return RLIMIT_DATA;
479         if (!strcmp(s, "FSIZE")) return RLIMIT_FSIZE;
480         if (!strcmp(s, "NOFILE")) return RLIMIT_NOFILE;
481         if (!strcmp(s, "STACK")) return RLIMIT_STACK;
482 #if !(defined(sun) || defined(__sun))
483         if (!strcmp(s, "MEMLOCK")) return RLIMIT_MEMLOCK;
484         if (!strcmp(s, "NPROC")) return RLIMIT_NPROC;
485         if (!strcmp(s, "RSS")) return RLIMIT_RSS;
486 #endif
487 #ifdef RLIMIT_NICE
488         if (!strcmp(s, "NICE")) return RLIMIT_NICE;
489 #endif
490         return -1;
491 }
492
493 int lc_setrlimit(lua_State *L) {
494         int arguments = lua_gettop(L);
495         int softlimit = -1;
496         int hardlimit = -1;
497         const char *resource = NULL;
498         int rid = -1;
499         if(arguments < 1 || arguments > 3) {
500                 lua_pushboolean(L, 0);
501                 lua_pushstring(L, "incorrect-arguments");
502         }
503
504         resource = luaL_checkstring(L, 1);
505         softlimit = luaL_checkinteger(L, 2);
506         hardlimit = luaL_checkinteger(L, 3);
507
508         rid = string2resource(resource);
509         if (rid != -1) {
510                 struct rlimit lim;
511                 struct rlimit lim_current;
512
513                 if (softlimit < 0 || hardlimit < 0) {
514                         if (getrlimit(rid, &lim_current)) {
515                                 lua_pushboolean(L, 0);
516                                 lua_pushstring(L, "getrlimit-failed");
517                                 return 2;
518                         }
519                 }
520
521                 if (softlimit < 0) lim.rlim_cur = lim_current.rlim_cur;
522                         else lim.rlim_cur = softlimit;
523                 if (hardlimit < 0) lim.rlim_max = lim_current.rlim_max;
524                         else lim.rlim_max = hardlimit;
525
526                 if (setrlimit(rid, &lim)) {
527                         lua_pushboolean(L, 0);
528                         lua_pushstring(L, "setrlimit-failed");
529                         return 2;
530                 }
531         } else {
532                 /* Unsupported resoucrce. Sorry I'm pretty limited by POSIX standard. */
533                 lua_pushboolean(L, 0);
534                 lua_pushstring(L, "invalid-resource");
535                 return 2;
536         }
537         lua_pushboolean(L, 1);
538         return 1;
539 }
540
541 int lc_getrlimit(lua_State *L) {
542         int arguments = lua_gettop(L);
543         const char *resource = NULL;
544         int rid = -1;
545         struct rlimit lim;
546
547         if (arguments != 1) {
548                 lua_pushboolean(L, 0);
549                 lua_pushstring(L, "invalid-arguments");
550                 return 2;
551         }
552
553         resource = luaL_checkstring(L, 1);
554         rid = string2resource(resource);
555         if (rid != -1) {
556                 if (getrlimit(rid, &lim)) {
557                         lua_pushboolean(L, 0);
558                         lua_pushstring(L, "getrlimit-failed.");
559                         return 2;
560                 }
561         } else {
562                 /* Unsupported resoucrce. Sorry I'm pretty limited by POSIX standard. */
563                 lua_pushboolean(L, 0);
564                 lua_pushstring(L, "invalid-resource");
565                 return 2;
566         }
567         lua_pushboolean(L, 1);
568         lua_pushnumber(L, lim.rlim_cur);
569         lua_pushnumber(L, lim.rlim_max);
570         return 3;
571 }
572
573 int lc_abort(lua_State* L)
574 {
575         abort();
576         return 0;
577 }
578
579 int lc_uname(lua_State* L)
580 {
581         struct utsname uname_info;
582         if(uname(&uname_info) != 0)
583         {
584                 lua_pushnil(L);
585                 lua_pushstring(L, strerror(errno));
586                 return 2;
587         }
588         lua_newtable(L);
589         lua_pushstring(L, uname_info.sysname);
590         lua_setfield(L, -2, "sysname");
591         lua_pushstring(L, uname_info.nodename);
592         lua_setfield(L, -2, "nodename");
593         lua_pushstring(L, uname_info.release);
594         lua_setfield(L, -2, "release");
595         lua_pushstring(L, uname_info.version);
596         lua_setfield(L, -2, "version");
597         lua_pushstring(L, uname_info.machine);
598         lua_setfield(L, -2, "machine");
599         return 1;
600 }
601
602 int lc_setenv(lua_State* L)
603 {
604         const char *var = luaL_checkstring(L, 1);
605         const char *value;
606
607         /* If the second argument is nil or nothing, unset the var */
608         if(lua_isnoneornil(L, 2))
609         {
610                 if(unsetenv(var) != 0)
611                 {
612                         lua_pushnil(L);
613                         lua_pushstring(L, strerror(errno));
614                         return 2;
615                 }
616                 lua_pushboolean(L, 1);
617                 return 1;
618         }
619
620         value = luaL_checkstring(L, 2);
621
622         if(setenv(var, value, 1) != 0)
623         {
624                 lua_pushnil(L);
625                 lua_pushstring(L, strerror(errno));
626                 return 2;
627         }
628
629         lua_pushboolean(L, 1);
630         return 1;
631 }
632
633 #ifdef WITH_MALLINFO
634 int lc_meminfo(lua_State* L)
635 {
636         struct mallinfo info = mallinfo();
637         lua_newtable(L);
638         /* This is the total size of memory allocated with sbrk by malloc, in bytes. */
639         lua_pushinteger(L, info.arena);
640         lua_setfield(L, -2, "allocated");
641         /* This is the total size of memory allocated with mmap, in bytes. */
642         lua_pushinteger(L, info.hblkhd);
643         lua_setfield(L, -2, "allocated_mmap");
644         /* This is the total size of memory occupied by chunks handed out by malloc. */
645         lua_pushinteger(L, info.uordblks);
646         lua_setfield(L, -2, "used");
647         /* This is the total size of memory occupied by free (not in use) chunks. */
648         lua_pushinteger(L, info.fordblks);
649         lua_setfield(L, -2, "unused");
650         /* This is the size of the top-most releasable chunk that normally borders the
651            end of the heap (i.e., the high end of the virtual address space's data segment). */
652         lua_pushinteger(L, info.keepcost);
653         lua_setfield(L, -2, "returnable");
654         return 1;
655 }
656 #endif
657
658 /* File handle extraction blatantly stolen from
659  * https://github.com/rrthomas/luaposix/blob/master/lposix.c#L631
660  * */
661
662 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
663 int lc_fallocate(lua_State* L)
664 {
665         off_t offset, len;
666         FILE *f = *(FILE**) luaL_checkudata(L, 1, LUA_FILEHANDLE);
667
668         offset = luaL_checkinteger(L, 2);
669         len = luaL_checkinteger(L, 3);
670
671 #if defined(_GNU_SOURCE)
672         if(fallocate(fileno(f), FALLOC_FL_KEEP_SIZE, offset, len) == 0)
673         {
674                 lua_pushboolean(L, 1);
675                 return 1;
676         }
677
678         if(errno != ENOSYS && errno != EOPNOTSUPP)
679         {
680                 lua_pushnil(L);
681                 lua_pushstring(L, strerror(errno));
682                 return 2;
683         }
684 #else
685 #warning Only using posix_fallocate() fallback.
686 #warning Linux fallocate() is strongly recommended if available: recompile with -D_GNU_SOURCE
687 #warning Note that posix_fallocate() will still be used on filesystems that dont support fallocate()
688 #endif
689
690         if(posix_fallocate(fileno(f), offset, len) == 0)
691         {
692                 lua_pushboolean(L, 1);
693                 return 1;
694         }
695         else
696         {
697                 lua_pushnil(L);
698                 lua_pushstring(L, strerror(errno));
699                 /* posix_fallocate() can leave a bunch of NULs at the end, so we cut that
700                  * this assumes that offset == length of the file */
701                 ftruncate(fileno(f), offset);
702                 return 2;
703         }
704 }
705 #endif
706
707 /* Register functions */
708
709 int luaopen_util_pposix(lua_State *L)
710 {
711         luaL_Reg exports[] = {
712                 { "abort", lc_abort },
713
714                 { "daemonize", lc_daemonize },
715
716                 { "syslog_open", lc_syslog_open },
717                 { "syslog_close", lc_syslog_close },
718                 { "syslog_log", lc_syslog_log },
719                 { "syslog_setminlevel", lc_syslog_setmask },
720
721                 { "getpid", lc_getpid },
722                 { "getuid", lc_getuid },
723                 { "getgid", lc_getgid },
724
725                 { "setuid", lc_setuid },
726                 { "setgid", lc_setgid },
727                 { "initgroups", lc_initgroups },
728
729                 { "umask", lc_umask },
730
731                 { "mkdir", lc_mkdir },
732
733                 { "setrlimit", lc_setrlimit },
734                 { "getrlimit", lc_getrlimit },
735
736                 { "uname", lc_uname },
737
738                 { "setenv", lc_setenv },
739
740 #ifdef WITH_MALLINFO
741                 { "meminfo", lc_meminfo },
742 #endif
743
744 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
745                 { "fallocate", lc_fallocate },
746 #endif
747
748                 { NULL, NULL }
749         };
750
751         luaL_register(L, "pposix",  exports);
752
753         lua_pushliteral(L, "pposix");
754         lua_setfield(L, -2, "_NAME");
755
756         lua_pushliteral(L, MODULE_VERSION);
757         lua_setfield(L, -2, "_VERSION");
758
759         return 1;
760 }