From eab072b26ec981bd41766afdaca6f55833b7dca6 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 19 Jul 2022 23:28:46 +0200 Subject: [PATCH] gh-95023: Add os.setns() and os.unshare() for Linux namespaces --- ...2-07-19-23-28-00.gh-issue-95023.WnSPuf.rst | 1 + Modules/clinic/posixmodule.c.h | 92 +++++++++++++++++- Modules/posixmodule.c | 94 +++++++++++++++++++ configure | 4 +- configure.ac | 4 +- pyconfig.h.in | 6 ++ 6 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-07-19-23-28-00.gh-issue-95023.WnSPuf.rst diff --git a/Misc/NEWS.d/next/Library/2022-07-19-23-28-00.gh-issue-95023.WnSPuf.rst b/Misc/NEWS.d/next/Library/2022-07-19-23-28-00.gh-issue-95023.WnSPuf.rst new file mode 100644 index 00000000000000..487a12a1e6e118 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-19-23-28-00.gh-issue-95023.WnSPuf.rst @@ -0,0 +1 @@ +Add :func:`os.setns` and :func:`os.unshare` to manage namespaces on Linux. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 1ce7d86204e6f3..edfc6b206642b8 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -8769,6 +8769,88 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #endif /* (defined(WIFEXITED) || defined(MS_WINDOWS)) */ +#if defined(HAVE_SETNS) + +PyDoc_STRVAR(os_setns__doc__, +"setns($module, /, fd, nstype)\n" +"--\n" +"\n" +"Reassociate thread with a namespace"); + +#define OS_SETNS_METHODDEF \ + {"setns", _PyCFunction_CAST(os_setns), METH_FASTCALL|METH_KEYWORDS, os_setns__doc__}, + +static PyObject * +os_setns_impl(PyObject *module, int fd, int nstype); + +static PyObject * +os_setns(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"fd", "nstype", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "setns", 0}; + PyObject *argsbuf[2]; + int fd; + int nstype; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!args) { + goto exit; + } + if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) { + goto exit; + } + nstype = _PyLong_AsInt(args[1]); + if (nstype == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = os_setns_impl(module, fd, nstype); + +exit: + return return_value; +} + +#endif /* defined(HAVE_SETNS) */ + +#if defined(HAVE_UNSHARE) + +PyDoc_STRVAR(os_unshare__doc__, +"unshare($module, /, flags)\n" +"--\n" +"\n" +"Disassociate parts of the process execution context"); + +#define OS_UNSHARE_METHODDEF \ + {"unshare", _PyCFunction_CAST(os_unshare), METH_FASTCALL|METH_KEYWORDS, os_unshare__doc__}, + +static PyObject * +os_unshare_impl(PyObject *module, int flags); + +static PyObject * +os_unshare(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"flags", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "unshare", 0}; + PyObject *argsbuf[1]; + int flags; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + flags = _PyLong_AsInt(args[0]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = os_unshare_impl(module, flags); + +exit: + return return_value; +} + +#endif /* defined(HAVE_UNSHARE) */ + #ifndef OS_TTYNAME_METHODDEF #define OS_TTYNAME_METHODDEF #endif /* !defined(OS_TTYNAME_METHODDEF) */ @@ -9352,4 +9434,12 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=bae15f09a1b3d2e7 input=a9049054013a1b77]*/ + +#ifndef OS_SETNS_METHODDEF + #define OS_SETNS_METHODDEF +#endif /* !defined(OS_SETNS_METHODDEF) */ + +#ifndef OS_UNSHARE_METHODDEF + #define OS_UNSHARE_METHODDEF +#endif /* !defined(OS_UNSHARE_METHODDEF) */ +/*[clinic end generated code: output=fe2b3db627e561ad input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 40229bce0f4033..2cbe0661128a15 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -14743,6 +14743,54 @@ os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) } #endif +#ifdef HAVE_SETNS +/*[clinic input] +os.setns + + fd: fildes + nstype: int + +Reassociate thread with a namespace +[clinic start generated code]*/ + +static PyObject * +os_setns_impl(PyObject *module, int fd, int nstype) +/*[clinic end generated code: output=5dbd055bfb66ecd0 input=9d2de6d8a880014b]*/ +{ + int result; + Py_BEGIN_ALLOW_THREADS + result = setns(fd, nstype); + Py_END_ALLOW_THREADS + if (result == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + Py_RETURN_NONE; +} +#endif + +#ifdef HAVE_UNSHARE +/*[clinic input] +os.unshare + + flags: int + +Disassociate parts of the process execution context +[clinic start generated code]*/ + +static PyObject * +os_unshare_impl(PyObject *module, int flags) +/*[clinic end generated code: output=1b3177906dd237ee input=4d655e499ec28dd6]*/ +{ + int result; + Py_BEGIN_ALLOW_THREADS + result = unshare(flags); + Py_END_ALLOW_THREADS + if (result == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + Py_RETURN_NONE; +} +#endif static PyMethodDef posix_methods[] = { @@ -14930,6 +14978,8 @@ static PyMethodDef posix_methods[] = { OS__ADD_DLL_DIRECTORY_METHODDEF OS__REMOVE_DLL_DIRECTORY_METHODDEF OS_WAITSTATUS_TO_EXITCODE_METHODDEF + OS_SETNS_METHODDEF + OS_UNSHARE_METHODDEF {NULL, NULL} /* Sentinel */ }; @@ -15468,6 +15518,50 @@ all_ins(PyObject *m) if (PyModule_AddIntMacro(m, EFD_SEMAPHORE)) return -1; #endif +// setns(2) and unshare(2) constants +#ifdef CLONE_FILES + if (PyModule_AddIntMacro(m, CLONE_FILES)) return -1; +#endif +#ifdef CLONE_FS + if (PyModule_AddIntMacro(m, CLONE_FS)) return -1; +#endif +#ifdef CLONE_NEWCGROUP + if (PyModule_AddIntMacro(m, CLONE_NEWCGROUP)) return -1; +#endif +#ifdef CLONE_NEWIPC + if (PyModule_AddIntMacro(m, CLONE_NEWIPC)) return -1; +#endif +#ifdef CLONE_NEWNET + if (PyModule_AddIntMacro(m, CLONE_NEWNET)) return -1; +#endif +#ifdef CLONE_NEWNS + if (PyModule_AddIntMacro(m, CLONE_NEWNS)) return -1; +#endif +#ifdef CLONE_NEWPID + if (PyModule_AddIntMacro(m, CLONE_NEWPID)) return -1; +#endif +#ifdef CLONE_NEWTIME + if (PyModule_AddIntMacro(m, CLONE_NEWTIME)) return -1; +#endif +#ifdef CLONE_NEWUSER + if (PyModule_AddIntMacro(m, CLONE_NEWUSER)) return -1; +#endif +#ifdef CLONE_NEWUTS + if (PyModule_AddIntMacro(m, CLONE_NEWUTS)) return -1; +#endif +#ifdef CLONE_SYSVSEM + if (PyModule_AddIntMacro(m, CLONE_SYSVSEM)) return -1; +#endif +#ifdef CLONE_THREAD + if (PyModule_AddIntMacro(m, CLONE_THREAD)) return -1; +#endif +#ifdef CLONE_SIGHAND + if (PyModule_AddIntMacro(m, CLONE_SIGHAND)) return -1; +#endif +#ifdef CLONE_VM + if (PyModule_AddIntMacro(m, CLONE_VM)) return -1; +#endif + #if defined(__APPLE__) if (PyModule_AddIntConstant(m, "_COPYFILE_DATA", COPYFILE_DATA)) return -1; if (PyModule_AddIntConstant(m, "_COPYFILE_STAT", COPYFILE_STAT)) return -1; diff --git a/configure b/configure index d607c5e5d37a03..69299af623381f 100755 --- a/configure +++ b/configure @@ -15561,12 +15561,12 @@ for ac_func in \ rtpSpawn sched_get_priority_max sched_rr_get_interval sched_setaffinity \ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ - setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ + setitimer setlocale setns setpgid setpgrp setpriority setregid setresgid \ setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \ sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf system tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ - tmpnam tmpnam_r truncate ttyname umask uname unlinkat utimensat utimes vfork \ + tmpnam tmpnam_r truncate ttyname umask uname unlinkat unshare utimensat utimes vfork \ wait wait3 wait4 waitid waitpid wcscoll wcsftime wcsxfrm wmemcmp writev \ do : diff --git a/configure.ac b/configure.ac index c5924169e03a0b..4128b0ed1f1400 100644 --- a/configure.ac +++ b/configure.ac @@ -4669,12 +4669,12 @@ AC_CHECK_FUNCS([ \ rtpSpawn sched_get_priority_max sched_rr_get_interval sched_setaffinity \ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ - setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ + setitimer setlocale setns setpgid setpgrp setpriority setregid setresgid \ setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \ sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf system tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ - tmpnam tmpnam_r truncate ttyname umask uname unlinkat utimensat utimes vfork \ + tmpnam tmpnam_r truncate ttyname umask uname unlinkat unshare utimensat utimes vfork \ wait wait3 wait4 waitid waitpid wcscoll wcsftime wcsxfrm wmemcmp writev \ ]) diff --git a/pyconfig.h.in b/pyconfig.h.in index aa9fc559fa2511..54c42121d37a2e 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1019,6 +1019,9 @@ /* Define to 1 if you have the `setlocale' function. */ #undef HAVE_SETLOCALE +/* Define to 1 if you have the `setns' function. */ +#undef HAVE_SETNS + /* Define to 1 if you have the `setpgid' function. */ #undef HAVE_SETPGID @@ -1383,6 +1386,9 @@ /* Define to 1 if you have the `unlinkat' function. */ #undef HAVE_UNLINKAT +/* Define to 1 if you have the `unshare' function. */ +#undef HAVE_UNSHARE + /* Define if you have a useable wchar_t type defined in wchar.h; useable means wchar_t must be an unsigned type with at least 16 bits. (see Include/unicodeobject.h). */