#include <stdlib.h>
#include <libgen.h>
#include <sys/resource.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#define ENSC_WRAPPERS_PREFIX "vlimit: "
+#define ENSC_WRAPPERS_UNISTD 1
+#define ENSC_WRAPPERS_VSERVER 1
+#include <wrappers.h>
+
+#define CMD_DIR 0x8000
+#define CMD_MISSINGOK 0x8001
+
+int wrapper_exit_code = 255;
#define NUMLIM(X) \
{ #X, required_argument, 0, 2048|X }
-#define RESLIM(RES,V) \
+#define OPT_RESLIM(RES,V) \
{ #RES, required_argument, 0, 2048|RLIMIT_##V }
static struct option const
CMDLINE_OPTIONS[] = {
- { "help", no_argument, 0, 'h' },
- { "version", no_argument, 0, 'v' },
- { "all", no_argument, 0, 'a' },
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'v' },
+ { "all", no_argument, 0, 'a' },
+ { "dir", required_argument, 0, CMD_DIR },
+ { "missingok", no_argument, 0, CMD_MISSINGOK },
NUMLIM( 0), NUMLIM( 1), NUMLIM( 2), NUMLIM( 3),
NUMLIM( 4), NUMLIM( 5), NUMLIM( 6), NUMLIM( 7),
NUMLIM( 8), NUMLIM( 9), NUMLIM(10), NUMLIM(11),
NUMLIM(20), NUMLIM(21), NUMLIM(22), NUMLIM(23),
NUMLIM(24), NUMLIM(25), NUMLIM(26), NUMLIM(27),
NUMLIM(28), NUMLIM(29), NUMLIM(30), NUMLIM(31),
- RESLIM(cpu, CPU),
- RESLIM(fsize, FSIZE),
- RESLIM(data, DATA),
- RESLIM(stack, STACK),
- RESLIM(core, CORE),
- RESLIM(rss, RSS),
- RESLIM(nproc, NPROC),
- RESLIM(nofile, NOFILE),
- RESLIM(memlock, MEMLOCK),
- RESLIM(as, AS),
- RESLIM(locks, LOCKS),
+ OPT_RESLIM(cpu, CPU),
+ OPT_RESLIM(fsize, FSIZE),
+ OPT_RESLIM(data, DATA),
+ OPT_RESLIM(stack, STACK),
+ OPT_RESLIM(core, CORE),
+ OPT_RESLIM(rss, RSS),
+ OPT_RESLIM(nproc, NPROC),
+ OPT_RESLIM(nofile, NOFILE),
+ OPT_RESLIM(memlock, MEMLOCK),
+ OPT_RESLIM(as, AS),
+ OPT_RESLIM(locks, LOCKS),
{ 0,0,0,0 }
};
WRITE_MSG(fd, "Usage: ");
WRITE_STR(fd, cmd);
WRITE_MSG(fd,
- " -c <xid> [-nd] [-a|--all] [[-MSH] --(<resource>|<nr>) <value>]*\n\n"
+ " [-c <xid>] [-nd] [-a|--all] [[-MSH] --(<resource>|<nr>) <value>]*\n"
+ " [--dir <pathname> [--missingok]] [--] [<program> <args>*]\n\n"
"Options:\n"
" -c <xid> ... operate on context <xid>\n"
" -a|--all ... show all available limits\n"
" -S ... set Soft limit\n"
" -H ... set Hard limit (assumed by default, when neither\n"
" M nor S was requested)\n"
+ " --dir <pathname>\n"
+ " ... read limits from <pathname>/; allowed filenames are\n"
+ " <resource> and <resource>.{min,soft,hard}. When a limit\n"
+ " was set by the CLI already, the corresponding file\n"
+ " will be ignored\n"
+ " --missingok ... do not fail when <pathname> does not exist\n"
" --<resource>|<nr> <value>\n"
" ... set specified (MSH) limit for <resource> to <value>\n\n"
"Valid values for resource are cpu, fsize, data, stack, core, rss, nproc,\n"
if (vc_get_rlimit_mask(ctx, &mask)==-1) {
perror("vc_get_rlimit_mask()");
- exit(1);
+ exit(wrapper_exit_code);
}
for (i=0; i<32; ++i) {
}
}
+static vc_limit_t
+readValue(int fd, char const *filename)
+{
+ char buf[128];
+ size_t len = Eread(fd, buf, sizeof(buf)-1);
+ vc_limit_t res;
+
+ buf[len] = '\0';
+
+ if (!vc_parseLimit(buf, &res)) {
+ WRITE_MSG(2, "Invalid limit in '");
+ WRITE_STR(2, filename);
+ WRITE_STR(2, "'\n");
+ exit(wrapper_exit_code);
+ }
+
+ return res;
+}
+
+static bool
+readFile(char const *file, char *base, char const *suffix,
+ vc_limit_t *limit)
+{
+ int fd;
+
+ memcpy(base, suffix, strlen(suffix)+1);
+ fd = open(file, O_RDONLY);
+ if (fd!=-1) {
+ *limit = readValue(fd, file);
+ Eclose(fd);
+ }
+
+ return fd!=-1;
+}
+
+static void
+readFromDir(struct vc_rlimit limits[32], uint_least32_t *mask,
+ char const *pathname, bool missing_ok)
+{
+ struct stat st;
+ size_t i;
+ size_t l_pathname = strlen(pathname);
+ char buf[l_pathname + sizeof("/memlock.hard") + 32];
+
+ if (stat(pathname, &st)==-1) {
+ if (errno==ENOENT && missing_ok) return;
+ PERROR_Q("vlimit: fstat", pathname);
+ exit(wrapper_exit_code);
+ }
+
+ memcpy(buf, pathname, l_pathname);
+ if (l_pathname>0 && pathname[l_pathname-1]!='/')
+ buf[l_pathname++] = '/';
+
+ for (i=0; i<DIM_OF(LIMIT_STR); ++i) {
+ size_t l_res;
+ char * ptr = buf+l_pathname;
+
+ // ignore unimplemented limits
+ if (LIMIT_STR[i]==0) continue;
+
+ // ignore limits set on cli already
+ if (*mask & (1<<i)) continue;
+
+ l_res = strlen(LIMIT_STR[i]);
+ memcpy(ptr, LIMIT_STR[i], l_res+1);
+ while (*ptr) {
+ *ptr = tolower(*ptr);
+ ++ptr;
+ }
+
+ if (readFile(buf, ptr, "", &limits[i].min)) {
+ limits[i].soft = limits[i].hard = limits[i].min;
+ *mask |= (1<<i);
+ }
+
+ if (readFile(buf, ptr, ".min", &limits[i].min))
+ *mask |= (1<<i);
+
+ if (readFile(buf, ptr, ".soft", &limits[i].soft))
+ *mask |= (1<<i);
+
+ if (readFile(buf, ptr, ".hard", &limits[i].hard))
+ *mask |= (1<<i);
+ }
+}
+
int main (int argc, char *argv[])
{
// overall used limits
uint32_t lim_mask = 0;
int set_mask = 0;
struct vc_rlimit limits[32];
- bool show_all = false;
- xid_t ctx = VC_NOCTX;
+ bool show_all = false;
+ xid_t ctx = VC_NOCTX;
+ char const * dir = 0;
+ bool missing_ok = false;
{
size_t i;
int id = c-2048;
vc_limit_t val;
- if (strcmp(optarg, "inf")==0) val = VC_LIM_INFINITY;
- else val = atoll(optarg);
+ if (!vc_parseLimit(optarg, &val)) {
+ WRITE_MSG(2, "Can not parse limit '");
+ WRITE_STR(2, optarg);
+ WRITE_STR(2, "'\n");
+ exit(wrapper_exit_code);
+ }
if (set_mask==0) set_mask=4;
case 'a' : show_all = true; break;
case 'n' : do_resolve = false; break;
case 'd' : fmt_func = utilvserver_fmt_uint64; break;
+ case CMD_DIR : dir = optarg; break;
+ case CMD_MISSINGOK: missing_ok = true; break;
case 'M' :
case 'S' :
case 'H' :
WRITE_MSG(2, "Try '");
WRITE_STR(2, argv[0]);
WRITE_MSG(2, " --help\" for more information.\n");
- return EXIT_FAILURE;
+ exit(wrapper_exit_code) ;
break;
}
}
- if (ctx==VC_NOCTX) {
- WRITE_MSG(2, "No context specified; try '--help' for more information\n");
- return EXIT_FAILURE;
- }
+ if (ctx==VC_NOCTX)
+ ctx = Evc_get_task_xid(0);
+ readFromDir(limits, &lim_mask, dir, missing_ok);
setLimits(ctx, limits, lim_mask);
if (show_all) showAll(ctx);
+ if (optind<argc)
+ EexecvpD(argv[optind], argv+optind);
+
return EXIT_SUCCESS;
}