Patchwork [1,of,2,chg-port] osutil: implement recvmsg() of SCM_RIGHTS for chg command server

login
register
mail settings
Submitter Yuya Nishihara
Date Dec. 19, 2015, 3:17 a.m.
Message ID <076525f412e74c913f72.1450495056@mimosa>
Download mbox | patch
Permalink /patch/12179/
State Accepted
Headers show

Comments

Yuya Nishihara - Dec. 19, 2015, 3:17 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1450363306 -32400
#      Thu Dec 17 23:41:46 2015 +0900
# Node ID 076525f412e74c913f72ff52034aa7ea046af74b
# Parent  ff305ab2e0d7291da12a8b640ce8c9bb28e0cb03
osutil: implement recvmsg() of SCM_RIGHTS for chg command server

It will be used to attach client's stdio files to a background chg command
server.

The socket module of Python 2.x doesn't provide recvmsg(). This could be
implemented by using ctypes, but it would be less portable than the C version
because the handling of socket ancillary data heavily depends on preprocessor.
Also, some length fields are wrongly typed in the Linux kernel.

Patch

diff --git a/mercurial/osutil.c b/mercurial/osutil.c
--- a/mercurial/osutil.c
+++ b/mercurial/osutil.c
@@ -11,6 +11,7 @@ 
 #include <Python.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 
@@ -19,6 +20,7 @@ 
 #include <io.h>
 #else
 #include <dirent.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -648,6 +650,69 @@  bail:
 	return NULL;
 }
 
+/*
+ * recvfds() simply does not release GIL during blocking io operation because
+ * command server is known to be single-threaded.
+ */
+
+static ssize_t recvfdstobuf(int sockfd, int **rfds, void *cbuf, size_t cbufsize)
+{
+	char dummy[1];
+	struct iovec iov = {dummy, sizeof(dummy)};
+	struct msghdr msgh = {0};
+	struct cmsghdr *cmsg;
+
+	msgh.msg_iov = &iov;
+	msgh.msg_iovlen = 1;
+	msgh.msg_control = cbuf;
+	msgh.msg_controllen = (socklen_t)cbufsize;
+	if (recvmsg(sockfd, &msgh, 0) < 0)
+		return -1;
+
+	for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg;
+	     cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
+		if (cmsg->cmsg_level != SOL_SOCKET ||
+		    cmsg->cmsg_type != SCM_RIGHTS)
+			continue;
+		*rfds = (int *)CMSG_DATA(cmsg);
+		return (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+	}
+
+	*rfds = cbuf;
+	return 0;
+}
+
+static PyObject *recvfds(PyObject *self, PyObject *args)
+{
+	int sockfd;
+	int *rfds = NULL;
+	ssize_t rfdscount, i;
+	char cbuf[256];
+	PyObject *rfdslist = NULL;
+
+	if (!PyArg_ParseTuple(args, "i", &sockfd))
+		return NULL;
+
+	rfdscount = recvfdstobuf(sockfd, &rfds, cbuf, sizeof(cbuf));
+	if (rfdscount < 0)
+		return PyErr_SetFromErrno(PyExc_OSError);
+
+	rfdslist = PyList_New(rfdscount);
+	if (!rfdslist)
+		goto bail;
+	for (i = 0; i < rfdscount; i++) {
+		PyObject *obj = PyInt_FromLong(rfds[i]);
+		if (!obj)
+			goto bail;
+		PyList_SET_ITEM(rfdslist, i, obj);
+	}
+	return rfdslist;
+
+bail:
+	Py_XDECREF(rfdslist);
+	return NULL;
+}
+
 #endif /* ndef _WIN32 */
 
 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
@@ -816,6 +881,8 @@  static PyMethodDef methods[] = {
 	{"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
 	 "stat a series of files or symlinks\n"
 "Returns None for non-existent entries and entries of other types.\n"},
+	{"recvfds", (PyCFunction)recvfds, METH_VARARGS,
+	 "receive list of file descriptors via socket\n"},
 #endif
 #ifdef __APPLE__
 	{