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