"unshare" не работает должным образом в C api
Эта последовательность команд работает:
unshare --fork --pid --mount
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts
Однако соответствующая C-программа не работает должным образом (кажется, она не отключает предыдущий /proc, а также обеспечивает EBUSY, пытающуюся отключить devpts):
unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
int status;
waitpid(-1, &status, 0);
return status;
}
printf("My pid: %i\n", getpid()); // It prints 1 as expected
umount("/proc"); // Returns 0
system("mount"); // Should print error on mtab, but it prints the previous mounted filesystems
mount("proc", "/proc", "proc",
MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
NULL)); // Returns 0
umount("/dev/pts"); // Returns -1 errno = 0 (??)
mount("devpts", "/dev/pts", "devpts",
MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
NULL) ); // Returns -1 errno = EBUSY
Здесь я пропустил проверку ошибок для чтения
Я думаю, что unshare или unmount не работают так, как ожидаете: даже если он возвращает ноль, кажется, что он не отключает /proc (если я пытаюсь выполнить system("mount")после этого, он печатает смонтированные файловые системы).
Ответы
Ответ 1
Я обнаружил проблему с проверкой исходного кода команды unshare. /proc
должен быть размонтирован с помощью MS_PRIVATE | MS_REC
и установлен без них, это, по сути, должно гарантировать, что монтирование имеет эффект только в текущем (новом) пространстве имен. Вторая проблема заключается в невозможности umount /dev/pts
без влияния на глобальное пространство имен (это вызвано внутренней процедурой драйвера devpts). Чтобы иметь частный /dev/pts, единственный способ - установить его с помощью выделенной опции -o newinstance
. Наконец, /dev/ptmx
также должен быть связан с привязкой.
Следовательно, это рабочий код C, как ожидалось:
unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
int status;
waitpid(-1, &status, 0);
return status;
}
printf("New PID after unshare is %i", getpid());
if (mount("none", "/proc", NULL, MS_PRIVATE|MS_REC, NULL)) {
printf("Cannot umount proc! errno=%i", errno);
exit(1);
}
if (mount("proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL)) {
printf("Cannot mount proc! errno=%i", errno);
exit(1);
}
if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC, "newinstance") ) {
printf("Cannot mount pts! errno=%i", errno);
exit(1);
}
if (mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_BIND, NULL) ) {
printf("Cannot mount ptmx! errno=%i", errno);
exit(1);
}
Ответ 2
Несмотря на ваш комментарий, что
"иногда" umount
возвращает 0 "иногда" -1, но в конце он не отключает /proc
вообще
в 10000 испытаниях кода из вашего pastebin, umount()
всегда терпел неудачу для меня, возвращая -1
и не размонтируя /proc
. Я не склонен полагать, что umount()
когда-либо возвращает 0
, несмотря на то, что не выполнил запрошенную размонтирование, но, если когда-либо это произойдет, это будет ошибкой в umount()
. Если вы можете на самом деле обосновать такую ошибку, тогда ответ, настроенный сообществом, будет состоять в том, чтобы подать отчет об ошибках против glibc.
Тогда возникает вопрос, почему и как твой bash
script ведет себя по-другому. На самом деле это, похоже, не делает.
Во-первых, у вас есть неправильное ожидание команды unshare(1)
. В отличие от функции unshare(2)
команда unshare
не влияет на оболочку, в которой она выполнена. Вместо этого он запускает отдельный процесс, который имеет свои личные копии указанных пространств имен. Обычно вы должны указать команду для запуска этого процесса в командной строке unshare
, и на самом деле на странице руководства программы указано, что выполнение этого является обязательным.
Эмпирически я нахожу, что если я не могу указать такую команду - как и вы, то unshare
запускает новую оболочку в качестве целевого процесса. В частности, когда я запускаю ваш script (с достаточной привилегией использовать unshare
), я сразу получаю новое приглашение, но это приглашение новой оболочки, работающей на переднем плане. Это сразу становится очевидным для меня, потому что приглашение отличается (ваше приглашение может не отличаться при этих обстоятельствах). В этой точке нет сообщения об ошибке и т.д. Из umount
, потому что он еще не запущен. Если я вручную попытаюсь выполнить umount
proc в подселле (unshare
d), он завершится неудачей, когда "устройство занято" - это аналог того, что пытается выполнить ваша программа C.
Когда я выхожу из подоболочки, остальная часть script запускается с ошибкой umount
и оба mount
. Этого следует ожидать, потому что основной script разделяет пространство имен mount.
Весьма правдоподобно, что /proc
действительно занят и поэтому не может быть размонтирован даже для процесса с частной копией пространства имен mount. Вероятно, такой процесс сам использует свою частную копию монтирования /proc
. Напротив, я обнаружил, что могу успешно размонтировать /dev/pts
в процессе с пространством имен без разделяемого пространства, но не в процессе, который разделяет системную копию этого пространства имен.
Ответ 3
Я думаю, что проблема связана с системой ( "mount" ), которая порождает оболочку и не переносит umount. Попробуйте открыть файл в /proc/after umount и посмотреть, как он работает, как ожидалось.
Смотрите это -
unshare(CLONE_NEWPID | CLONE_NEWNS );
int rc = 0;
int pid = fork();
if (pid != 0) {
int status;
waitpid(-1, &status, 0);
return status;
}
printf(">>> My pid: %d\n", getpid()); // It prints 1 as expected
rc = umount2("/proc", MNT_FORCE); // Returns 0
printf(">>> umount returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));
rc = open("/proc/cpuinfo", O_RDONLY);
printf(">>> open returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));
Ответ 4
unshare bash!= unshare c
unshare - запустить программу с некоторыми пространствами имен, не разделенными с родителями
Таким образом, в основном с --fork вы форкируете из /bin/sh из/bin/bash (независимо от того, что вы выполняете с помощью script) с параметрами --pid и --mount.
"fork", за которым следует "unshare"
unshare - дизассемблировать части контекста выполнения процесса (текущий процесс)
Вы не разделяетесь с init, а затем на forking.
CLONE_NEWPID - это флаг "clone" не "unshare"
Итак, в зависимости от того, чего вы пытаетесь достичь, я предполагаю, что вы пытаетесь сделать "/proc" и "/dev/pts" исключительными для дочернего процесса.
Вот небольшой пример с локальными папками mount --bind:
# mkdir mnt point
# touch point/point.txt
# mount --bind point mnt
# ls mnt
point.txt
# ./unshare
My pid: 28377
Child:
point.txt
Parent:
# ls mnt
код:
#define _GNU_SOURCE
#include <sched.h>
int main(int argc, char *argv[])
{
/** umount global */
system("umount mnt/");
int pid = fork();
if (pid != 0) {
int status;
waitpid(-1, &status, 0);
printf("Parent:\n");
/* and here we don't */
system("ls mnt/");
return status;
}
/* unshare */
unshare(CLONE_FS | CLONE_NEWNS);
printf("My pid: %i\n", getpid()); // It prints 1 as expected
/* mount exclusively */
system("mount --bind point/ mnt/");
printf("Child:\n");
/* here we see it */
system("ls mnt/");
return 0;
}
есть также хороший пример с bash:
http://karelzak.blogspot.ru/2009/12/unshare1.html
продолжение:
mount зависит от /etc/mtab, который не всегда является символической ссылкой на /proc/mounts
то проверьте /etc/mtab с помощью ls -la.
также проверьте код для umount на /dev/pts с помощью:
int ret = umount("/dev/pts");
int errsv = errno;
if(ret == -1) {
printf("Error on umount: %s\n", strerror(errsv));
}
Я уверен, что он используется - проверьте его с помощью fuser/dev/pts/
** EDITED **
Наконец - я не уверен, что вы можете umount procfs только в пространстве имен (я думаю, что это невозможно)
но вы можете смонтировать свою собственную копию procfs в своем пространстве имен:
# mount -t proc proc /proc/
Теперь только ваш процесс отображается через ps -e.