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