''Unshare'' no funciona como se espera en C api
linux linux-namespaces (4)
Esta secuencia de comandos funciona:
unshare --fork --pid --mount
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts
Sin embargo, el programa C correspondiente no funciona como se esperaba (parece que no desmonta el proc / anterior, y también proporciona EBUSY al intentar desmontar los 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
He omitido aquí la comprobación de errores para la legibilidad
Creo que no compartir o desmontar no funciona como se esperaba: incluso si devuelve cero, parece que no se desmonta / proc (si intento ejecutar un system("mount")
después de eso, imprime los sistemas de archivos montados).
A pesar de tu comentario que
"a veces"
umount
devuelve 0 "a veces" -1, pero al final no desmonta/proc
en absoluto
, en 10000 intentos del código de tu pastebin, umount()
siempre falló para mí, devolviendo -1
y no desmontando /proc
. No estoy dispuesto a creer que umount()
siempre devuelve 0
pesar de no haber realizado el desmontaje solicitado, pero si alguna vez lo hace, eso constituiría un error en umount()
. Si de hecho puede justificar un error, la respuesta de la comunidad sería presentar un informe de error contra glibc.
La pregunta entonces se convierte en por qué y cómo su script de bash
comporta de manera diferente. De hecho, sin embargo, no parece hacerlo.
En primer lugar, tiene la expectativa errónea del comando unshare(1)
. A diferencia de la función unshare(2)
, el comando unshare
no afecta al shell en el que se ejecuta. En su lugar, lanza un proceso separado que tiene sus propias copias privadas de los espacios de nombres especificados. Normalmente, debe especificar el comando para iniciar ese proceso en la línea de comandos de no unshare
y, de hecho, la página del manual del programa indica que hacerlo es obligatorio.
Empíricamente, encuentro que si no especifico un comando de este tipo, como lo hace usted, entonces unshare
lanza un nuevo shell como proceso objetivo. En particular, cuando ejecuto su secuencia de comandos (con el privilegio suficiente para usar unshare
), inmediatamente recibo un nuevo indicador, pero es el indicador del nuevo shell, que se ejecuta en primer plano. Esto es inmediatamente evidente para mí porque la solicitud es diferente (sin embargo, su solicitud podría no ser diferente en esas circunstancias). No hay mensaje de error, etc. de umount
en ese punto porque aún no se ha ejecutado . Si umount
de umount
manualmente proc en la subshell ( unshare
d), falla con "el dispositivo está ocupado": este es el análogo de lo que su programa C intenta hacer.
Cuando salgo de la subshell, el resto de la secuencia de comandos se ejecuta, con umount
sy ambos mount
s fallando. Eso es de esperar, porque el script principal comparte su espacio de nombres de montaje.
Es completamente plausible que /proc
realmente esté ocupado y, por lo tanto, no se pueda desmontar, incluso para un proceso que tiene una copia privada del espacio de nombres de montaje. Es probable que ese proceso esté utilizando su copia privada del montaje de /proc
. En contraste, encuentro que puedo desmontar /dev/pts
exitosamente en un proceso con un espacio de nombres de montaje no compartido, pero no en un proceso que comparta la copia del sistema de ese espacio de nombres.
Creo que el problema está en el sistema ("mount"), que genera un shell y no se transfiere a umount. Intente abrir un archivo en / proc / after umount y vea que funciona como se esperaba.
Mira esto -
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));
Encontré el problema al verificar el código fuente del comando Unshare . El /proc
debe ser desmontado con MS_PRIVATE | MS_REC
MS_PRIVATE | MS_REC
y montado sin ellos, esto es esencialmente para garantizar que el montaje tenga efecto solo en el espacio de nombres actual (el nuevo). El segundo problema es que no es posible desmontar /dev/pts
sin producir un efecto en el espacio de nombres global (esto es causado por la rutina interna del controlador de devpts). Para tener un / dev / pts privado, la única forma es montarlo con la opción -o newinstance
dedicada. Finalmente, /dev/ptmx
también debe ser remontado de manera vinculante.
Por lo tanto, este es el código de trabajo de C como se esperaba:
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);
}
unshare bash! = unshare c
unshare - ejecuta el programa con algunos espacios de nombres no compartidos desde el padre
Básicamente, con --fork estás bifurcando desde / bin / sh de / bin / bash (sea cual sea tu ejecución de script) con las opciones --pid y --mount. "fork" seguido de "unshare"
unshare - desasociar partes del contexto de ejecución del proceso (proceso actual) Está desconectando de init y luego bifurcando.
CLONE_NEWPID es un indicador de "clonación" y no "no compartido"
Entonces, dependiendo de lo que está tratando de lograr, asumo que está tratando de hacer "/ proc" y "/ dev / pts" exclusivos para el proceso de niños.
Aquí hay un pequeño ejemplo con las carpetas locales de 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
Código:
#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;
}
También hay un buen ejemplo con bash: http://karelzak.blogspot.ru/2009/12/unshare1.html
continuación:
el montaje depende de / etc / mtab, que no siempre es un enlace simbólico a / proc / mounts
así que compruebe / etc / mtab con ls -la.
También verifique el código para umount en / dev / pts con:
int ret = umount("/dev/pts");
int errsv = errno;
if(ret == -1) {
printf("Error on umount: %s/n", strerror(errsv));
}
Estoy bastante seguro de que está en uso - verifíquelo con fuser / dev / pts /
** EDITADO **
Finalmente, no estoy seguro de que pueda desmontar procfs solo en el espacio de nombres (creo que es imposible)
pero puedes montar tu propia copia de procfs en tu espacio de nombres:
# mount -t proc proc /proc/
Ahora solo tu proceso es visible a través de ps -e.