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