UNSHARE(2)

ИМЯ
unshare - отделяет части процесса контекста выполнения

ОБЗОР
#define _GNU_SOURCE
#include <sched.h>

int unshare(int flags);

ОПИСАНИЕ
Вызов 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_NEWCGROUP (начиная с Linux 4.6)
    Этот флаг имеет действие подобное флагу CLONE_NEWCGROUP для clone(2). Отделяет пространство имён cgroup. Для использования CLONE_NEWCGROUP требуется мандат CAP_SYS_ADMIN.

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. Дополнительная информация доступна в mount_namespaces(7).

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
    Не удалось выделить достаточно памяти для копирования части контекста вызывающего, которая должна быть отделена.

ENOSPC (начиная с Linux 3.7)
    В флагах указан CLONE_NEWPID, но вызов привёл бы к превышению ограничения на количество вложенных имён PID; смотрите pid_namespaces(7).

ENOSPC (начиная с Linux 4.9; до этого EUSERS)
    Флаг CLONE_NEWUSER указан в flags, и вызов привёл бы к превышению ограничения на количество вложенных пользовательских пространств имён. Смотрите user_namespaces(7).

    В этом случае в Linux 3.11 по Linux 4.8 возвращалась ошибка EUSERS.

ENOSPC (начиная с Linux 4.9)
    Одним из значений в flags задаётся создание нового пространства пространства имён пользователя, но это превысило бы ограничение, определённое в соответствующем файле в каталоге /proc/sys/user. Дополнительную информацию смотрите в namespaces(7).

EPERM
    Вызывающий процесс не имеет требуемых привилегий для этой операции.

EPERM
    Флаг CLONE_NEWUSER указан в flags, но эффективный пользовательский ID или эффективный ID группы вызывающего не отображён в родительское пространство имён (смотрите user_namespaces(7)).

EPERM (начиная с Linux 3.9)
    В flags был указан флаг CLONE_NEWUSER и вызывающий выполняется в окружении chroot (т. е. корневой каталог вызывающего не совпадает с корневым каталогом пространства имён монтирования, в котором он находится).

EUSERS (Linux 3.11 по Linux 4.8)
    Флаг CLONE_NEWUSER указан в flags, и вызов привёл бы к превышению ограничения на количество вложенных пользовательских пространств имён. Смотрите описание ошибки ENOSPC, представленное выше.

ВЕРСИИ
Системный вызов 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
    # 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");
    }

СМОТРИТЕ ТАКЖЕ
unshare(1), clone(2), fork(2), kcmp(2), setns(2), vfork(2), namespaces(7)

Файл Documentation/userspace-api/unshare.rst из дерева исходного кода ядра Linux (или Documentation/unshare.txt до Linux 4.12)