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