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