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