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