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