Patchwork [2,of,2,V4] osutil: implement setprocname to set process title for some platforms

login
register
mail settings
Submitter Jun Wu
Date Nov. 14, 2016, 4:35 p.m.
Message ID <695fa432ce250fdc40d5.1479141342@x1c>
Download mbox | patch
Permalink /patch/17564/
State Accepted
Headers show

Comments

Jun Wu - Nov. 14, 2016, 4:35 p.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1478898677 0
#      Fri Nov 11 21:11:17 2016 +0000
# Node ID 695fa432ce250fdc40d5871a0009907262fe7949
# Parent  98761d64eaaf67f3bdb99f3f80a57910e2624b78
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r 695fa432ce25
osutil: implement setprocname to set process title for some platforms

This patch adds a simple setprocname method to osutil. The operation is not
defined by any standard and is platform-specific, the current implementation
tries to cover some major platforms (ex. Linux, OS X, FreeBSD) that is
relatively easy to support. Other platforms (Windows [4], other BSDs, ...)
can be added in the future.

The current implementation supports two methods to change process title:
  a. setproctitle if available (works in FreeBSD).
  b. rewrite argv in place (works in Linux [1] and Mac OS X). [2] [3]

[1]: Linux has "prctl(PR_SET_NAME, ...)" but 1) it has 16-byte limit, which
is too small; 2) it is not quite equivalent to what we want - it changes
"/proc/self/comm", not "/proc/self/cmdline" - "comm" change won't show up
in "ps" output unless "-o comm" is used.

[2]: The implementation does not rewrite the **environ buffer like some
other implementations do, just to make the code simpler and safer. However,
this also means the buffer size we can rewrite is significantly shorter. If
we are really greedy and want the "environ" space, we can change the
implementation later.

[3]: It requires a CPython private API: Py_GetArgcArgv to get the original
argv. Unfortunately Python 3 makes a copy of argv and returns the wchar_t
version, so it is not supported for now. (if we really want to, we could
count backwards from "char **environ", given known argc and argv, not sure
if that's a good idea - probably not)

[4]: The feature is aimed to make it easier for forked command server
processes to show what they are doing. Since Windows does not support
fork(), despite it's a major platform, its support is not added in this
patch.
Yuya Nishihara - Nov. 15, 2016, 12:54 p.m.
On Mon, 14 Nov 2016 16:35:42 +0000, Jun Wu wrote:
> # HG changeset patch
> # User Jun Wu <quark@fb.com>
> # Date 1478898677 0
> #      Fri Nov 11 21:11:17 2016 +0000
> # Node ID 695fa432ce250fdc40d5871a0009907262fe7949
> # Parent  98761d64eaaf67f3bdb99f3f80a57910e2624b78
> # Available At https://bitbucket.org/quark-zju/hg-draft
> #              hg pull https://bitbucket.org/quark-zju/hg-draft -r 695fa432ce25
> osutil: implement setprocname to set process title for some platforms

Queued, thanks.

> +#ifndef SETPROCNAME_USE_NONE
> +static PyObject *setprocname(PyObject *self, PyObject *args)
> +{
> +	const char *name = NULL;
> +	if (!PyArg_ParseTuple(args, "s", &name))
> +		return NULL;
> +
> +#if defined(SETPROCNAME_USE_SETPROCTITLE)
> +	setproctitle("%s", name);
> +#elif defined(SETPROCNAME_USE_ARGVREWRITE)
> +	{
> +		static char *argvstart = NULL;
> +		static size_t argvsize = 0;
> +		if (argvstart == NULL) {
> +			int argc = 0, i;
> +			char **argv = NULL;
> +			extern void Py_GetArgcArgv(int *argc, char ***argv);
> +			Py_GetArgcArgv(&argc, &argv);
> +
> +			/* Check the memory we can use. Typically, argv[i] and
> +			 * argv[i + 1] are continuous. */
> +			char *argvend = argvstart = argv[0];

Moved the declaration of argvend next to argv.

Patch

diff --git a/mercurial/osutil.c b/mercurial/osutil.c
--- a/mercurial/osutil.c
+++ b/mercurial/osutil.c
@@ -728,4 +728,60 @@  bail:
 
 #endif /* CMSG_LEN */
+
+#if defined(HAVE_SETPROCTITLE)
+/* setproctitle is the first choice - available in FreeBSD */
+#define SETPROCNAME_USE_SETPROCTITLE
+#elif (defined(__linux__) || defined(__APPLE__)) && PY_MAJOR_VERSION == 2
+/* rewrite the argv buffer in place - works in Linux and OS X. Py_GetArgcArgv
+ * in Python 3 returns the copied wchar_t **argv, thus unsupported. */
+#define SETPROCNAME_USE_ARGVREWRITE
+#else
+#define SETPROCNAME_USE_NONE
+#endif
+
+#ifndef SETPROCNAME_USE_NONE
+static PyObject *setprocname(PyObject *self, PyObject *args)
+{
+	const char *name = NULL;
+	if (!PyArg_ParseTuple(args, "s", &name))
+		return NULL;
+
+#if defined(SETPROCNAME_USE_SETPROCTITLE)
+	setproctitle("%s", name);
+#elif defined(SETPROCNAME_USE_ARGVREWRITE)
+	{
+		static char *argvstart = NULL;
+		static size_t argvsize = 0;
+		if (argvstart == NULL) {
+			int argc = 0, i;
+			char **argv = NULL;
+			extern void Py_GetArgcArgv(int *argc, char ***argv);
+			Py_GetArgcArgv(&argc, &argv);
+
+			/* Check the memory we can use. Typically, argv[i] and
+			 * argv[i + 1] are continuous. */
+			char *argvend = argvstart = argv[0];
+			for (i = 0; i < argc; ++i) {
+				if (argv[i] > argvend || argv[i] < argvstart)
+					break; /* not continuous */
+				size_t len = strlen(argv[i]);
+				argvend = argv[i] + len + 1 /* '\0' */;
+			}
+			if (argvend > argvstart) /* sanity check */
+				argvsize = argvend - argvstart;
+		}
+
+		if (argvstart && argvsize > 1) {
+			int n = snprintf(argvstart, argvsize, "%s", name);
+			if (n >= 0 && (size_t)n < argvsize)
+				memset(argvstart + n, 0, argvsize - n);
+		}
+	}
+#endif
+
+	Py_RETURN_NONE;
+}
+#endif /* ndef SETPROCNAME_USE_NONE */
+
 #endif /* ndef _WIN32 */
 
@@ -900,5 +956,9 @@  static PyMethodDef methods[] = {
 	 "receive list of file descriptors via socket\n"},
 #endif
+#ifndef SETPROCNAME_USE_NONE
+	{"setprocname", (PyCFunction)setprocname, METH_VARARGS,
+	 "set process title (best-effort)\n"},
 #endif
+#endif /* ndef _WIN32 */
 #ifdef __APPLE__
 	{