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