]> xenbits.xensource.com Git - libvirt.git/commitdiff
util: new virCommandSetMax(MemLock|Processes|Files)
authorLaine Stump <laine@laine.org>
Thu, 25 Apr 2013 16:10:10 +0000 (12:10 -0400)
committerLaine Stump <laine@laine.org>
Fri, 26 Apr 2013 14:23:46 +0000 (10:23 -0400)
This patch adds two sets of functions:

1) lower level virProcessSet*() functions that will immediately set
the RLIMIT_MEMLOCK. RLIMIT_NPROC, or RLIMIT_NOFILE of either the
current process (using setrlimit()) or any other process (using
prlimit()). "current process" is indicated by passing a 0 for pid.

2) functions for virCommand* that will setup a virCommand object to
set those limits at a later time just after it has forked a new
process, but before it execs the new program.

configure.ac has prlimit and setrlimit added to the list of functions
to check for, and the low level functions log an "unsupported" error)
on platforms that don't support those functions.

configure.ac
src/libvirt_private.syms
src/util/vircommand.c
src/util/vircommand.h
src/util/virprocess.c
src/util/virprocess.h

index 89dae3def33275b15297098433d8dbf985ad454b..23c24d21db4ed2eee98188c68f75fd1d96ad53c4 100644 (file)
@@ -194,7 +194,7 @@ dnl Availability of various common functions (non-fatal if missing),
 dnl and various less common threadsafe functions
 AC_CHECK_FUNCS_ONCE([cfmakeraw geteuid getgid getgrnam_r getmntent_r \
   getpwuid_r getuid initgroups kill mmap newlocale posix_fallocate \
-  posix_memalign regexec sched_getaffinity setns symlink])
+  posix_memalign prlimit regexec sched_getaffinity setns setrlimit symlink])
 
 dnl Availability of pthread functions (if missing, win32 threading is
 dnl assumed).  Because of $LIB_PTHREAD, we cannot use AC_CHECK_FUNCS_ONCE.
index 2a2c40eb3adfc9ef27df28ad65c8cb5e66849120..0bb6f5f3d5b998da8b1c16b06f886181b9b40ec8 100644 (file)
@@ -1185,6 +1185,9 @@ virCommandSetErrorFD;
 virCommandSetGID;
 virCommandSetInputBuffer;
 virCommandSetInputFD;
+virCommandSetMaxFiles;
+virCommandSetMaxMemLock;
+virCommandSetMaxProcesses;
 virCommandSetOutputBuffer;
 virCommandSetOutputFD;
 virCommandSetPidFile;
@@ -1668,6 +1671,9 @@ virProcessGetNamespaces;
 virProcessKill;
 virProcessKillPainfully;
 virProcessSetAffinity;
+virProcessSetMaxFiles;
+virProcessSetMaxMemLock;
+virProcessSetMaxProcesses;
 virProcessSetNamespaces;
 virProcessTranslateStatus;
 virProcessWait;
index ac56a638a89780d19ae56358398d7818bbdd040f..98521ec9ec7845a2c3ef146f73bb3b1534eecc09 100644 (file)
@@ -107,6 +107,10 @@ struct _virCommand {
     char *pidfile;
     bool reap;
 
+    unsigned long long maxMemLock;
+    unsigned int maxProcesses;
+    unsigned int maxFiles;
+
     uid_t uid;
     gid_t gid;
     unsigned long long capabilities;
@@ -598,6 +602,13 @@ virExec(virCommandPtr cmd)
         goto fork_error;
     }
 
+    if (virProcessSetMaxMemLock(0, cmd->maxMemLock) < 0)
+        goto fork_error;
+    if (virProcessSetMaxProcesses(0, cmd->maxProcesses) < 0)
+        goto fork_error;
+    if (virProcessSetMaxFiles(0, cmd->maxFiles) < 0)
+        goto fork_error;
+
     if (cmd->hook) {
         VIR_DEBUG("Run hook %p %p", cmd->hook, cmd->opaque);
         ret = cmd->hook(cmd->opaque);
@@ -958,6 +969,33 @@ virCommandSetUID(virCommandPtr cmd, uid_t uid)
     cmd->uid = uid;
 }
 
+void
+virCommandSetMaxMemLock(virCommandPtr cmd, unsigned long long bytes)
+{
+    if (!cmd || cmd->has_error)
+        return;
+
+    cmd->maxMemLock = bytes;
+}
+
+void
+virCommandSetMaxProcesses(virCommandPtr cmd, unsigned int procs)
+{
+    if (!cmd || cmd->has_error)
+        return;
+
+    cmd->maxProcesses = procs;
+}
+
+void
+virCommandSetMaxFiles(virCommandPtr cmd, unsigned int files)
+{
+    if (!cmd || cmd->has_error)
+        return;
+
+    cmd->maxFiles = files;
+}
+
 /**
  * virCommandClearCaps:
  * @cmd: the command to modify
index 6c13795b3ad706067df7e5e89101467e3c800b95..18568feb873db1e96cefa94757950005ac8afdb8 100644 (file)
@@ -65,6 +65,10 @@ void virCommandSetGID(virCommandPtr cmd, gid_t gid);
 
 void virCommandSetUID(virCommandPtr cmd, uid_t uid);
 
+void virCommandSetMaxMemLock(virCommandPtr cmd, unsigned long long bytes);
+void virCommandSetMaxProcesses(virCommandPtr cmd, unsigned int procs);
+void virCommandSetMaxFiles(virCommandPtr cmd, unsigned int files);
+
 void virCommandClearCaps(virCommandPtr cmd);
 
 void virCommandAllowCap(virCommandPtr cmd,
index a492bd11f77103b3bb5f37773010faf1fadadfb6..fb818059f8bb1cedf91bcf1eeb09bc742433caed 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * virprocess.c: interaction with processes
  *
- * Copyright (C) 2010-2012 Red Hat, Inc.
+ * Copyright (C) 2010-2013 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include <signal.h>
 #include <errno.h>
 #include <sys/wait.h>
+#if HAVE_SETRLIMIT
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
 #include <sched.h>
 
 #include "virprocess.h"
@@ -605,3 +609,149 @@ int virProcessSetNamespaces(size_t nfdlist ATTRIBUTE_UNUSED,
     return -1;
 }
 #endif /* ! HAVE_SETNS */
+
+#if HAVE_PRLIMIT
+static int
+virProcessPrLimit(pid_t pid, int resource, struct rlimit *rlim)
+{
+    return prlimit(pid, resource, rlim, NULL);
+}
+#else /* ! HAVE_PRLIMIT */
+static int
+virProcessPrLimit(pid_t pid ATTRIBUTE_UNUSED,
+                  int resource ATTRIBUTE_UNUSED,
+                  struct rlimit *rlim ATTRIBUTE_UNUSED)
+{
+    errno = ENOSYS;
+    return -1;
+}
+#endif /* ! HAVE_PRLIMIT */
+
+#if HAVE_SETRLIMIT && defined(RLIMIT_MEMLOCK)
+int
+virProcessSetMaxMemLock(pid_t pid, unsigned long long bytes)
+{
+    struct rlimit rlim;
+
+    if (bytes == 0)
+        return 0;
+
+    rlim.rlim_cur = rlim.rlim_max = bytes;
+    if (pid == 0) {
+        if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
+            virReportSystemError(errno,
+                                 _("cannot limit locked memory to %llu"),
+                                 bytes);
+            return -1;
+        }
+    } else {
+        if (virProcessPrLimit(pid, RLIMIT_MEMLOCK, &rlim) < 0) {
+            virReportSystemError(errno,
+                                 _("cannot limit locked memory "
+                                   "of process %lld to %llu"),
+                                 (long long int)pid, bytes);
+            return -1;
+        }
+    }
+    return 0;
+}
+#else /* ! (HAVE_SETRLIMIT && defined(RLIMIT_MEMLOCK)) */
+int
+virProcessSetMaxMemLock(pid_t pid ATTRIBUTE_UNUSED, unsigned long long bytes)
+{
+    if (bytes == 0)
+        return 0;
+
+    virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
+    return -1;
+}
+#endif /* ! (HAVE_SETRLIMIT && defined(RLIMIT_MEMLOCK)) */
+
+
+#if HAVE_SETRLIMIT && defined(RLIMIT_NPROC)
+int
+virProcessSetMaxProcesses(pid_t pid, unsigned int procs)
+{
+    struct rlimit rlim;
+
+    if (procs == 0)
+        return 0;
+
+    rlim.rlim_cur = rlim.rlim_max = procs;
+    if (pid == 0) {
+        if (setrlimit(RLIMIT_NPROC, &rlim) < 0) {
+            virReportSystemError(errno,
+                                 _("cannot limit number of subprocesses to %u"),
+                                 procs);
+            return -1;
+        }
+    } else {
+        if (virProcessPrLimit(pid, RLIMIT_NPROC, &rlim) < 0) {
+            virReportSystemError(errno,
+                                 _("cannot limit number of subprocesses "
+                                   "of process %lld to %u"),
+                                 (long long int)pid, procs);
+            return -1;
+        }
+    }
+    return 0;
+}
+#else /* ! (HAVE_SETRLIMIT && defined(RLIMIT_NPROC)) */
+int
+virProcessSetMaxProcesses(pid_t pid ATTRIBUTE_UNUSED, unsigned int procs)
+{
+    if (procs == 0)
+        return 0;
+
+    virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
+    return -1;
+}
+#endif /* ! (HAVE_SETRLIMIT && defined(RLIMIT_NPROC)) */
+
+#if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE)
+int
+virProcessSetMaxFiles(pid_t pid, unsigned int files)
+{
+    struct rlimit rlim;
+
+    if (files == 0)
+        return 0;
+
+   /* Max number of opened files is one greater than actual limit. See
+    * man setrlimit.
+    *
+    * NB: That indicates to me that we would want the following code
+    * to say "files - 1", but the original of this code in
+    * qemu_process.c also had files + 1, so this preserves current
+    * behavior.
+    */
+    rlim.rlim_cur = rlim.rlim_max = files + 1;
+    if (pid == 0) {
+        if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+            virReportSystemError(errno,
+                                 _("cannot limit number of open files to %u"),
+                                 files);
+            return -1;
+        }
+    } else {
+        if (virProcessPrLimit(pid, RLIMIT_NOFILE, &rlim) < 0) {
+            virReportSystemError(errno,
+                                 _("cannot limit number of open files "
+                                   "of process %lld to %u"),
+                                 (long long int)pid, files);
+            return -1;
+        }
+    }
+    return 0;
+}
+#else /* ! (HAVE_SETRLIMIT && defined(RLIMIT_NOFILE)) */
+int
+virProcessSetMaxFiles(pid_t pid ATTRIBUTE_UNUSED, unsigned int files)
+{
+    if (files == 0)
+        return 0;
+
+    virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
+    return -1;
+}
+#endif /* ! (HAVE_SETRLIMIT && defined(RLIMIT_NOFILE)) */
index 53475d3cc3ff3410d6c13f6c9cfe0dd427616ba0..5dea3349028d4c21ef9ca1b15e6a74e011ce0527 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * virprocess.h: interaction with processes
  *
- * Copyright (C) 2010-2012 Red Hat, Inc.
+ * Copyright (C) 2010-2013 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -54,4 +54,7 @@ int virProcessGetNamespaces(pid_t pid,
 int virProcessSetNamespaces(size_t nfdlist,
                             int *fdlist);
 
+int virProcessSetMaxMemLock(pid_t pid, unsigned long long bytes);
+int virProcessSetMaxProcesses(pid_t pid, unsigned int procs);
+int virProcessSetMaxFiles(pid_t pid, unsigned int files);
 #endif /* __VIR_PROCESS_H__ */