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
}