Skip to content

Commit

Permalink
gh-95023: added os.setns and os.unshare for namespaces switching on L…
Browse files Browse the repository at this point in the history
…inux
  • Loading branch information
noamcohen97 committed Jul 20, 2022
1 parent 49aeff4 commit 0bdd8bc
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 1 deletion.
30 changes: 30 additions & 0 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,15 @@ process and user.
See the documentation for :func:`getgroups` for cases where it may not
return the same group list set by calling setgroups().

.. function:: setns(fd, flags=0)

Call the system call :c:func:`setns`. See the Linux manual for the semantics.
For flags see the ``CLONE_NEW*`` constants.

.. availability:: Linux 3.0 or newer.

.. versionadded:: 3.12

.. function:: setpgrp()

Call the system call :c:func:`setpgrp` or ``setpgrp(0, 0)`` depending on
Expand Down Expand Up @@ -732,6 +741,27 @@ process and user.
The function is now always available and is also available on Windows.


.. function:: unshare(flags)

Call the system call :c:func:`unshare`. See the Linux manual for the semantics.

.. availability:: Linux 2.6.16 or newer.

.. versionadded:: 3.12

Parameters to the :func:`unshare` function, if the implementation supports them.

.. data:: CLONE_FS
CLONE_FILES
CLONE_NEWNS
CLONE_NEWCGROUP
CLONE_NEWUTS
CLONE_NEWIPC
CLONE_NEWUSER
CLONE_NEWPID
CLONE_NEWNET
CLONE_NEWTIME

.. _os-newstreams:

File Object Creation
Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -2172,6 +2172,35 @@ def test_utime(self):
os.utime("path", dir_fd=0)


class NamespacesTests(unittest.TestCase):
"""Tests for os.unshare() and os.setns()."""

@unittest.skipUnless(hasattr(os, 'unshare'), 'needs os.unshare()')
@unittest.skipUnless(hasattr(os, 'setns'), 'needs os.setns()')
@unittest.skipUnless(hasattr(os, 'readlink'), 'needs os.readlink()')
@unittest.skipUnless(os.path.exists('/proc/self/ns/uts'), 'need /proc/self/ns/uts')
@support.requires_linux_version(3, 0, 0)
def test_unshare_setns(self):
original = os.readlink('/proc/self/ns/uts')
original_fd = os.open('/proc/self/ns/uts', os.O_RDONLY)
self.addCleanup(os.close, original_fd)

try:
os.unshare(os.CLONE_NEWUTS)
except OSError as e:
self.assertEqual(e.errno, errno.EPERM)
self.skipTest("unprivileged users cannot call unshare.")

current = os.readlink('/proc/self/ns/uts')
self.assertNotEqual(original, current)

self.assertRaises(OSError, os.setns, original_fd, os.CLONE_NEWNET)
os.setns(original_fd, os.CLONE_NEWUTS)

current = os.readlink('/proc/self/ns/uts')
self.assertEqual(original, current)


def tearDownModule():
support.reap_children()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement :func:`os.setns` and :func:`os.unshare` for Linux. Patch by Noam Cohen
97 changes: 96 additions & 1 deletion Modules/clinic/posixmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 92 additions & 0 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -8588,6 +8588,61 @@ os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags)
#endif


#if defined(__linux__) && defined(HAVE_SETNS)
/*[clinic input]
os.setns
fd: fildes
nstype: int = 0
Allows the calling thread to move into different namespaces.
[clinic start generated code]*/

static PyObject *
os_setns_impl(PyObject *module, int fd, int nstype)
/*[clinic end generated code: output=5dbd055bfb66ecd0 input=c097c9aa123c43ce]*/
{
int res;

Py_BEGIN_ALLOW_THREADS
res = setns(fd, nstype);
Py_END_ALLOW_THREADS

if (res != 0) {
return posix_error();
}

Py_RETURN_NONE;
}
#endif


#if defined(__linux__) && defined(HAVE_UNSHARE)
/*[clinic input]
os.unshare
flags: int
Allows a process (or thread) to disassociate parts of its execution context.
[clinic start generated code]*/

static PyObject *
os_unshare_impl(PyObject *module, int flags)
/*[clinic end generated code: output=1b3177906dd237ee input=f8d7bd2c69325537]*/
{
int res;

Py_BEGIN_ALLOW_THREADS
res = unshare(flags);
Py_END_ALLOW_THREADS

if (res != 0) {
return posix_error();
}

Py_RETURN_NONE;
}
#endif


#if defined(HAVE_READLINK) || defined(MS_WINDOWS)
/*[clinic input]
os.readlink
Expand Down Expand Up @@ -14930,6 +14985,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 */
};

Expand Down Expand Up @@ -15375,6 +15432,41 @@ all_ins(PyObject *m)
#ifdef SCHED_FX
if (PyModule_AddIntConstant(m, "SCHED_FX", SCHED_FSS)) return -1;
#endif

/* constants for namespaces */
#if defined(__linux__) && (defined(HAVE_SETNS) || defined(HAVE_UNSHARE))
#ifdef CLONE_FS
if (PyModule_AddIntMacro(m, CLONE_FS)) return -1;
#endif
#ifdef CLONE_FILES
if (PyModule_AddIntMacro(m, CLONE_FILES)) return -1;
#endif
#ifdef CLONE_NEWNS
if (PyModule_AddIntMacro(m, CLONE_NEWNS)) return -1;
#endif
#ifdef CLONE_NEWCGROUP
if (PyModule_AddIntMacro(m, CLONE_NEWCGROUP)) return -1;
#endif
#ifdef CLONE_NEWUTS
if (PyModule_AddIntMacro(m, CLONE_NEWUTS)) return -1;
#endif
#ifdef CLONE_NEWIPC
if (PyModule_AddIntMacro(m, CLONE_NEWIPC)) return -1;
#endif
#ifdef CLONE_NEWUSER
if (PyModule_AddIntMacro(m, CLONE_NEWUSER)) return -1;
#endif
#ifdef CLONE_NEWPID
if (PyModule_AddIntMacro(m, CLONE_NEWPID)) return -1;
#endif
#ifdef CLONE_NEWNET
if (PyModule_AddIntMacro(m, CLONE_NEWNET)) return -1;
#endif
#ifdef CLONE_NEWTIME
if (PyModule_AddIntMacro(m, CLONE_NEWTIME)) return -1;
#endif
#endif

#endif

#ifdef USE_XATTRS
Expand Down
14 changes: 14 additions & 0 deletions configure

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -4937,6 +4937,9 @@ AC_CHECK_FUNCS(setpgrp,
[])
)

# check for namespace functions
AC_CHECK_FUNCS(setns unshare)

dnl We search for both crypt and crypt_r as one or the other may be defined
dnl libxcrypt provides <crypt.h> and libcrypt with crypt_r() since
dnl at least 3.1.1 from 2015.
Expand Down
6 changes: 6 additions & 0 deletions pyconfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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). */
Expand Down

0 comments on commit 0bdd8bc

Please sign in to comment.