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