aboutsummaryrefslogtreecommitdiffstats
path: root/res/res_limit.c
blob: b1cf025b0c77e80dce4b095e2b8cad76a851859e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * Asterisk -- A telephony toolkit for Linux.
 *
 * Resource limits
 * 
 * Copyright (c) 2006 Tilghman Lesher.  All rights reserved.
 *
 * Tilghman Lesher <res_limit_200607@the-tilghman.com>
 *
 * This code is released by the author with no restrictions on usage.
 *
 */

/*! \file
 *
 * \brief Resource limits
 *
 * \author Tilghman Lesher <res_limit_200607@the-tilghman.com>
 */


#include "asterisk.h"

ASTERISK_FILE_VERSION(__FILE__, "$Revision$")

#define _XOPEN_SOURCE 600

#include <ctype.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "asterisk/module.h"
#include "asterisk/cli.h"

/* Find proper rlimit for virtual memory */
#ifdef RLIMIT_AS
#define VMEM_DEF RLIMIT_AS
#else
#ifdef RLIMIT_VMEM
#define VMEM_DEF RLIMIT_VMEM
#endif
#endif

static struct limits {
	int resource;
	char limit[3];
	char desc[40];
} limits[] = {
	{ RLIMIT_CPU,     "-t", "cpu time" },
	{ RLIMIT_FSIZE,   "-f", "file size" },
	{ RLIMIT_DATA,    "-d", "program data segment" },
	{ RLIMIT_STACK,   "-s", "program stack size" },
	{ RLIMIT_CORE,    "-c", "core file size" },
#ifdef RLIMIT_RSS
	{ RLIMIT_RSS,     "-m", "resident memory" },
	{ RLIMIT_MEMLOCK, "-l", "amount of memory locked into RAM" },
#endif
#ifdef RLIMIT_NPROC
	{ RLIMIT_NPROC,   "-u", "number of processes" },
#endif
	{ RLIMIT_NOFILE,  "-n", "number of file descriptors" },
#ifdef VMEM_DEF
	{ VMEM_DEF,       "-v", "virtual memory" },
#endif
};

static int str2limit(const char *string)
{
	size_t i;
	for (i = 0; i < ARRAY_LEN(limits); i++) {
		if (!strcasecmp(string, limits[i].limit))
			return limits[i].resource;
	}
	return -1;
}

static const char *str2desc(const char *string)
{
	size_t i;
	for (i = 0; i < ARRAY_LEN(limits); i++) {
		if (!strcmp(string, limits[i].limit))
			return limits[i].desc;
	}
	return "<unknown>";
}

static char *complete_ulimit(struct ast_cli_args *a)
{
	int which = 0, i;
	int wordlen = strlen(a->word);

	if (a->pos > 1)
		return NULL;
	for (i = 0; i < ARRAY_LEN(limits); i++) {
		if (!strncasecmp(limits[i].limit, a->word, wordlen)) {
			if (++which > a->n)
				return ast_strdup(limits[i].limit);
		}
	}
	return NULL;
}

static char *handle_cli_ulimit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	int resource;
	struct rlimit rlimit = { 0, 0 };

	switch (cmd) {
	case CLI_INIT:
		e->command = "ulimit";
		e->usage =
			"Usage: ulimit {-d|"
#ifdef RLIMIT_RSS
			"-l|"
#endif
			"-f|"
#ifdef RLIMIT_RSS
			"-m|"
#endif
			"-s|-t|"
#ifdef RLIMIT_NPROC
			"-u|"
#endif
#ifdef VMEM_DEF
			"-v|"
#endif
			"-c|-n} [<num>]\n"
			"       Shows or sets the corresponding resource limit.\n"
			"         -d  Process data segment [readonly]\n"
#ifdef RLIMIT_RSS
			"         -l  Memory lock size [readonly]\n"
#endif
			"         -f  File size\n"
#ifdef RLIMIT_RSS
			"         -m  Process resident memory [readonly]\n"
#endif
			"         -s  Process stack size [readonly]\n"
			"         -t  CPU usage [readonly]\n"
#ifdef RLIMIT_NPROC
			"         -u  Child processes\n"
#endif
#ifdef VMEM_DEF
			"         -v  Process virtual memory [readonly]\n"
#endif
			"         -c  Core dump file size\n"
			"         -n  Number of file descriptors\n";
		return NULL;
	case CLI_GENERATE:
		return complete_ulimit(a);
	}

	if (a->argc > 3)
		return CLI_SHOWUSAGE;

	if (a->argc == 1) {
		char arg2[3];
		char *newargv[2] = { "ulimit", arg2 };
		for (resource = 0; resource < ARRAY_LEN(limits); resource++) {
			struct ast_cli_args newArgs = { .argv = newargv, .argc = 2 };
			ast_copy_string(arg2, limits[resource].limit, sizeof(arg2));
			handle_cli_ulimit(e, CLI_HANDLER, &newArgs);
		}
		return CLI_SUCCESS;
	} else {
		resource = str2limit(a->argv[1]);
		if (resource == -1) {
			ast_cli(a->fd, "Unknown resource\n");
			return CLI_FAILURE;
		}

		if (a->argc == 3) {
			int x;
#ifdef RLIMIT_NPROC
			if (resource != RLIMIT_NOFILE && resource != RLIMIT_CORE && resource != RLIMIT_NPROC && resource != RLIMIT_FSIZE) {
#else
			if (resource != RLIMIT_NOFILE && resource != RLIMIT_CORE && resource != RLIMIT_FSIZE) {
#endif
				ast_cli(a->fd, "Resource not permitted to be set\n");
				return CLI_FAILURE;
			}

			sscanf(a->argv[2], "%d", &x);
			rlimit.rlim_max = rlimit.rlim_cur = x;
			setrlimit(resource, &rlimit);
			return CLI_SUCCESS;
		} else {
			if (!getrlimit(resource, &rlimit)) {
				char printlimit[32];
				const char *desc;
				if (rlimit.rlim_max == RLIM_INFINITY)
					ast_copy_string(printlimit, "effectively unlimited", sizeof(printlimit));
				else
					snprintf(printlimit, sizeof(printlimit), "limited to %d", (int) rlimit.rlim_cur);
				desc = str2desc(a->argv[1]);
				ast_cli(a->fd, "%c%s (%s) is %s.\n", toupper(desc[0]), desc + 1, a->argv[1], printlimit);
			} else
				ast_cli(a->fd, "Could not retrieve resource limits for %s: %s\n", str2desc(a->argv[1]), strerror(errno));
			return CLI_SUCCESS;
		}
	}
}

static struct ast_cli_entry cli_ulimit =
	AST_CLI_DEFINE(handle_cli_ulimit, "Set or show process resource limits");

static int unload_module(void)
{
	return ast_cli_unregister(&cli_ulimit);
}

static int load_module(void)
{
	return ast_cli_register(&cli_ulimit) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;
}

AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Resource limits");