Merge 0.7->trunk
[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.3"
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_umask(lua_State* L)
363 {
364         char old_mode_string[7];
365         mode_t old_mode = umask(strtoul(luaL_checkstring(L, 1), NULL, 8));
366
367         snprintf(old_mode_string, sizeof(old_mode_string), "%03o", old_mode);
368         old_mode_string[sizeof(old_mode_string)-1] = 0;
369         lua_pushstring(L, old_mode_string);
370
371         return 1;
372 }
373
374 int lc_mkdir(lua_State* L)
375 {
376         int ret = mkdir(luaL_checkstring(L, 1), S_IRUSR | S_IWUSR | S_IXUSR
377                 | S_IRGRP | S_IWGRP | S_IXGRP
378                 | S_IROTH | S_IXOTH); /* mode 775 */
379
380         lua_pushboolean(L, ret==0);
381         if(ret)
382         {
383                 lua_pushstring(L, strerror(errno));
384                 return 2;
385         }
386         return 1;
387 }
388
389 /*      Like POSIX's setrlimit()/getrlimit() API functions.
390  *
391  *      Syntax:
392  *      pposix.setrlimit( resource, soft limit, hard limit)
393  *
394  *      Any negative limit will be replace with the current limit by an additional call of getrlimit().
395  *
396  *      Example usage:
397  *      pposix.setrlimit("NOFILE", 1000, 2000)
398  */
399 int string2resource(const char *s) {
400         if (!strcmp(s, "CORE")) return RLIMIT_CORE;
401         if (!strcmp(s, "CPU")) return RLIMIT_CPU;
402         if (!strcmp(s, "DATA")) return RLIMIT_DATA;
403         if (!strcmp(s, "FSIZE")) return RLIMIT_FSIZE;
404         if (!strcmp(s, "NOFILE")) return RLIMIT_NOFILE;
405         if (!strcmp(s, "STACK")) return RLIMIT_STACK;
406 #if !(defined(sun) || defined(__sun))
407         if (!strcmp(s, "MEMLOCK")) return RLIMIT_MEMLOCK;
408         if (!strcmp(s, "NPROC")) return RLIMIT_NPROC;
409         if (!strcmp(s, "RSS")) return RLIMIT_RSS;
410 #endif
411         return -1;
412 }
413
414 int lc_setrlimit(lua_State *L) {
415         int arguments = lua_gettop(L);
416         int softlimit = -1;
417         int hardlimit = -1;
418         const char *resource = NULL;
419         int rid = -1;
420         if(arguments < 1 || arguments > 3) {
421                 lua_pushboolean(L, 0);
422                 lua_pushstring(L, "incorrect-arguments");
423         }
424
425         resource = luaL_checkstring(L, 1);
426         softlimit = luaL_checkinteger(L, 2);
427         hardlimit = luaL_checkinteger(L, 3);
428
429         rid = string2resource(resource);
430         if (rid != -1) {
431                 struct rlimit lim;
432                 struct rlimit lim_current;
433
434                 if (softlimit < 0 || hardlimit < 0) {
435                         if (getrlimit(rid, &lim_current)) {
436                                 lua_pushboolean(L, 0);
437                                 lua_pushstring(L, "getrlimit-failed");
438                                 return 2;
439                         }
440                 }
441
442                 if (softlimit < 0) lim.rlim_cur = lim_current.rlim_cur;
443                         else lim.rlim_cur = softlimit;
444                 if (hardlimit < 0) lim.rlim_max = lim_current.rlim_max;
445                         else lim.rlim_max = hardlimit;
446
447                 if (setrlimit(rid, &lim)) {
448                         lua_pushboolean(L, 0);
449                         lua_pushstring(L, "setrlimit-failed");
450                         return 2;
451                 }
452         } else {
453                 /* Unsupported resoucrce. Sorry I'm pretty limited by POSIX standard. */
454                 lua_pushboolean(L, 0);
455                 lua_pushstring(L, "invalid-resource");
456                 return 2;
457         }
458         lua_pushboolean(L, 1);
459         return 1;
460 }
461
462 int lc_getrlimit(lua_State *L) {
463         int arguments = lua_gettop(L);
464         const char *resource = NULL;
465         int rid = -1;
466         struct rlimit lim;
467
468         if (arguments != 1) {
469                 lua_pushboolean(L, 0);
470                 lua_pushstring(L, "invalid-arguments");
471                 return 2;
472         }
473
474         resource = luaL_checkstring(L, 1);
475         rid = string2resource(resource);
476         if (rid != -1) {
477                 if (getrlimit(rid, &lim)) {
478                         lua_pushboolean(L, 0);
479                         lua_pushstring(L, "getrlimit-failed.");
480                         return 2;
481                 }
482         } else {
483                 /* Unsupported resoucrce. Sorry I'm pretty limited by POSIX standard. */
484                 lua_pushboolean(L, 0);
485                 lua_pushstring(L, "invalid-resource");
486                 return 2;
487         }
488         lua_pushboolean(L, 1);
489         lua_pushnumber(L, lim.rlim_cur);
490         lua_pushnumber(L, lim.rlim_max);
491         return 3;
492 }
493
494 int lc_abort(lua_State* L)
495 {
496         abort();
497         return 0;
498 }
499
500 /* Register functions */
501
502 int luaopen_util_pposix(lua_State *L)
503 {
504         luaL_Reg exports[] = {
505                 { "abort", lc_abort },
506
507                 { "daemonize", lc_daemonize },
508
509                 { "syslog_open", lc_syslog_open },
510                 { "syslog_close", lc_syslog_close },
511                 { "syslog_log", lc_syslog_log },
512                 { "syslog_setminlevel", lc_syslog_setmask },
513
514                 { "getpid", lc_getpid },
515                 { "getuid", lc_getuid },
516                 { "getgid", lc_getgid },
517
518                 { "setuid", lc_setuid },
519                 { "setgid", lc_setgid },
520
521                 { "umask", lc_umask },
522
523                 { "mkdir", lc_mkdir },
524
525                 { "setrlimit", lc_setrlimit },
526                 { "getrlimit", lc_getrlimit },
527
528                 { NULL, NULL }
529         };
530
531         luaL_register(L, "pposix",  exports);
532
533         lua_pushliteral(L, "pposix");
534         lua_setfield(L, -2, "_NAME");
535
536         lua_pushliteral(L, MODULE_VERSION);
537         lua_setfield(L, -2, "_VERSION");
538
539         return 1;
540 };