unshare(2) отделяет части процесса контекста выполнения

ОБЗОР

#include <sched.h>


int unshare(int flags);

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

unshare():

Начиная с glibc 2.14:
_GNU_SOURCE
До glibc 2.14: _BSD_SOURCE || _SVID_SOURCE
    /* также достаточно _GNU_SOURCE */

ОПИСАНИЕ

Вызов unshare() позволяет процессу (или потоку) отделить части своего контекста выполнения, которые используются совместно с другими процессами (или потоками). Часть контекста выполнения, например пространство имён монтирования, неявно делается общей при создании нового процесса с помощью fork(2) или vfork(2), в то время как другие части, такие как виртуальная память, могут стать общими по явному запросу при создании процесса или потока с помощью clone(2).

Основное предназначение unshare() --- позволить процессу контролировать свой общий контекст выполнения без создания нового процесса.

Значение аргумента flags представляет собой битовую маску, в которой указывается какие части контекста выполнения должны перестать быть общими. Значение составляется из нескольких следующих констант (через OR):

CLONE_FILES
Обратный эффект флагу CLONE_FILES для clone(2). Отделяет таблицу файловых дескрипторов таким образом, что вызывающий процесс больше не имеет общих файловых дескрипторов с другими процессами.
CLONE_FS
Обратный эффект флагу CLONE_FS для clone(2). Отделяет атрибуты файловой системы таким образом, что вызывающий процесс больше не имеет общих атрибутов корневого каталога (chroot(2)), текущего каталога (chdir(2)) и umask (umask(2)) с другими процессами.
CLONE_NEWIPC (начиная с Linux 2.6.19)
Этот флаг имеет действие подобное флагу CLONE_NEWIPC для clone(2). Отделяет пространство имён IPC таким образом, что вызывающий процесс будет иметь свою личную копию пространства имён IPC, неиспользуемую другими процессами. Задание данного флага автоматически устанавливает флаг CLONE_SYSVSEM. Для использования CLONE_NEWIPC требуется мандат CAP_SYS_ADMIN.
CLONE_NEWNET (начиная с Linux 2.6.24)
Этот флаг имеет действие подобное флагу CLONE_NEWNET для clone(2). Отделяет сетевое пространство имён таким образом, что вызывающий процесс будет иметь свою личную копию сетевого пространства имён, неиспользуемую другими процессами. Для использования CLONE_NEWNET требуется мандат CAP_SYS_ADMIN.
CLONE_NEWNS
Этот флаг имеет действие подобное флагу CLONE_NEWNS для clone(2). Отделяет пространство имён монтирования таким образом, что вызывающий процесс будет иметь свою личную копию данного пространства имён, неиспользуемую другими процессами. Задание данного флага автоматически устанавливает флаг CLONE_FS. Для использования CLONE_NEWNS требуется мандат CAP_SYS_ADMIN.
CLONE_NEWPID (начиная с Linux 3.8)
Данный флаг позволяет то же что и CLONE_NEWPID у clone(2). Выполняется отключение от пространства имён PID; вызывающий процесс создаёт новое пространство имён PID для своих потомков, которые до этого не были объединены с существующим процессом. Вызывающий процесс не перемещается в новое пространство имён. Первый потомок, созданный вызывающим процессом, будет иметь ID процесса 1 и считаться init(1) в новом пространстве имён. Также флаг CLONE_NEWPID автоматически подразумевает CLONE_THREAD. Для использования CLONE_NEWPID требуется мандат CAP_SYS_ADMIN. Подробней смотрите pid_namespaces(7).
CLONE_NEWUSER (начиная с Linux 3.8)
Данный флаг позволяет то же что и CLONE_NEWUSER у clone(2). Выполняется отключение от пользовательского пространства имён; вызывающий процесс перемещается в новое пользовательское пространство имён, которое до этого не был общим для существующего процесса. Как потомок процесса, созданный clone(2) с флагом CLONE_NEWUSER, вызывающий получает полный набор мандатов в новом пользовательском пространстве имён.
Для CLONE_NEWUSER требуется, чтобы вызывающий процесс не имел нитей; указание CLONE_NEWUSER автоматически подразумевает CLONE_THREAD. Начиная с Linux 3.9, CLONE_NEWUSER также автоматически подразумевает CLONE_FS. Для CLONE_NEWUSER требуется, чтобы ID пользователя и группы вызывающего процесса отображались в ID пользователя и группы в пользовательском пространстве имён вызывающего процесса на момент вызова.

Дополнительную информацию о пространствах имён пользователя смотрите в user_namespaces(7).

CLONE_NEWUTS (начиная с Linux 2.6.19)
Этот флаг имеет действие подобное флагу CLONE_NEWUTS для clone(2). Отделяет пространство имён UTS IPC таким образом, что вызывающий процесс будет иметь свою личную копию пространства имён UTS, неиспользуемую другими процессами. Для использования CLONE_NEWUTS требуется мандат CAP_SYS_ADMIN.
CLONE_SYSVSEM (начиная с Linux 2.6.26)
С данным флагом выполняется обратное действие clone(2) с флагом CLONE_SYSVSEM. Выполняется отмена изменения значений семафоров System V (semadj) таким образом, что вызывающий процесс получает новый пустой список semadj, который не является общим ни с одним другим процессом. Если это последний процесс, который ссылается на текущий список semadj процесса, то изменения (adjustments) в этом списке применяются к соответствующим семафорам как описано в semop(2).

Также в flags могут быть указаны флаги CLONE_THREAD, CLONE_SIGHAND и CLONE_VM, если вызывающий состоит из одной нити (т. е., он не делит своё адресное пространство с другим процессом или нитью). Иначе данные флаги не работают (также заметим, что указание CLONE_THREAD автоматически подразумевает CLONE_VM, а указание CLONE_VM автоматически подразумевает CLONE_SIGHAND). Если процесс состоит из нескольких нитей, то использование данных флагов приведёт к ошибке.

Если значение flags равно нулю, то unshare() ничего не делает, то есть в контексте выполнения вызывающего процесса ничего не изменяется.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении возвращается 0. При ошибке возвращается -1, а errno присваивается значение ошибки.

ОШИБКИ

EINVAL
В значении flags установлен недопустимый бит.
EINVAL
В flags указан CLONE_THREAD, CLONE_SIGHAND или CLONE_VM, а вызывающий состоит из нескольких нитей.
ENOMEM
Не удалось выделить достаточно памяти для копирования части контекста вызывающего, которая должна быть отделена.
EPERM
Вызывающий процесс не имеет требуемых привилегий для этой операции.
EPERM
Флаг CLONE_NEWUSER указан в flags, но эффективный пользовательский ID или эффективный ID группы вызывающего не отображён в родительское пространство имён (смотрите user_namespaces(7)).
EPERM (начиная с Linux 3.9)
В flags был указан флаг CLONE_NEWUSER и вызывающий выполняется в окружении chroot (т. е. корневой каталог вызывающего не совпадает с корневым каталогом пространства имён монтирования, в котором он находится).
EUSERS (начиная с Linux 3.11)
Флаг CLONE_NEWUSER указан в flags, и вызов привёл бы к превышению ограничения на количество вложенных пользовательских пространств имён. Смотрите user_namespaces(7).

ВЕРСИИ

Системный вызов unshare() был добавлен в ядро Linux версии 2.6.16.

СООТВЕТСТВИЕ СТАНДАРТАМ

Системный вызов unshare() есть только в Linux.

ЗАМЕЧАНИЯ

Не все атрибуты процесса, которые могут использоваться совместно при создании нового процесса с помощью clone(2), могут быть отделены с помощью unshare(). В частности, начиная с ядра 3.8 в unshare() не реализована поддержка флагов, которые были имели обратное действие CLONE_SIGHAND, CLONE_THREAD или CLONE_VM. Эти возможности могут быть добавлены позднее, если потребуется.

ПРИМЕР

Программа, представленная далее, предоставляет простую реализацию команды unshare(1), которая отключает использование одного или более пространств имён и выполняет команду, переданную в аргументах командной строки. Вот пример использования этой программы; запускается оболочка в новом пространстве имён монтирования и проверяется, что первоначальная оболочка и новая оболочка находятся в разных пространствах имён монтирования:
$ readlink /proc/$$/ns/mnt
mnt:[4026531840]
$ sudo ./unshare -m /bin/bash
[sudo] password for cecilia:
# readlink /proc/$$/ns/mnt
mnt:[4026532325]

Различающийся вывод двух команд readlink(1) показывает, что две оболочки находятся в разных пространствах имён монтирования.

Исходный код программы

/* unshare.c
   Простая реализация команды unshare(1): отключение от
   пространств имён и выполнение команды.
*/
#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
/* Простая функция обработки ошибок: выводит сообщение об ошибке согласно
   значению в «errno» и завершает вызвавший процесс */
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
static void
usage(char *pname)
{
    fprintf(stderr, "Использование: %s [параметры] программа [аргументы…]\n", pname);
    fprintf(stderr, "Параметры:\n");
    fprintf(stderr, "    -i   отключиться от пространства имён IPC\n");
    fprintf(stderr, "    -m   отключиться от пространства имён монтирования\n");
    fprintf(stderr, "    -n   отключиться от сетевого пространства имён\n");
    fprintf(stderr, "    -p   отключиться от пространства имён PID\n");
    fprintf(stderr, "    -u   отключиться от пространства имён UTS\n");
    fprintf(stderr, "    -U   отключиться от пользовательского пространства имён\n");
    exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
    int flags, opt;
    flags = 0;
    while ((opt = getopt(argc, argv, "imnpuU")) != -1) {
        switch (opt) {
        case 'i': flags |= CLONE_NEWIPC;        break;
        case 'm': flags |= CLONE_NEWNS;         break;
        case 'n': flags |= CLONE_NEWNET;        break;
        case 'p': flags |= CLONE_NEWPID;        break;
        case 'u': flags |= CLONE_NEWUTS;        break;
        case 'U': flags |= CLONE_NEWUSER;       break;
        default:  usage(argv[0]);
        }
    }
    if (optind >= argc)
        usage(argv[0]);
    if (unshare(flags) == -1)
        errExit("unshare");
    execvp(argv[optind], &argv[optind]);
    errExit("execvp");
}