util.pposix: syslog(): Support an optional source parameter (producing messages of...
[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 = 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 int lc_setrlimit(lua_State *L) {
495         int arguments = lua_gettop(L);
496         int softlimit = -1;
497         int hardlimit = -1;
498         const char *resource = NULL;
499         int rid = -1;
500         if(arguments < 1 || arguments > 3) {
501                 lua_pushboolean(L, 0);
502                 lua_pushstring(L, "incorrect-arguments");
503         }
504
505         resource = luaL_checkstring(L, 1);
506         softlimit = luaL_checkinteger(L, 2);
507         hardlimit = luaL_checkinteger(L, 3);
508
509         rid = string2resource(resource);
510         if (rid != -1) {
511                 struct rlimit lim;
512                 struct rlimit lim_current;
513
514                 if (softlimit < 0 || hardlimit < 0) {
515                         if (getrlimit(rid, &lim_current)) {
516                                 lua_pushboolean(L, 0);
517                                 lua_pushstring(L, "getrlimit-failed");
518                                 return 2;
519                         }
520                 }
521
522                 if (softlimit < 0) lim.rlim_cur = lim_current.rlim_cur;
523                         else lim.rlim_cur = softlimit;
524                 if (hardlimit < 0) lim.rlim_max = lim_current.rlim_max;
525                         else lim.rlim_max = hardlimit;
526
527                 if (setrlimit(rid, &lim)) {
528                         lua_pushboolean(L, 0);
529                         lua_pushstring(L, "setrlimit-failed");
530                         return 2;
531                 }
532         } else {
533                 /* Unsupported resoucrce. Sorry I'm pretty limited by POSIX standard. */
534                 lua_pushboolean(L, 0);
535                 lua_pushstring(L, "invalid-resource");
536                 return 2;
537         }
538         lua_pushboolean(L, 1);
539         return 1;
540 }
541
542 int lc_getrlimit(lua_State *L) {
543         int arguments = lua_gettop(L);
544         const char *resource = NULL;
545         int rid = -1;
546         struct rlimit lim;
547
548         if (arguments != 1) {
549                 lua_pushboolean(L, 0);
550                 lua_pushstring(L, "invalid-arguments");
551                 return 2;
552         }
553
554         resource = luaL_checkstring(L, 1);
555         rid = string2resource(resource);
556         if (rid != -1) {
557                 if (getrlimit(rid, &lim)) {
558                         lua_pushboolean(L, 0);
559                         lua_pushstring(L, "getrlimit-failed.");
560                         return 2;
561                 }
562         } else {
563                 /* Unsupported resoucrce. Sorry I'm pretty limited by POSIX standard. */
564                 lua_pushboolean(L, 0);
565                 lua_pushstring(L, "invalid-resource");
566                 return 2;
567         }
568         lua_pushboolean(L, 1);
569         lua_pushnumber(L, lim.rlim_cur);
570         lua_pushnumber(L, lim.rlim_max);
571         return 3;
572 }
573
574 int lc_abort(lua_State* L)
575 {
576         abort();
577         return 0;
578 }
579
580 int lc_uname(lua_State* L)
581 {
582         struct utsname uname_info;
583         if(uname(&uname_info) != 0)
584         {
585                 lua_pushnil(L);
586                 lua_pushstring(L, strerror(errno));
587                 return 2;
588         }
589         lua_newtable(L);
590         lua_pushstring(L, uname_info.sysname);
591         lua_setfield(L, -2, "sysname");
592         lua_pushstring(L, uname_info.nodename);
593         lua_setfield(L, -2, "nodename");
594         lua_pushstring(L, uname_info.release);
595         lua_setfield(L, -2, "release");
596         lua_pushstring(L, uname_info.version);
597         lua_setfield(L, -2, "version");
598         lua_pushstring(L, uname_info.machine);
599         lua_setfield(L, -2, "machine");
600         return 1;
601 }
602
603 int lc_setenv(lua_State* L)
604 {
605         const char *var = luaL_checkstring(L, 1);
606         const char *value;
607
608         /* If the second argument is nil or nothing, unset the var */
609         if(lua_isnoneornil(L, 2))
610         {
611                 if(unsetenv(var) != 0)
612                 {
613                         lua_pushnil(L);
614                         lua_pushstring(L, strerror(errno));
615                         return 2;
616                 }
617                 lua_pushboolean(L, 1);
618                 return 1;
619         }
620
621         value = luaL_checkstring(L, 2);
622
623         if(setenv(var, value, 1) != 0)
624         {
625                 lua_pushnil(L);
626                 lua_pushstring(L, strerror(errno));
627                 return 2;
628         }
629
630         lua_pushboolean(L, 1);
631         return 1;
632 }
633
634 #ifdef WITH_MALLINFO
635 int lc_meminfo(lua_State* L)
636 {
637         struct mallinfo info = mallinfo();
638         lua_newtable(L);
639         /* This is the total size of memory allocated with sbrk by malloc, in bytes. */
640         lua_pushinteger(L, info.arena);
641         lua_setfield(L, -2, "allocated");
642         /* This is the total size of memory allocated with mmap, in bytes. */
643         lua_pushinteger(L, info.hblkhd);
644         lua_setfield(L, -2, "allocated_mmap");
645         /* This is the total size of memory occupied by chunks handed out by malloc. */
646         lua_pushinteger(L, info.uordblks);
647         lua_setfield(L, -2, "used");
648         /* This is the total size of memory occupied by free (not in use) chunks. */
649         lua_pushinteger(L, info.fordblks);
650         lua_setfield(L, -2, "unused");
651         /* This is the size of the top-most releasable chunk that normally borders the
652            end of the heap (i.e., the high end of the virtual address space's data segment). */
653         lua_pushinteger(L, info.keepcost);
654         lua_setfield(L, -2, "returnable");
655         return 1;
656 }
657 #endif
658
659 /* File handle extraction blatantly stolen from
660  * https://github.com/rrthomas/luaposix/blob/master/lposix.c#L631
661  * */
662
663 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
664 int lc_fallocate(lua_State* L)
665 {
666         off_t offset, len;
667         FILE *f = *(FILE**) luaL_checkudata(L, 1, LUA_FILEHANDLE);
668
669         offset = luaL_checkinteger(L, 2);
670         len = luaL_checkinteger(L, 3);
671
672 #if defined(_GNU_SOURCE)
673         if(fallocate(fileno(f), FALLOC_FL_KEEP_SIZE, offset, len) == 0)
674         {
675                 lua_pushboolean(L, 1);
676                 return 1;
677         }
678
679         if(errno != ENOSYS && errno != EOPNOTSUPP)
680         {
681                 lua_pushnil(L);
682                 lua_pushstring(L, strerror(errno));
683                 return 2;
684         }
685 #else
686 #warning Only using posix_fallocate() fallback.
687 #warning Linux fallocate() is strongly recommended if available: recompile with -D_GNU_SOURCE
688 #warning Note that posix_fallocate() will still be used on filesystems that dont support fallocate()
689 #endif
690
691         if(posix_fallocate(fileno(f), offset, len) == 0)
692         {
693                 lua_pushboolean(L, 1);
694                 return 1;
695         }
696         else
697         {
698                 lua_pushnil(L);
699                 lua_pushstring(L, strerror(errno));
700                 /* posix_fallocate() can leave a bunch of NULs at the end, so we cut that
701                  * this assumes that offset == length of the file */
702                 ftruncate(fileno(f), offset);
703                 return 2;
704         }
705 }
706 #endif
707
708 /* Register functions */
709
710 int luaopen_util_pposix(lua_State *L)
711 {
712         luaL_Reg exports[] = {
713                 { "abort", lc_abort },
714
715                 { "daemonize", lc_daemonize },
716
717                 { "syslog_open", lc_syslog_open },
718                 { "syslog_close", lc_syslog_close },
719                 { "syslog_log", lc_syslog_log },
720                 { "syslog_setminlevel", lc_syslog_setmask },
721
722                 { "getpid", lc_getpid },
723                 { "getuid", lc_getuid },
724                 { "getgid", lc_getgid },
725
726                 { "setuid", lc_setuid },
727                 { "setgid", lc_setgid },
728                 { "initgroups", lc_initgroups },
729
730                 { "umask", lc_umask },
731
732                 { "mkdir", lc_mkdir },
733
734                 { "setrlimit", lc_setrlimit },
735                 { "getrlimit", lc_getrlimit },
736
737                 { "uname", lc_uname },
738
739                 { "setenv", lc_setenv },
740
741 #ifdef WITH_MALLINFO
742                 { "meminfo", lc_meminfo },
743 #endif
744
745 #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(_GNU_SOURCE)
746                 { "fallocate", lc_fallocate },
747 #endif
748
749                 { NULL, NULL }
750         };
751
752         luaL_register(L, "pposix",  exports);
753
754         lua_pushliteral(L, "pposix");
755         lua_setfield(L, -2, "_NAME");
756
757         lua_pushliteral(L, MODULE_VERSION);
758         lua_setfield(L, -2, "_VERSION");
759
760         return 1;
761 }