units

title: units(7)

summary: десятичные и двоичные приставки

text:
# units(7)

## ИМЯ
units - десятичные и двоичные приставки

## ОПИСАНИЕ

### Десятичные приставки
В системе единиц СИ используются приставки, значения которых является степенью числа. В километре 1000 метров, а в мегаватте — 1000000 ватт. Далее показаны стандартные приставки.

Приставка

Имя

Значение

и

иокто

10^-24 = 0.000000000000000000000001

з

зепто

10^-21 = 0.000000000000000000001

а

атто

10^-18 = 0.000000000000000001

ф

фемто

10^-15 = 0.000000000000001

п

пико

10^-12 = 0.000000000001

н

нано

10^-9 = 0.000000001

мк

микро

10^-6 = 0.000001

м

милли

10^-3 = 0.001

с

санти

10^-2 = 0.01

д

деци

10^-1 = 0.1

да

дека

10^ 1 = 10

г

гекто

10^ 2 = 100

к

кило

10^ 3 = 1000

М

мега

10^ 6 = 1000000

Г

гига

10^ 9 = 1000000000

Т

тера

10^12 = 1000000000000

П

пета

10^15 = 1000000000000000

Э

экса

10^18 = 1000000000000000000

З

зетта

10^21 = 1000000000000000000000

И

иотта

10^24 = 1000000000000000000000000

Для обозначения micro используется греческая буква мю. В кодировке ASCII, где она отсутствует, вместо неё часто используют латинскую u. Смотрите также

http://physics.nist.gov/cuu/Units/prefixes.html

### Двоичные префиксы
Двоичные префиксы походят на десятичные, но имеют дополнительную 'i' (и «Ki» начинается с заглавной 'K'). Имена формируются из первого звука имени десятичных префиксов примерно того же размера, за которым следует «bi» (от «binary» — «двоичный»).

Приставка

Имя

Значение

КиБ

киби

2^10 = 1024

МиБ

меби

2^20 = 1048576

ГиБ

гиби

2^30 = 1073741824

ТиБ

теби

2^40 = 1099511627776

ПиБ

пеби

2^50 = 1125899906842624

ЭиБ

эксби

2^60 = 1152921504606846976

Смотрите также

http://physics.nist.gov/cuu/Units/binary.html

### Обсуждение
До введения двоичных префиксов довольно часто использовалось k=1000 и K=1024, также как и b=bit (бит), B=byte (байт). К сожалению, заглавная M уже занята, и не может быть использована как признак двоичности.

Поначалу это не имело большого значения, модули памяти и диски имели размер, равный степеням двойки, и все знали, что в таких случаях «килобайт» означает 1024, а «мегабайт» — 1048576 байтов. Впрочем, некоторая путаница связанная с этим всё же была. После некоторых колебаний все производители дисков установили стандарт, по которому k=1000, M=1000 к, G=1000 М.

Ситуация получилась запутанной: в модемах со скоростью 14k4 k=1000; в дискетах 1.44 МБ M=1024000 и т. п. В 1998 году IEC принял стандарт, который определяет двоичные префиксы как указано выше, что позволяет людям быть точными и однозначными.

Таким образом, сегодня МБ = 1000000 \[u0411], а МиБ = 1048576 \[u0411].

В мире свободного программного обеспечения программы медленно переходят к таким обозначениям. Когда ядро Linux при загрузке выводит

```
hda: 120064896 sectors (61473 MB) w/2048KiB Cache
```

то MB — это мегабайты, а KiB — кибибайты.


unix

package: man-pages-ru

title: unix(7)

text:
# unix(7)

## ИМЯ
unix - сокеты для локального межпроцессного взаимодействия

## ОБЗОР
\#include <sys/socket.h>

\#include <sys/un.h>

unix_socket = socket(AF_UNIX, type, 0);

error = socketpair(AF_UNIX, type, 0, int *sv);

## ОПИСАНИЕ
Семейство сокетов AF_UNIX (также известное, как AF_LOCAL) используется для эффективного взаимодействия между процессами на одной машине. Доменные сокеты UNIX могут быть как безымянными, так и иметь имя файла в файловой системе (типизированный сокет). В Linux также поддерживается абстрактное пространство имён, которое не зависит от файловой системы.

Допустимые типы сокета для домена UNIX: потоковый сокет SOCK_STREAM, датаграмный сокет SOCK_DGRAM, сохраняющий границы сообщений (в большинстве реализаций UNIX, доменные датаграмные сокеты UNIX всегда надёжны и не меняют порядок датаграмм); и (начиная с Linux 2.6.4) ориентированный на соединение задающий последовательность пакетам сокет SOCK_SEQPACKET, сохраняющий границы сообщений и доставляющий сообщения в том же порядке, в каком они были отправлены.

Доменные сокеты UNIX поддерживают передачу файловых дескрипторов или учётных данных (credentials) о процессе другим процессам, используя вспомогательные (ancillary) данные.

### Формат адреса
Адрес доменного сокета UNIX представляет собой следующую структуру:

```
struct sockaddr_un {

    sa_family_t sun_family;               /* AF_UNIX */

    char        sun_path[108];            /* имя пути */
};
```

Поле sun_family всегда содержит AF_UNIX. В Linux размер sun_path равен 108 байтам; также смотрите ЗАМЕЧАНИЯ ниже.

В различных системных вызовах (например, bind(2), connect(2) и sendto(2)) в качестве входных данных используется параметр sockaddr_un. Другие системные вызовы (например, getsockname(2), getpeername(2), recvfrom(2) и accept(2)) возвращают результат в параметре этого типа.

В sockaddr_un структуре различают три типа адресов:

*

с именем пути: доменный сокет UNIX может быть привязан к имени пути (с завершающимся null) в файловой системе с помощью bind(2). При возврате адреса имени пути сокета (одним и системных вызовов, упомянутых выше), его длина равна

offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1

и sun_path содержит путь, оканчивающийся null (в Linux, указанное выше выражение offsetof() равно sizeof(sa_family_t), но в некоторых реализациях включаются другие поля перед sun_path, поэтому выражение offsetof() описывает размер адресной структуры более переносимым способом).

Дополнительную информацию о путях сокета смотрите далее.

- безымянный: Потоковый сокет, который не привязан к имени пути с помощью bind(), не имеет имени. Аналогично, два сокета, создаваемые socketpair(), также не имеют имён. При возврате адреса сокета его длина равна sizeof(sa_family_t), а значение sun_path не используется.

- абстрактный: абстрактный адрес сокета отличается (от имени пути сокета) тем, что значением sun_path[0] является байт null ('\0'). Адрес сокета в этом пространстве имён определяется дополнительными байтами в sun_path, количество которых определяется длиной указанной структуры адреса. Байты null в имени не имеют специального значения. Имя не связано с именем пути в файловой системе. При возврате адреса абстрактного сокета возвращаемое значение addrlen больше чем sizeof(sa_family_t) (т.е. больше 2), а имя сокета содержится в первых (addrlen - sizeof(sa_family_t)) байтах sun_path.

### Путевые сокеты
При привязке сокета к пути для максимальной переносимости и простоте кодирования нужно учесть несколько правил:

- Имя пути в sun_path должно завершаться null.

- Длина имени пути, включая завершающий байт null, не должна превышать размер sun_path.

- Аргумент addrlen, описывающий включаемую структуру sockaddr_un, должен содержать значение, как минимум:

```
    offsetof(struct sockaddr_un, sun_path)+strlen(addr.sun_path)+1
```

или, проще говоря, для addrlen можно использовать sizeof(struct sockaddr_un).

Есть несколько реализаций по работе с адресами доменных сокетов UNIX, которые не следуют данным правилам. Например, в некоторых реализациях (но не во всех) добавляется конечный null, если если его нет в sun_path.

При написании переносимых приложений учтите, что в некоторых реализациях размер sun_pathравен 92 байтам.

Различные системные вызовы (например, accept(2), recvfrom(2), getsockname(2), getpeername(2)) возвращают адресные структуры сокета. В случае с доменными сокетами UNIX аргумент значение-результат addrlen, передаваемый вызову, должен быть инициализирован как описано выше. При возврате в аргументе содержится реальный размер адресной структуры. Вызывающий должен проверить полученное значение этого аргумента: если оно превышает значение до вызова, то не гарантируется наличие конечного null в sun_path (смотрите ДЕФЕКТЫ).

### Пути к сокетам и права
В реализации Linux учитываются права на каталоги, в которых располагаются сокеты. Создание нового сокета завершается ошибкой, если процесс не имеет права писать или искать (выполнять) в каталог, в котором создаётся сокет.

В Linux для подключения к объекту потокового сокета требуются права на запись в этот сокет; схожим образом, для отправки дейтаграммы в дейтаграммный сокет требуются права на запись в этот сокет. В POSIX ничего не сказано о влиянии прав файла сокета и в некоторых системах (например, в старых BSD) права на сокет игнорируются. Переносимые программы не должны полагаться на это свойство для обеспечения безопасности.

При создании нового сокета владелец и группа файла сокета назначаются согласно обычных правил. К файлу сокета разрешается любой доступ кроме выключенного процессом с помощью umask(2).

Владелец, группа и права доступа пути сокета можно изменять (с помощью chown(2) и chmod(2)).

### Абстрактные сокеты
Права на сокеты не учитываются у абстрактных сокетов: umask(2) процесса не учитывается при подключении к абстрактному сокету как и изменение владельца и прав доступа к объекту (посредством fchown(2) и fchmod(2)) не влияют на доступность сокета.

Абстрактные сокеты автоматически исчезают при закрытии всех открытых ссылок на них.

Пространство имён абстрактных сокетов является непереносимым расширением Linux.

### Параметры сокета
В силу исторических причин эти параметры сокетов относятся к типу SOL_SOCKET, даже если они относятся к AF_UNIX. Они могут быть установлены с помощью setsockopt(2) и прочитаны с помощью getsockopt(2); тип SOL_SOCKET указывается в качестве семейства сокета.

SO_PASSCRED

Разрешает приём учётных данных посылающего процесса в вспомогательном сообщении SCM_CREDENTIALS каждого последующего принятого сообщения. Полученные учётные данные были заданы отправителем с помощью SCM_CREDENTIALS, или имеют значение по умолчанию, которое содержит PID отправителя, фактический пользовательский и групповой ID, если отправитель не задал вспомогательные данные SCM_CREDENTIALS.

Если при включении этого параметра сокет ещё не соединён, то в абстрактном пространстве имён будет автоматически создано уникальное имя.

Значение передаётся в аргументе setsockopt(2) и возвращается в результате getsockopt(2) в виде целочисленного логического флага.

SO_PASSSEC

Разрешает приём метки безопасности SELinux однорангового сокета в вспомогательном сообщении с типом SCM_SECURITY (смотрите ниже).

Значение передаётся в аргументе setsockopt(2) и возвращается в результате getsockopt(2) в виде целочисленного логического флага.

Параметр SO_PASSSEC поддерживается для дейтаграммных доменных сокетов UNIX начиная с Linux 2.6.18; поддержка потоковых доменных сокетов UNIX добавлена в Linux 4.2.

SO_PEEK_OFF

Смотрите socket(7).

SO_PEERCRED

С параметром сокета, доступным только для чтения, возвращаются учётные данные однорангового процесса, соединённого с сокетом. Возвращаются информационные данные, которые были действительными на момент вызова connect(2) или socketpair(2).

Аргументом getsockopt(2) является указатель на структуру ucred; определите макрос тестирования свойств _GNU_SOURCE для получения определения этой структуры из <sys/socket.h>.

Использование этого параметра возможо только для соединённых потоковых сокетов AF_UNIX и потоков AF_UNIX и для дейтаграммных сокетных пар, созданных с помощью socketpair(2).

### Свойство автоматической привязки
Если в вызов bind(2) передано значение addrlen равное sizeof(sa_family_t), или для сокета, который не привязан к адресу явно, был указан параметр сокета SO_PASSCRED, то сокет автоматически привязывается к абстрактному адресу. Адрес состоит из байта null и 5 байтов символов из набора [0-9a-f]. Таким образом, максимальное количество автоматически привязываемых адресов равно 2^20 (в Linux 2.1.15, когда была добавлена автоматическая привязка, использовалось 8 байт, и, таким образом, ограничение было 2^32 адресов. В Linux 2.3.15 количество байт сократили до 5).

### Программный интерфейс сокетов
В следующих параграфах описываются специфичные тонкости доменов и неподдерживаемые возможности программного интерфейса сокетов для доменных сокетов UNIX в Linux.

Доменные сокеты UNIX не поддерживают передачу внеполосных данных (флаг MSG_OOB у send(2) и recv(2)).

Флаг MSG_MORE у send(2) не поддерживается доменными сокетами UNIX.

До Linux 3.4 использование MSG_TRUNC в аргументе flags у recv(2) не поддерживалось доменными сокетами UNIX.

Параметр сокета SO_SNDBUF учитывается в доменных сокетах UNIX, а параметр SO_RCVBUF — нет. Для датаграмных сокетов значение SO_SNDBUF считается максимальным размером для исходящих датаграмм. Это ограничение, вычисляемое как удвоенное значение (см. socket(7)) параметра, содержит меньше 32 байт накладных расходов.

### Вспомогательные сообщения
Вспомогательные данные отправляются и принимаются с помощью sendmsg(2) и recvmsg(2). В силу исторических причин перечисленные типы вспомогательных сообщений относятся к типу SOL_SOCKET, даже если они относятся к AF_UNIX. Для того, чтобы отправить их, установите значение поля cmsg_level структуры cmsghdr равным SOL_SOCKET, а в значении поля cmsg_type укажите его тип. Дополнительная информация приведена в cmsg(3).

SCM_RIGHTS

Передать или принять набор открытых файловых дескрипторов из другого процесса. Часть с данными содержит целочисленный массив файловых дескрипторов.

Обычно, эта операция упоминается как «передача дескриптора файла» другому процессу. Но если точнее, то передается ссылка на открытое файловое описание (смотрите open(2)) и в принимающем процессе будет использоваться, вероятно, файловый дескриптор с другим номером. Семантически, эта операция эквивалентна дублированию (dup(2)) файлового дескриптора в таблицу файловых дескрипторов другого процесса.

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

Если количество файловых дескрипторов, полученных во вспомогательных данных, превышает ограничение ресурса процесса RLIMIT_NOFILE (смотрите getrlimit(2)), то превысившие файловые дескрипторы автоматически закрываются в принимающем процессе.

Константой ядра SCM_MAX_FD задаётся ограничение на количество файловых дескрипторов в массиве. Попытка послать с помощью sendmsg(2) массив превышающий ограничение завершается ошибкой EINVAL. Значение SCM_MAX_FD равно 253 (или 255 в ядрах до версии 2.6.38).

SCM_CREDENTIALS

Передать или принять учётные данные UNIX. Может быть использована для аутентификации. Учётные данные передаются в виде структуры struct ucred вспомогательного сообщения. Эта структура определена в <sys/socket.h> следующим образом:

```
struct ucred {

    pid_t pid;    /* идентификатор посылающего процесса */

    uid_t uid;    /* идентификатор пользователя посылающего процесса */

    gid_t gid;    /* идентификатор группы посылающего процесса */
};

```

Начиная с glibc 2.8, чтобы получить определение данной структуры должен быть определён макрос тестирования свойств _GNU_SOURCE (до включения каких-либо заголовочных файлов).

Учётные данные (credentials), указываемые отправителем, проверяются ядром. Привилегированный процесс может указывать значения, отличные от его собственных. Отправитель должен указать идентификатор своего процесса (если только он не имеет мандата CAP_SYS_ADMIN), свой реальный идентификатор пользователя, действующий идентификатор или сохранённый set-user-ID (если только он не имеет CAP_SETUID) и реальный идентификатор своей группы, действующий идентификатор группы или сохранённый set-group-ID (если только он не имеет CAP_SETGID).

Для получения сообщения со структурой struct ucred у сокета должен быть включён параметр SO_PASSCRED.

SCM_SECURITY

Получить контекст безопасности SELinux (метку безопасности) однорангового сокета. Полученные вспомогательные данные представляют собой строку (с null в конце) с контекстом безопасности. Получатель должен выделить не менее NAME_MAX байт под эти данные в в части данных вспомогательного сообщения.

Для получения контекста безопасности у сокета должен быть включён параметр SO_PASSSEC (смотрите выше).

При отправке вспомогательных данных с помощью sendmsg(2) посылаемое сообщение может содержать только по одному элементу каждого типа, из представленных выше.

По крайней мере один байт реальных данных должен быть отправлен при отправке вспомогательных данных. В Linux это требуется для успешной отправки вспомогательных данных через потоковый доменный сокет UNIX. При отправке вспомогательных данных через дейтаграммный доменный сокет UNIX в Linux необязательно отправлять какие-либо реальные сопровождающие данные. Однако переносимые приложения должны также включать, по крайней мере, один байт реальных данных при отправке вспомогательных данных через дейтаграммный сокет.

При получении из потокового сокета вспомогательные данные формируют своего рода барьер для полученных данных. Например, предположим, что отправитель передает так:

1.

sendmsg(2) отправляет четыре байта без вспомогательных данных.

2.

sendmsg(2) отправляет один байт вспомогательных данных.

3.

sendmsg(2) отправляет четыре байта без вспомогательных данных.

Предположим, что получатель теперь выполняет каждый вызов recvmsg(2) с буфером размером 20 байтов. Первый вызов получит пять байтов данных вместе с вспомогательными данными, отправленными вторым вызовом sendmsg(2). Следующий вызов получит оставшиеся пять байтов данных.

Если место, выделенное для получения входящих вспомогательных данных, слишком маленькое, то вспомогательные данные обрезаются по количеству заголовков, которые влезут в предоставленной буфер (или, в случае списка файловых дескрипторов SCM_RIGHTS, может быть обрезан список файловых дескрипторов). Если для входящих вспомогательных данных буфер не был предусмотрен (т. е., поле msg_control в структуре msghdr, указанное recvmsg(2), равно NULL), то входящие вспомогательные данные отбрасываются. В обоих случаях, в возвращаемом значении recvmsg(2) в msg.msg_flags будет установлен флаг MSG_CTRUNC.

### Вызовы ioctl
Следующие вызовы ioctl(2) возвращают информацию в аргументе value. Корректный синтаксис:

```
int value;
error = ioctl(unix_socket, ioctl_type, &value);
```

Значением ioctl_type может быть:

SIOCINQ

Для сокетов SOCK_STREAM этот вызов возвращает количество непрочитанных данных в приёмном буфере. Сокет не должен быть в состоянии LISTEN, иначе возвращается ошибка (EINVAL). Значение SIOCINQ определено в <linux/sockios.h>. В качестве альтернативы можно использовать синоним FIONREAD, определённый в <sys/ioctl.h>. Для сокетов SOCK_DGRAM возвращаемое значение совпадает с дейтаграммными доменными сокетами Интернета; смотрите udp(7).

## ОШИБКИ
EADDRINUSE

Заданный локальный адрес уже используется, или сокетный объект файловой системы уже существует.

EBADF

Эта ошибка может возникать в sendmsg(2) при отправке файлового дескриптора в вспомогательных данных через доменный сокет UNIX (смотрите описание SCM_RIGHTS выше), и указывает на то, что отправляемый номер файлового дескриптора некорректен (например, не является открытым файловым дескриптором).

ECONNREFUSED

Удалённый адрес, указанный connect(2) не является слушающим сокетом. Эта ошибка также может возникнуть, если путь назначения не является сокетом.

ECONNRESET

Удалённый сокет был неожиданно закрыт.

EFAULT

Некорректный адрес пользовательской памяти.

EINVAL

Передан неправильный аргумент. Основная причина — не задано значение AF_UNIX в поле sun_type передаваемых адресов или сокет находится в некорректном состоянии для производимой операции.

EISCONN

Вызов connect(2) запущен для уже соединённого сокета, или адрес назначения указывает на соединённый сокет.

ENOENT

Путь, указанный в удалённом адресе для connect(2), не существует.

ENOMEM

Не хватает памяти.

ENOTCONN

Для операции над сокетом требуется адрес назначения, а сокет не соединён.

EOPNOTSUPP

Вызвана потоковая операция для не потокового сокета, или произведена попытка использования параметра для внеполосных данных.

EPERM

Отправитель указал неправильную информацию (credentials) в структуре struct ucred.

EPIPE

Удалённый сокет был закрыт в потоковом сокете. Если разрешено, также будет послан сигнал SIGPIPE. Этого можно избежать, передав флаг MSG_NOSIGNAL при вызове send(2) или sendmsg(2).

EPROTONOSUPPORT

Указанный протокол не является AF_UNIX.

EPROTOTYPE

Удалённый сокет не совпадает с типом локального сокета (SOCK_DGRAM против SOCK_STREAM).

ESOCKTNOSUPPORT

Неизвестный тип сокета.

ETOOMANYREFS

Эта ошибка может возникнуть в sendmsg(2) при передаче через доменный сокет UNIX в качестве вспомогательных данных файлового дескриптора (смотрите описание SCM_RIGHTS выше). Это происходит, если количество файловых дескрипторов «в полёте» превышает ограничитель ресурса RLIMIT_NOFILE и вызывающий не имеет мандата CAP_SYS_RESOURCE. Файловым дескриптором в полёте считается посланный с помощью sendmsg(2), но ещё не принятый процессом-получателем с помощью recvmsg(2).

Данная ошибка выявляется начиная с Linux 4.5 (и в некоторых старых версиях, в которые перенесено исправление). В ранних версиях ядра было возможно получить неограниченное количество файловых дескрипторов в полёте, посылая каждый файловый дескриптор с помощью sendmsg(2) и затем закрывая файловый дескриптор, и таким образом он не учитывался в ограничителе ресурса RLIMIT_NOFILE.

При создании сокетного объекта на уровне сокетов или файловой системы могут генерироваться другие ошибки. За дополнительной информацией обращайтесь к соответствующей справочной странице.

## ВЕРСИИ
SCM_CREDENTIALS и абстрактное пространство имён появились в Linux 2.2 и не должны использоваться в переносимых программах. Некоторые клоны BSD также поддерживают передачу дополнительной информации (credential), но методы реализации передачи могут серьезно отличаться на разных системах.

## ЗАМЕЧАНИЯ
Привязка сокета к имени файла создаёт сокет в файловой системе, который должен быть удалён создателем, когда необходимость в нём отпадёт (с помощью unlink(2)). Обычная система ссылок UNIX также подходит для работы с сокетами; сокет может быть удалён в любое время, а реальное удаление из файловой системы будет произведено при закрытии последней на него ссылки.

Для передачи файловых дескрипторов или учётных данных (credentials) через сокет SOCK_STREAM необходимо передать или принять, по меньшей мере, один байт не вспомогательных данных в том же вызове sendmsg(2) или recvmsg(2).

В потоковых доменных сокетах UNIX отсутствует такое понятие как внеполосные данные.

## ДЕФЕКТЫ
При привязке сокета к адресу Linux является одной из реализаций, которые добавляют конечный null, если он отсутствует в sun_path. В большинстве случаев в этом нет проблемы: когда адрес сокета возвращается, он будет на один байт длиннее чем был перед привязкой сокета. Однако такое неожиданное поведение может привести к следующему: если передаётся 108 не-null байтов при привязке сокета, то с дополнительным конечным null пути превышает длину sizeof(sun_path). В последствии при возврате адреса сокета (например, из accept(2)), если входной аргумент addrlen перед вызовом был равен sizeof(struct sockaddr_un), то в sun_path возвращаемой структуры адреса будет отсутствовать конечный null.

Также, некоторые реализации не требуют наличия конечного null при привязке сокета (для определения длины sun_path используется аргумент addrlen) и когда в этих реализациях возвращается адрес сокета, то в sun_path также отсутствует конечный null.

Приложения, которые получают адрес сокета могут содержать код (переносимый) для обработки случая, когда нет конечного null в sun_path, учитывая фактическое количество пригодных байт в пути:

strnlen(addr.sun_path, addrlen - offsetof(sockaddr_un, sun_path))

Или же приложение может перед получением адреса сокета выделить буфер размера sizeof(struct sockaddr_un)+1, который будет обнулён перед возвращением. Возвращающий вызов может задать в addrlen значение sizeof(struct sockaddr_un), и дополнительный нулевой байт здесь будет конечным null в строке, возвращаемой в sun_path:

```
void *addrp;
addrlen = sizeof(struct sockaddr_un);
addrp = malloc(addrlen + 1);
if (addrp == NULL)

    /* обработка ошибки */ ;
memset(addrp, 0, addrlen + 1);
if (getsockname(sfd, (struct sockaddr *) addrp, &addrlen)) == -1)

    /* обработка ошибки */ ;
printf("sun_path = %s\n", ((struct sockaddr_un *) addrp)->sun_path);
```

Данного беспорядка можно избежать, если гарантировать, что приложения, создающие путевые сокеты, следуют правилам, описанным в общих чертах выше в Путевые сокеты.

## ПРИМЕР
В следующем коде демонстрируется использование пакето-упорядочивающих сокетов для локального межпроцессного обмена. Он состоит из двух программ. Программа-сервер ждёт подключения программы-клиента. Клиент посылает свой каждый аргумент командной строки в виде отдельного сообщения. Сервер считает входящие сообщения как целые числа и складывает их. Клиент посылает строку-команду «END». Сервер посылает ответное сообщение, содержащее сумму чисел клиента. Клиент печатает сумму и завершает работу. Сервер ждёт подключение следующего клиента. Для остановки сервера, клиент вызывается с аргументом командной строки «DOWN».

Следующий вывод был записан при работе сервера в фоновом режиме и повторяющемся запуске клиента. Выполнение программы-сервера завершилось после получения им команды «DOWN».

### Пример вывода

```
$ ./server &
[1] 25887
$ ./client 3 4
Результат = 7
$ ./client 11 -5
Результат = 6
$ ./client DOWN
Результат = 0
[1]+  Done                    ./server
$
```

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

```
/*

 * Файл connection.h

 */
#define SOCKET_NAME "/tmp/9Lq7BNBnBycd6nxy.socket"
#define BUFFER_SIZE 12
/*

 * Файл server.c

 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "connection.h"
int
main(int argc, char *argv[])
{

    struct sockaddr_un name;

    int down_flag = 0;

    int ret;

    int connection_socket;

    int data_socket;

    int result;

    char buffer[BUFFER_SIZE];

    /*

     * Удалить сокет, оставшийся после последнего

     * некорректного завершения программы.

     */

    unlink(SOCKET_NAME);

    /* Создание локального сокета. */

    connection_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);

    if (connection_socket == -1) {

        perror("socket");

        exit(EXIT_FAILURE);

    }

    /*

     * Для переносимости очищаем всю структуру, так как в некоторых

     * реализациях имеются дополнительные (нестандартные) поля.

     */

    memset(&name, 0, sizeof(struct sockaddr_un));

    /* Привязываем сокет к имени сокета. */

    name.sun_family = AF_UNIX;

    strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);

    ret = bind(connection_socket, (const struct sockaddr *) &name,

               sizeof(struct sockaddr_un));

    if (ret == -1) {

        perror("bind");

        exit(EXIT_FAILURE);

    }

    /*

     * Готовимся принимать подключения. Размер очереди (backlog)

     * устанавливаем равным 20. Пока один запрос обрабатывается, другие

     * запросы смогут подождать.

     */

    ret = listen(connection_socket, 20);

    if (ret == -1) {

        perror("listen");

        exit(EXIT_FAILURE);

    }

    /* Основной цикл обработки подключений. */

    for (;;) {

        /* Ожидание входящих подключений. */

        data_socket = accept(connection_socket, NULL, NULL);

        if (data_socket == -1) {

            perror("accept");

            exit(EXIT_FAILURE);

        }

        result = 0;

        for (;;) {

            /* Ожидание следующего пакета с данными. */

            ret = read(data_socket, buffer, BUFFER_SIZE);

            if (ret == -1) {

                perror("read");

                exit(EXIT_FAILURE);

            }

            /* Проверяем, что буфер завершается 0. */

            buffer[BUFFER_SIZE - 1] = 0;

            /* Обработка команд. */

            if (!strncmp(buffer, "DOWN", BUFFER_SIZE)) {

                down_flag = 1;

                break;

            }

            if (!strncmp(buffer, "END", BUFFER_SIZE)) {

                break;

            }

            /* Добавляем полученную команду. */

            result += atoi(buffer);

        }

        /* Отправка результата. */

        sprintf(buffer, "%d", result);

        ret = write(data_socket, buffer, BUFFER_SIZE);

        if (ret == -1) {

            perror("write");

            exit(EXIT_FAILURE);

        }

        /* Закрытие сокета. */

        close(data_socket);

        /* Завершаем работу по команде DOWN. */

        if (down_flag) {

            break;

        }

    }

    close(connection_socket);

    /* Удаляем сокет. */

    unlink(SOCKET_NAME);

    exit(EXIT_SUCCESS);
}
/*

 * Файл client.c

 */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "connection.h"
int
main(int argc, char *argv[])
{

    struct sockaddr_un addr;

    int i;

    int ret;

    int data_socket;

    char buffer[BUFFER_SIZE];

    /* Создание локального сокета. */

    data_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);

    if (data_socket == -1) {

        perror("socket");

        exit(EXIT_FAILURE);

    }

    /*

     * Для переносимости очищаем всю структуру, так как в некоторых

     * реализациях имеются дополнительные (нестандартные) поля.

     */

    memset(&addr, 0, sizeof(struct sockaddr_un));

    /* соединяем сокет с адресом сокета */

    addr.sun_family = AF_UNIX;

    strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);

    ret = connect (data_socket, (const struct sockaddr *) &addr,

                   sizeof(struct sockaddr_un));

    if (ret == -1) {

        fprintf(stderr, "The server is down.\n");

        exit(EXIT_FAILURE);

    }

    /* Посылаем аргументы. */

    for (i = 1; i < argc; ++i) {

        ret = write(data_socket, argv[i], strlen(argv[i]) + 1);

        if (ret == -1) {

            perror("write");

            break;

        }

    }

    /* Отправка результата. */

    strcpy (buffer, "END");

    ret = write(data_socket, buffer, strlen(buffer) + 1);

    if (ret == -1) {

        perror("write");

        exit(EXIT_FAILURE);

    }

    /* Получение результата. */

    ret = read(data_socket, buffer, BUFFER_SIZE);

    if (ret == -1) {

        perror("read");

        exit(EXIT_FAILURE);

    }

    /* Проверяем, что буфер завершается 0. */

    buffer[BUFFER_SIZE - 1] = 0;

    printf("Result = %s\n", buffer);

    /* Закрытие сокета. */

    close(data_socket);

    exit(EXIT_SUCCESS);
}
```

Пример использования SCM_RIGHTS приведён в cmsg(3).

## СМОТРИТЕ ТАКЖЕ
recvmsg(2), sendmsg(2), socket(2), socketpair(2), cmsg(3), capabilities(7), credentials(7), socket(7), udp(7)

branch: sisyphus


uri

title: uri(7)

text:
# uri(7)

## ИМЯ
uri, url, urn - единый идентификатор ресурса (URI), содержащий URL или URN

## ОБЗОР

```
URI = [ абсолютный_URI | относительный_URI ] [ "#" фрагмент ]
```

```
абсолютныйURI = схема ":" ( иерархическая_часть | неясная_часть )
```

```
относительныйURI = ( сетевой_путь | абсолютный_путь | относительный_путь ) [ "?" запрос ]
```

```
схема = "http" | "ftp" | "gopher" | "mailto" | "news" | "telnet" |

         "file" | "man" | "info" | "whatis" | "ldap" | "wais" | …
```

```
иерархическая_часть = ( сетевой_путь | абсолютный_путь ) [ "?" запрос ]
```

```
сетевой_путь = "//" полномочия [ абсолютный_путь ]
```

```
абсолютный_путь = "/"  сегменты_пути
```

```
относительный_путь = относительный_сегмент [ абсолютный_путь ]
```

## ОПИСАНИЕ
Единый идентификатор ресурса (Uniform Resource Identifier (URI)) — это короткая строка символов, идентифицирующая абстрактный или физический ресурс (например, веб-страницу). Единый указатель местоположения ресурса (Uniform Resource Locator (URL)) — это URI, который идентифицирует ресурс по основному способу доступа к нему (например, его «местонахождению в сети»), а не по названию или другим атрибутам этого ресурса. Единое название ресурса (Uniform Resource Name (URN)) — это URI, который должен оставаться уникальным и неизменным даже в том случае, когда ресурс уже не существует или недоступен.

URI являются стандартным способом создания конечных адресов гипертекстовых ссылок для таких инструментов как веб-браузеры. Строка «http://www.kernel.org» является URL (а значит и URI). Многие используют термин URL неправильно, как синоним URI (технически, URL являются частью URI).

URI могут быть абсолютными или относительными. Абсолютный идентификатор ссылается на ресурс независимо от контекста, в то время как относительный идентификатор ссылается на ресурс, описывая его относительно текущего контекста. В относительном идентификаторе части пути «.» и «..» имеют специальные значения: «текущий иерархический уровень» и «уровень выше текущего иерархического уровня», соответственно —аналогично UNIX-подобным системам. Часть пути, содержащая двоеточие, не может быть использована как первая часть пути относительного URI (например, «это:то»), потому что это приведёт к ошибке; перед такими частями надо ставить ./ (например, «./это:то»). Заметим, что в производных MS-DOS (например, Microsoft Windows) в URI в именах устройств двоеточие заменено на вертикальную черту («|»), то есть «C:» заменяется на «C|».

Если используется идентификатор фрагмента, то он ссылается на часть ресурса с конкретным названием; фрагментом называют текст после символа «#». URI, начинающийся с «#», ссылается на такой фрагмент в текущем ресурсе.

### Использование
Существует множество различных схем URI, в каждой могут быть свои дополнительные правила и смыслы, но все они создаются максимально похожими друг на друга. Например, многие схемы URL позволяют задавать полномочия в следующем формате, обозначаемом здесь как ip_server (квадратные скобки указывают на необязательность):

ip_server = [пользователь [ : пароль ] @ ] узел [ : порт]

Этот формат позволяет указать имя пользователя, его пароль и/или номер порта. Узел — это имя компьютера-узла; он может быть указан в виде имени DNS или IP-адреса (числа через точку). Таким образом, URI <http://fred:fredpassword@example.com:8080/> определяется подключение к веб-серверу example.com по порту 8080 пользователем fred (с помощью пароля fredpassword). Желательно в целях безопасности не указывать пароль в URI. Если в URL указано имя пользователя, но нет пароля, а серверу этот пароль требуется, то программа, обрабатывающая URL, должна запросить его у пользователя.

Далее приводятся наиболее распространённые схемы, используемые в UNIX-подобных системах. Заметьте, что многие программы, использующие URI, имеют внутренние или специализированные схемы, поэтому советуем прочитать на них документацию.

http — веб-сервер (HTTP)

http://ip_server/путь

http://ip_server/путь?запрос

Это URL для доступа к веб-серверу (HTTP). По умолчанию используется порт 80. Если путь указывает на каталог, то веб-сервер сам выберет, что необходимо вернуть; обычно, если есть файл «index.html» или «index.htm», то возвращается его содержимое, в ином случае возвращается список файлов в каталоге. Пример: <http://lwn.net>.

Запрос может быть задан в устаревшем формате «isindex», состоящим из слова или фразы, и не содержащим знака равенства (=). Запрос может быть также в удлинённом формате «GET», в котором может быть несколько запрашиваемых элементов в виде ключ=значение и разделяемых амперсандом (&). Заметим, что ключ может повторяться, но как это обрабатывать будет решать сам веб-сервер и его прикладная программа. Есть проблемы при работе с HTML/XML/SGML с помощью формата запроса GET: когда в такие URI добавляются несколько ключей, используемый в документах SGML/XML (включая HTML) амперсанд (&) перезаписывается в виде &. Заметим, что не все запросы используют этот формат; огромные формы могут быть слишком большими для записи в виде URI, поэтому они используют другой механизм взаимодействия (называемый POST), в котором данные в URI не включаются. Подробности смотрите в спецификации на Common Gateway Interface http://www.w3.org/CGI.

ftp — протокол передачи файлов (FTP)

ftp://ip_server/путь

Это URL для получения доступа к файлу с помощью протокола передачи файлов FTP. Номер порта (управляющего) по умолчанию равен 21. Если не указано имя пользователя, то пишется слово «anonymous», и в этом случае многие клиентские программы в качестве пароля отправляют адрес электронной почты пользователя. Пример: <ftp://ftp.is.co.za/rfc/rfc1808.txt>.

gopher — сервер Gopher

gopher://ip_server/тип_gopher селектор

gopher://ip_server/тип_gopher селектор%09поиск

gopher://ip_server/тип_gopher селектор%09поиск%09gopher+_строка

По умолчанию служба gopher использует порт 70. тип_gopher — односимвольное поле для указания типа ресурса Gopher, на который ссылается URL. Полный путь может быть пустым; в этом случае знак «/» тоже является необязательным, а значение тип_gopher по умолчанию равно «1».

селектор — это строка селектора в Gopher. В протоколе Gopher строки селектора могут содержать любые байты, кроме шестнадцатеричных 09 (US-ASCII HT или tab), 0A (US-ASCII-символ LF) и 0D (US-ASCII-символ CR).

mailto — адрес электронной почты

mailto:адрес_email

Это адрес электронный почты, обычно имеющий форму имя_пользователя@имя_узла. Для получения более подробной информации о формате адресов электронной почты смотрите mailaddr(7). Заметим, что любой символ % должен записываться как %25. Пример: <mailto:dwheeler@dwheeler.com>.

news — сообщение или группа новостей

news:newsgroup-name

news:message-id

В newsgroup-name задаётся иерархическое имя, разделённое точками, например, «comp.infosystems.www.misc». Если значение <newsgroup-name> равно «*» (записывается как <news:*>), то такая запись используется для указания «всех доступных групп новостей». Пример: <news:comp.lang.ada>.

Значение message-id соответствует Message-ID из IETF RFC 1036, без крайних «<» и «>»; оно имеет вид уникальная_часть@полное_доменное_имя. Идентификатор сообщения можно отличить от названия группы новостей по находящемуся в названии символу «@».

telnet — удалённый вход

telnet://ip_server/

Схема URL для Telnet используется для определения интерактивных текстовых служб, которые доступны по протоколу Telnet. Последний символ «/» может быть опущен. По умолчанию используется порт 23. Пример: <telnet://melvyl.ucop.edu/>.

file — обычный файл

file://ip_server/сегменты_пути

file:сегменты_пути

Представляет файл или каталог, доступный локально. Исключение: значение ip_server может быть пустым или равно строке «localhost»; это означает: «машина, с которой обращаются по URL». Если путь указывает на каталог, то программа просмотра представит содержимое каталога с ссылками на каждый элемент, что выполняется не всеми просмотрщиками. KDE поддерживает генерируемые файлы с помощью URL <file:/cgi-bin>. Если указанный файл не найден, то обозреватель может попробовать расширить имя файла при помощи функций охвата (смотрите glob(7) и glob(3)).

Второй формат (например, <file:/etc/passwd>) существует для ссылки на локальные файлы. Однако, в старых стандартах этот формат не допускался и некоторые программы не воспринимают его как URI. Более переносим следующий синтаксис: пустая строка вместо имени сервера, например, <file:///etc/passwd>; эта форма имеет тот же смысл, легко распознаётся определителями по шаблону и старыми программами как URI. Заметим, что если вы на самом деле хотите «начать с текущего места», то не используйте такую схему вообще; рекомендуется использовать относительный адрес, такой как, <../test.txt>, который имеет побочный эффект: он не зависит от схемы. Пример такой схемы: <file:///etc/passwd>.

man — справочная страница man

man:имя_команды

man:имя_команды(раздел)

Это ссылка на локальные справочные страницы (man). За именем команды может следовать номер раздела в круглых скобках; для получения более подробной информации о разделах прочтите man(7). Схемы URI в UNIX-подобных системах (например, Linux) различаются и до сих пор не зарегистрированы в IETF. Пример: <man:ls(1)>.

info — страница документации info

info:виртуальное_имя_файла

info:виртуальное_имя_файла#имя_узла

info:(виртуальное_имя_файла)

info:(виртуальное_имя_файла)имя_узла

Эта схема ссылается на справочные страницы info (созданные из файлов texinfo); данный формат документации используется в инструментах GNU. Схемы URI в UNIX-подобных системах (например, Linux) различаются и до сих пор не зарегистрированы в IETF. На момент написания синтаксисы URI у GNOME и KDE различались, поэтому они не понимают синтаксис друг друга. Первые два формата — GNOME: в именах узлов все пробелы заменены подчеркиванием. Следующие два формата — KDE: пробелы в названиях узлов остаются пробелами, даже если это запрещено стандартами URI. Это делалось в надежде на то, что в будущем все программы будут принимать все эти форматы и будут считать подчеркивания как пробелы. В GNOME и KDE, если используется форма без названия узла, то названием по умолчанию считается «Top». Пример формата GNOME: <info:gcc> и <info:gcc#G++_and_GCC>. Пример формата КDE: <info:(gcc)> и <info:(gcc)G++ and GCC>.

whatis — поиск документации

whatis:строка

Эта схема применяет для поиска в базе однострочных описаний команд и возвращает список описаний, содержащих искомую строку. Возвращаются только полные совпадения. За более полной информацией обращайтесь к whatis(1). Данная схема URI в UNIX-подобных системах (например, Linux) различается и до сих пор не зарегистрирована IETF.

ghelp — справочная документация GNOME

ghelp:название_приложения

Схема загружает справку GNOME для заданного приложения. Замечание: пока в документации описаны не все приложения в этом формате.

ldap — простой протокол доступа к каталогам (Lightweight Directory Access Protocol)

ldap://узел_порт

ldap://узел_порт/

ldap://узел_порт/dn

ldap://узел_порт/dn?атрибуты

ldap://узел_порт/dn?атрибуты?область

ldap://узел_порт/dn?атрибуты?область?фильтр

ldap://узел_порт/dn?атрибуты?область?фильтр?расширения

Эта схема поддерживает запросы, направляемые по протоколу LDAP одному или нескольким серверам для получения иерархически упорядоченной информации (например, о людях или ресурсах компьютеров). Подробности о схеме LDAP URL смотрите в RFC 2255. Компоненты этого URL:

узел_порт

запрашиваемый сервер LDAP, записывается как имя машины, двоеточие, номер порта (необязательно). По умолчанию в LDAP используется TCP-порт 389. Если компонент не указан, то клиент сам определяет, какой из LDAP-серверов запрашивать.

dn

отличительное имя LDAP, которое определяет базовый объект поиска LDAP (смотрите RFC 2253 раздел 3).

атрибуты

возвращаемый список атрибутов, разделённых запятой; смотрите RFC 2251, раздел 4.1.5. Если данный компонент не задан, то возвращаются все атрибуты.

область

область поиска, значениями могут быть: «base» (для поиска по базовому объекту), «one» (для поиска на одном уровне) или «sub» (для поиска по ветвям). Если область поиска не указывается, то по умолчанию используется «base».

фильтр

фильтр поиска (набор возвращаемых записей). Если компонент не задан, то возвращаются все записи. Смотрите RFC 2254 раздел 4.

расширения

список пар тип=значение (через запятую), где часть =значение (в случаях, когда это не требуется) может не употребляться. Расширение с приставкой «!» обязательно; если этот значок отсутствует, то необязательно.

Приведём примеры запросов LDAP. Запрос к ldap.itd.umich.edu информации о Мичиганском университете США:

```
ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US
```

Для того, чтобы получить атрибут почтового адреса, введите запрос:

```
ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress
```

Для того, чтобы запросить у host.com (порт 6666) информацию о человеке по имени (cn) «Babs Jensen» в Мичиганском университете, введите строку:

```
ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)
```

wais — глобальная сеть информационных серверов (Wide Area Information Servers)

wais://узел_порт/база_данных

wais://узел_порт/база_данных?поиск

wais://узел_порт/база_данных/wtype/wpath

Эта схема предназначена для базы данных WAIS, поиска или документа (более подробную информацию о WAIS смотрите IETF RFC 1625 ). Узел_порт — это название машины, в некоторых случаях сопровождающееся номером порта (после двоеточия). Используемый порт по умолчанию — 210.

Первая форма определяет базу данных WAIS для поиска. Вторая форма — это определённой поиск в базе данных WAIS. Третья форма предназначена для поиска конкретного документа в базе данных WAIS. Значение wtype служит для обозначения объекта в WAIS, а wpath — это document-id WAIS.

другие схемы

Существует множество других схем URI. Большинство программ, использующих URI, поддерживают свои собственные схемы URI (например, Mozilla имеет схему about: для работы с внутренней информацией; браузер справки GNOME имеет схему toc: для работы с разными разделами). Есть схемы, которые в данный момент не распространены (например, prospero). Схема news: предпочтительнее схемы nntp:. URN поддерживаются схемой urn: (например, urn:ietf:… означает документы IETF); в настоящее время URN широко не используются. Не все приложения поддерживают все схемы.

### Кодирование символов
В URI используется ограниченный набор символов с учётом того, чтобы их можно было набирать в различных ситуациях.

Следующие символы зарегистрированы, то есть они могут появляться в URI, но только в определённых для них целях (эти символы в данных должны быть экранированы перед созданием URI):

; / ? : @ & = + $ ,

Незарезервированные символы могут быть использованы в URI. Незарезервированные символы — это английские буквы верхнего и нижнего регистров, цифры и следующий набор знаков пунктуации и символов:

- _ . ! ~ * ' ( )

Все остальные символы должны экранироваться. Экранированный октет кодируется тремя символами: символом процента «%» и двумя шестнадцатеричными цифрами, представляющими код октета (для ввода шестнадцатеричных цифр можно использовать буквы верхнего и нижнего регистров). Например, пробел обозначается как «%20», символ табуляции (tab) обозначается как «%09», а «&» как «%26». Так как символ «%» зарезервирован, он всегда обозначаться только как «%25». Обычно в запросах пробельные символы заменяют знаком плюс (+); этот способ не определён в RFC (где рекомендуется использовать %20), но любое приложение, принимающее запросы URI, должно его воспринимать. URI всегда показываются в своём «экранированном» виде.

Незарезервированные символы можно экранировать без изменения семантики URI, но это нельзя делать в случаях, когда URI не используется в контексте, не допускающем появления неэкранированных символов. Например, «%7e» иногда используется вместо «~» в пути HTTP URL, но обе формы воспринимаются в HTTP URL одинаково.

Для URI, обрабатывающих символы, не входящих в набор US-ASCII, в главе В.2 документации по HTML 4.01 и в главе 2.2.5 IETF RFC 2718 приводится рекомендуемое рациональное решение:

1.

Переводить последовательность символов в кодировку UTF-8 (IETF RFC 2279) — смотрите utf-8(7) — затем

2.

использовать механизм экранирования URI, то есть, использовать конвертацию %HH для небезопасных октетов.

### Запись URI
При записи URI должны помещаться в двойные кавычки (например, "http://www.kernel.org"), закрываться в угловые скобки (например, <http://lwn.net>) или помещаться на отдельной строке. Предупреждение для использующих двойные кавычки: никогда не помещайте лишнюю пунктуацию (например, точку в конце фразы или запятую в списке) внутрь URI, так как они изменят значение URI. Вместо этого используйте угловые скобки, или перейдите к системе цитирования, в которой никогда не встречаются дополнительные символы внутри цитат. В качестве такой системы можно предложить «новую» или «логическую» систему цитирования от «Hart's Rules» и «Oxford Dictionary for Writers and Editors», используемую в Великобритании и хакерами по всему миру (смотрите раздел Jargon File в Hacker Writing Style, http://www.fwi.uva.nl/~mes/jargon/h/HackerWritingStyle.html ). Старые документы предполагают вставку префикса «URL:» сразу перед URI, но такая форма никогда не была популярной.

Синтаксис URI разрабатывался как однозначный. Однако когда URI стали использовать повсеместно, традиционные средства массовой информации (телевидение, радио, газеты и т.д.) стали применять сокращённые ссылки URI, состоящие из названия домена и пути к конкретному ресурсу (например, <www.w3.org/Addressing>). Такие ссылки больше предназначены для человеческого восприятия, а не для считывания машиной в предположении, что для полного понимания URI будет достаточно контекста применения (например, имена узлов, начинающиеся с «www», скорее всего, имеют в URI префикс «http://», а имена узлов, начинающиеся с «ftp», скорее всего, имеют префикс «ftp://»). Много реализаций клиентов «додумывают» такие ссылки. Такие предположения могут иногда меняться, в частности при появлении новых схем. Так как сокращённые URI имеют тот же синтаксис, что и путь относительных URL, сокращённые URI не могут быть использованы в тех случаях, где используются относительные URI; они могут использоваться только когда нет определённой основы (например, в диалоговых окнах). Не используйте сокращённые URI как гипертекстовые ссылки в документах, используйте стандартный формат, как описано в данном документе.

## СООТВЕТСТВИЕ СТАНДАРТАМ
(IETF RFC 2396), (HTML 4.0).

## ЗАМЕЧАНИЯ
Любое приложение, использующее URI (например, веб-браузер) в Linux, должно поддерживать (непосредственно или косвенно) все схемы, описанные здесь, включая схемы man: и info:. Для обработки предлагается вызывать стороннюю программу.

Технически, фрагмент не является частью URI.

Для получения информации о том, как включить URI (включая URL) в формат данных, прочтите соответствующую документацию. HTML использует формат <A HREF="uri"> text </A>. Файлы texinfo используют формат @uref{uri}. Man и mdoc имеют недавно добавленный макрос UR или просто включают URI в текст (программы просмотра должны распознавать, что :// это часть URI).

В настоящее время окружения рабочего стола GNOME и KDE используют различные URI, в частности в своих справочных системах. Для справочных страниц в GNOME используется <toc:man>, а в KDE — <man:(index)>; для страниц info в GNOME используется <toc:info>, а в KDE — <info:(dir)> (автору данной справочной страницы нравится выбор KDE, так как он больше похож на обычно используемый формат). В общем случае, в KDE используется <file:/cgi-bin/> в качестве приставки к набору генерируемых файлов. В KDE предпочитают использовать документацию в HTML, доступную как <file:/cgi-bin/helpindex>. В GNOME для хранения и поиска документации предпочитают использовать схему ghelp. Ни один обозреватель в момент написания этого документа не поддерживал схему ссылок на каталоги file:, что затрудняет ссылку на весь каталог с помощью просматриваемого URI. Как говорилось ранее, эти окружения различаются по способу поддержки схемы info:. В перспективе GNOME и KDE должны прийти к единому формату URI, и будущая версия системы войдёт в эту справочную страницу. Помогите достичь этого единства.

### Безопасность
Сам по себе URI не создаёт никакой угрозы безопасности. Но нет никакой гарантии, что URL, который однажды указывал на конкретный ресурс, в дальнейшем будет делать то же самое. Так же нет никаких гарантий, что этот же URL позднее не будет ссылаться на совершенно другой ресурс. Такие гарантии можно получить лишь от лица, ответственного за эти ресурсы и их пространство имён.

Существует возможность составить URL таким образом, что попытка произвести обыкновенную, «безобидную» операцию, такую, как получение информации с определённого ресурса, возможно, вызовет сбой в её выполнении. Небезопасный URL, обычно, создаётся следующим образом: в запросе указывается номер порта, не совпадающий с номером зарезервированного порта сетевого протокола. Клиент неосознанно заходит на сайт, но используя другой протокол. В URL содержатся команды, которые при их считывании (в соответствии с другим протоколом) производят непредвиденную операцию. Примером может послужить использование gopher URL для создания ненужного и безадресного сообщения, которое отправится через сервер SMTP.

Нужно быть осторожным с URL, в которых указан номер порта, отличный от стандартного порта для протокола, в особенности, если этот номер находится в зарезервированной области.

Надо быть очень внимательным и в тех случаях, когда URI содержит экранированные разделители протокола (например, символы CR и LF для протокола telnet), с которых экранирование не будет снято перед передачей. Это могло бы нарушить протокол, но само отключение возможностей таких символов может вызвать возникновение дополнительных действий этого протокола, которые могут привести к выполнению непредвиденных и не совсем безобидных операций.

Также довольно не предусмотрительно использовать пароль в URI. В частности, рекомендуется использовать пароль внутри URI-компонента «userinfo», за исключением редких случаев, когда параметр «password» может быть показан всем.

## ДЕФЕКТЫ
Документация может быть расположена в различных местах, поэтому не существует схемы URI для просмотра документации в различных форматах. Ссылки типа <file:///usr/doc/ZZZ> не работают по причине различных требований дистрибутивов и установочных требований, согласно которым документация может быть размещена в разных каталогах (она может быть в /usr/doc, /usr/local/doc, /usr/share или где-нибудь ещё). К тому же, содержимое каталога ZZZ, обычно, изменяется с каждой версией (хотя, частично это можно решить использованием шаблонов имён файлов). А людям, которые получают документацию по Интернет (вместо того, чтобы хранить её в своей системе), схема file: вообще ничем не будет полезна. В будущем может быть добавлена новая схема URI (например, «userdoc:»), которая позволит программам включать перекрёстные ссылки на дополнительную документацию и, при этом, не учитывать точное расположение такой документации. Или же в новой версии файловой системы можно будет указывать расположения файлов так, что можно будет находить документацию прямо по схеме file:.

Многие программы и форматы файлов не имеют возможности включать ссылки с помощью URI.

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

## СМОТРИТЕ ТАКЖЕ
lynx(1), man2html(1), mailaddr(7), utf-8(7)

IETF RFC 2255


user-keyring

title: user-keyring(7)

summary: пользовательская связка ключей

text:
# user-keyring(7)

## ИМЯ
user-keyring - пользовательская связка ключей

## ОПИСАНИЕ
Пользовательская связка ключей используется для хранения ключей пользователя. Для каждого UID у ядра есть своя связка ключей пользователя, которая доступна из всех процессов с этим UID. Пользовательская связка ключей имеет имя (описание) в виде _uid.<UID>, где <UID> идентификатор пользователя соответствующего пользователя.

Пользовательская связка ключей связана с записью, которая обслуживается ядром для UID. Она начинает существовать при попытке первого обращения к пользовательской связке ключей, к user-session-keyring(7) или к session-keyring(7). Связка ключей существует привязанной всё время пока выполняются процессы с этим реальным UID или остаются открытыми файлы, открытые этими процессами (связка ключей также может быть привязана бесконечно, если её прицепить к другой связке ключей).

Обычно, пользовательская связка ключей создаётся pam_keyinit(8) при входе пользователя в систему.

По умолчанию в пользовательской связке ключей вызов request_key(2) не производит поиск. Когда pam_keyinit(8) создаёт связку ключей сеанса, он добавляет в неё пользовательскую связку ключей, и таким образом поиск выполняется в пользовательской связке ключей, если происходит поиск в связке ключей сеанса.

Существует специальный серийный номер, KEY_SPEC_USER_KEYRING, который можно указывать вместо реального серийного номера пользовательской связки ключей вызывающего процесса.

В утилите keyctl(1) подобным образом можно использовать «@u» вместо числового идентификатора ключа.

Пользовательские связки ключей не зависят от clone(2), fork(2), vfork(2), execve(2) и _exit(2) за исключением того, что эта связка ключей уничтожается когда запись UID уничтожается при завершении последнего привязанного процесса.

Если нужно, чтобы ключ, связанный с пользователем, существовал и после сбора мусорщиком записи UID — например, для сценария cron(8) — то должна быть использована persistent-keyring(7).

Если связка ключей пользователя не существует на момент доступа, то она создаётся.

## СМОТРИТЕ ТАКЖЕ
keyctl(1), keyctl(3), keyrings(7), persistent-keyring(7), process-keyring(7), session-keyring(7), thread-keyring(7), user-session-keyring(7), pam_keyinit(8)


user-session-keyring

title: user-session-keyring(7)

text:
# user-session-keyring(7)

## ИМЯ
user-session-keyring - пользовательская сеансовая связка ключей по умолчанию

## ОПИСАНИЕ
Пользовательская сеансовая связка ключей используется для хранения ключей пользователя. Для каждого UID у ядра есть своя пользовательская сеансовая связка ключей, которая доступна из всех процессов с этим UID. Пользовательская сеансовая связка ключей имеет имя (описание) в виде _uid_ses.<UID>, где <UID> идентификатор пользователя соответствующего пользователя.

Пользовательская сеансовая связка ключей связана с записью, которая обслуживается ядром для UID. Она начинает существовать при попытке первого обращения к пользовательской сеансовой связке ключей, к user-keyring(7) или к session-keyring(7). Связка ключей существует привязанной всё время пока выполняются процессы с этим реальным UID или остаются открытыми файлы, открытые этими процессами (связка ключей также может быть привязана бесконечно, если её прицепить к другой связке ключей).

Пользовательская сеансовая связка ключей создаётся когда этого запросит нить или когда нить запросит свою session-keyring(7) и эта связка ключей не существует. В последнем случае, пользовательская сеансовая связка ключей будет создана и, если сеансовая связка ключей не была создана, то пользовательская сеансовая связка ключей будет использована как действующая сеансовая связка ключей процесса.

В пользовательской сеансовой связке ключей выполняется поиск request_key(2), если не существует реальная связка ключей сеанса и не выполняется в противном случае.

Существует специальный серийный номер, KEY_SPEC_USER_SESSION_KEYRING, который можно указывать вместо реального серийного номера пользовательской связки ключей сеанса вызывающего процесса.

В утилите keyctl(1) подобным образом можно использовать «@us» вместо числового идентификатора ключа.

Пользовательские сеансовые связки ключей не зависят от clone(2), fork(2), vfork(2), execve(2) и _exit(2) за исключением того, что эта связка ключей уничтожается когда запись UID уничтожается при завершении последнего привязанного процесса.

Если сеансовая связка ключей пользователя не существует на момент доступа, то она создаётся.

Лучше не полагаться на пользовательскую сеансовую связку ключей; настоятельно рекомендуется, особенно если процесс выполняется с правами суперпользователя, задавать session-keyring(7) явным образом, например с помощью pam_keyinit(8).

## ЗАМЕЧАНИЯ
Поддержка пользовательской сеансовой связки ключей была добавлена для ситуаций, когда процесс не имеет сеансовой связки ключей, возможно из-за того, что был создан способом, не включающим PAM (например, служба, запущенная inetd(8)). В этом случае, пользовательская сеансовая связка ключей подставляется вместо session-keyring(7).

## СМОТРИТЕ ТАКЖЕ
keyctl(1), keyctl(3), keyrings(7), persistent-keyring(7), process-keyring(7), session-keyring(7), thread-keyring(7), user-keyring(7)


title: user_namespaces(7)

text:
# user_namespaces(7)

## ИМЯ
user_namespaces - обзор пользовательских пространств имён Linux

## ОПИСАНИЕ
Обзор пространств имён смотрите в namespaces(7).

Пользовательские пространства имён изолируют идентификаторы и атрибуты безопасности, в частности ID пользователя и ID группы (смотрите credentials(7)), корневой каталог, ключи (смотрите keyrings(7)) и мандаты (смотрите capabilities(7)). Идентификаторы пользователя и группы процесса могут отличаться внутри и снаружи пользовательского пространства имён. В частности, процесс может иметь обычный бесправный пользовательский ID снаружи и ID равный 0 внутри пространства имён; другими словами, процесс имеет доступ ко всем операциям внутри пользовательского пространства имён, но не имеет доступа к привилегированным операциям вне пространства имён.

### Вложенные пространства имён, членство пространств имён
Пользовательские пространства имён могут быть вложенными, то есть каждое пользовательское пространство имён — за исключением первого («корневого») — имеет родительское пространство имён и может иметь ноль или более дочерних пространств имён. Родительское пространство имён — это пользовательское пространство имён процесса, которое создаётся с помощью вызова unshare(2) или clone(2) с флагом CLONE_NEWUSER.

Ядро ограничивает (начиная с версии 3.11) глубину вложенности пользовательских пространств имён 32 уровнями. Вызовы unshare(2) или clone(2), которые бы превысили это ограничение, завершаются с ошибкой EUSERS.

Каждый процесс является членом только одного пользовательского пространства имён. Процесс, созданный с помощью fork(2) или clone(2) без флага CLONE_NEWUSER, является членом того же пользовательского пространства имён что и его родитель. Однонитевой процесс может перейти в другое пользовательское пространство имён с помощью setns(2), если в этом пространстве у него есть мандат CAP_SYS_ADMIN; после перехода он получает полный набор мандатов в этом пространстве имён.

Вызов clone(2) или unshare(2) с флагом CLONE_NEWUSER делает новый дочерний (для clone(2)) или вызвавший (для unshare(2)) процесс членом нового пользовательского пространства имён, создаваемого вызовом.

Операцию NS_GET_PARENT ioctl(2) можно использовать для обнаружения родительской связи между пространствами имён пользователя; смотрите ioctl_ns(2).

### Мандаты
Дочерний процесс, созданный clone(2) с флагом CLONE_NEWUSER, запускается в новом пользовательском пространстве имён с полным набором мандатов. Аналогично, процесс, создающий новое пользовательское пространство имён с помощью unshare(2) или переходящий в существующее пользовательское пространство имён с помощью setns(2), получает полный набор мандатов в этом пространстве имён. С другой стороны, этот процесс не имеет мандатов в родительском (в случае clone(2)) или предыдущем (в случае unshare(2) и setns(2)) пользовательском пространстве имён, даже если новое пространство имён создано или переход осуществлялся суперпользователем (т. е., процесс с ID пользователя 0 в корневом пространстве имён).

Заметим, что вызов execve(2) приводит к пересчёту мандатов процесса обычным порядком (смотрите capabilities(7)). Следовательно, если ID пользователя процесс не равно 0 внутри пространства имён или исполняемый файл имеет непустую маску наследования мандатов, то процесс теряет все мандаты. Смотрите описание отображения пользовательских и групповых ID далее.

Вызов clone(2), unshare(2) или setns(2) с флагом CLONE_NEWUSER устанавливает флаги «securebits» (смотрите capabilities(7)) в их значения по умолчанию (все флаги сброшены) в потомке (для clone(2)) или вызывающем (для unshare(2) или setns(2)). Заметим, что из-то того, что вызывающий больше не имеет мандатов в своём первоначальном пользовательском пространстве имён после вызова setns(2), невозможно у процесса сбросить его флаги «securebits», хотя удержать своё членство в пользовательском пространстве имён можно с помощью пары вызовов setns(2) — сначала переместиться в другое пользовательское пространство имён и затем вернуться в своё первоначальное пользовательское пространство имён.

Правила определения наличия мандата у процесса в определённом пользовательском пространстве имён следующие:

1.

Процесс имеет мандат внутри пользовательского пространства имён, если он является членом этого пространства имён и имеет мандат в своём наборе эффективных мандатов. Процесс может получить мандаты в своём наборе эффективных мандатов различными способами. Например, он может запустить программу с битом set-user-ID или исполняемый файл, имеющий мандаты файла. Также процесс может получить мандаты при выполнении clone(2), unshare(2) или setns(2), как описывалось ранее.

2.

Если процесс имеет мандат в пользовательском пространстве имён, то он также имеет этот мандат во всех дочерних (и позже удалённых потомках) пространствах имён.

3.

При создании пользовательского пространства имён ядро записывает эффективный пользовательский ID создающего процесса как «владельца» пространства имён. Процесс, располагающийся в родительском пространстве имён пользовательского пространства имён и чей эффективный пользовательский ID совпадает с владельцем пространства имён, имеет все мандаты в пространстве имён. Предыдущее правило означает, что у процесса также есть все мандаты во всех в последствии удалённых потомках пользовательских пространств имён. Для обнаружения идентификатора пользователя-владельца пространства имён можно использовать операцию NS_GET_OWNER_UID вызова ioctl(2); смотрите ioctl_ns(2).

### Влияние мандатов внутри пространства имён пользователя
Наличие мандата внутри пространства имён пользователя разрешает процессу выполнять операции (требующие привилегий) с ресурсами, управляемыми только этим пространством имён. Иначе говоря, наличие мандата в пользовательском пространстве имён разрешает процессу выполнять привилегированные операции с ресурсами, которые управляются (не пользовательскими) пространствами имён, принадлежащими (связанными с) пространству имён пользователя (смотрите следующий подраздел).

С другой стороны, существует много привилегированных операций, которые влияют на ресурсы не связанные с каким-либо типом пространства имён, например, изменение системного времени (регулируется CAP_SYS_TIME), загрузка модуля ядра (регулируется CAP_SYS_MODULE) и создание устройства (регулируется CAP_MKNOD). Такие операции может выполнять только процесс с привилегиями в начальном пользовательском пространстве имён.

Наличие CAP_SYS_ADMIN внутри пользовательского пространства имён, принадлежащему пространству имён монтирования процесса, позволяет этому процессу создавать привязки монтирования и монтировать следующие типы файловых систем:

- /proc (начиная с Linux 3.8)

- /sys (начиная с Linux 3.8)

- devpts (начиная с Linux 3.9)

- tmpfs(5) (начиная с Linux 3.9)

- ramfs (начиная с Linux 3.9)

- mqueue (начиная с Linux 3.9)

- bpf (начиная с Linux 4.4)

Наличие CAP_SYS_ADMIN внутри пользовательского пространства имён, принадлежащему пространству имён cgroup процесса, позволяет (начиная с Linux 4.6) этому процессу монтировать именованные иерархии файловой системы cgroup версии 2 и cgroup версии 1 (т. е., файловые системы cgroup, монтируемые с параметром "none,name=").

Наличие CAP_SYS_ADMIN внутри пользовательского пространства имён, принадлежащему пространству имён PID процесса, позволяет (начиная с Linux 3.8) этому процессу монтировать файловые системы /proc.

Однако заметим, что монтирование блочных файловых систем может производиться только процессом, имеющим CAP_SYS_ADMIN в начальном пространстве имён пользователя.

### Взаимодействие между пользовательскими и другими типами пространств имён
Начиная с Linux 3.8, непривилегированные процессы могут создавать пользовательские пространства имён, а для создания пространств имён других типов требуется мандат CAP_SYS_ADMIN в пользовательском пространстве имён вызывающего.

После создания не пользовательского пространства имён оно принадлежит пользовательскому пространству имён, в котором на момент создания пространства имён создающий процесс являлся членом. Для привилегированных операций над ресурсами, управляемыми не пользовательским пространством имён, от процесса требуется иметь мандаты в пользовательском пространстве имён, которому принадлежит не пользовательское пространство имён.

Если вместе с флагами CLONE_NEW* указан флаг CLONE_NEWUSER в вызове clone(2) или unshare(2), то пользовательское пространство имён гарантированно создаётся первым, давая потомку (clone(2)) или вызывающему (unshare(2)) права на остальные пространства имён, создаваемые вызовом. Даже бесправный вызывающий может задать такую комбинацию флагов.

При создании нового пространства имён (не пользовательского пространства имён) посредством clone(2) или unshare(2), ядро записывает пользовательское пространство имён создающего процесса как владельца нового пространства имён (эту связь нельзя изменить). Когда процесс в новом пространстве имён в дальнейшем выполняет привилегированные операции, которые работают с глобальными ресурсами, изолированными пространством имён, выполняется проверка прав согласно мандатам процесса в пользовательском пространстве имён, которое ядро связало с новым пространством имён. Например, предположим, что процесс пытается изменить имя узла (sethostname(2)) — ресурс, управляемый пространство имён UTS. В этом случае, ядро будет искать пространство имён пользователя, принадлежащее пространству имён UTS процесса, и проверять что процесс имеет необходимый мандат (CAP_SYS_ADMIN) в этом пространстве имён пользователя.

Операцию NS_GET_USERNS ioctl(2) можно использовать для обнаружения пространства имён пользователя, которое владеет не пользовательским пространством имён; смотрите ioctl_ns(2).

### Отображение идентификаторов пользователей и групп: uid_map и gid_map
В новом созданном пользовательском пространстве имён отсутствует отображение пользовательских ID (ID групп) в родительское пользовательское пространство. Файл /proc/[pid]/uid_map и /proc/[pid]/gid_map (доступны начиная с Linux 3.5) предоставляют отображения пользовательских и групповых ID внутри пользовательского пространства имён для процесса pid. Эти файлы можно читать для просмотра отображений в пользовательском пространстве имён и писать (однократно) для определения отображений.

В следующих параграфах объясняется формат uid_map; gid_map имеет тот же формат, но каждый экземпляр «ID пользователя» заменяется на «ID группы».

Файл uid_map предоставляет отображение пользовательских ID из пользовательского пространства имён процесса pid в пользовательское пространство имён процесса, который открыл uid_map (но смотрите уточнение далее). Другими словами, процессы, которые находятся в разных пользовательских пространствах имён, возможно будут видеть разные значения при чтении соответствующего файла uid_map, в зависимости от отображений пользовательских ID у пользовательских пространств имён читающего процесса.

Каждая строка в файле uid_map определяет отображение 1-в-1 непрерывного диапазона пользовательских ID между двумя пользовательскими пространствами имён (при создании пользовательского пространства имён этот файл пуст). В каждой строке содержится три числа через пробел. Первые два числа определяют начальный пользовательский ID в каждом из двух пользовательских пространств имён. Третье число определяет длину отображаемого диапазона. Эти поля рассматриваются так:

(1)

Начало диапазона пользовательских ID в пользовательском пространстве имён процесса pid.

(2)

Начало диапазона пользовательских ID, на который отображаются пользовательские ID, указанные в первом поле. Интерпретация второго поля зависит от того, находится ли процесс, открывший uid_map, и процесс pid, в одном пользовательском пространстве имён:

а)

Если два процесса находятся в разных пользовательских пространствах имён: поле два — начало диапазона пользовательских ID в пользовательском пространстве имён процесса, который открыл uid_map.

б)

Если два процесса находятся в одном пользовательском пространстве имён: поле два — начало диапазона пользовательских ID в родительском пользовательском пространстве имён процесса pid. Это позволяет открывшему uid_map (обычно открывают /proc/self/uid_map) видеть отображение пользовательских ID в пользовательском пространстве имён процесса, создавшего это пользовательское пространство имён.

(3)

Длина диапазона пользовательских ID, выполняющего отображение между двумя пользовательскими пространствами имён.

Системные вызовы, возвращающие пользовательские ID (ID групп), например, getuid(2), getgid(2), и мандатные поля в структуре, возвращаемой stat(2), возвращают пользовательский ID (ID группы), отображённый в пользовательском пространстве имён вызывающего.

Когда процесс обращается к файлу, его ID пользователя и группы отображаются в начальном пользовательском пространстве имён с целью проверки прав доступа и назначенного ID при создании файла. Когда процесс получает ID пользователя и группы файла через stat(2), то ID отображаются в обратном направлении, для создания значений, относительно отображений ID пользователя и группы процесса.

Начальное пользовательское пространство имён не имеет родительского пространства имён, но для однородности, для него ядро предоставляет фиктивные файлы отображения ID пользователей и групп. Посмотрим на файл uid_map (в gid_map тоже самое) из оболочки в начальном пространстве имён:

```
$ cat /proc/$$/uid_map

         0          0 4294967295
```

Данное отображение показывает, что диапазон начинающийся с пользовательского ID 0 в этом пространстве имён, отображается в диапазон, начинающийся, с 0, в (несуществующее) родительское пространство имён, и длина диапазона равна самому большому 32-битному беззнаковому целому. Значение 4294967295 (32-битное знаковое значение -1) оставлено без отображения. Предназначение: (uid_t) -1 используется в некоторых интерфейсах (например, setreuid(2)) для указания «отсутствия ID пользователя». Оставление (uid_t) -1 без отображения и его не использование гарантирует, что при использовании этих интерфейсов не будет проблем).

### Отображение идентификаторов пользователей и групп: запись в uid_map и gid_map
После создания нового пользовательского пространства имён в файл uid_map один из процессов в пространстве имён может выполнить однократную запись для определения отображения пользовательских ID в новом пользовательском пространстве имён. Повторная попытка записи в файл uid_map в пользовательском пространстве имён завершится с ошибкой EPERM. Эти же правила применимы к файлам gid_map.

Записываемые в uid_map (gid_map) строки должны соответствовать следующим правилам:

- В трёх полях должны быть корректные числа и последнее поле должно быть больше 0.

- Строки заканчиваются символами новой строки.

- Существует ограничение на количество строк в файле. В Linux 4.14 и старее оно установлено (произвольно) равным пятью строкам. Начиная с Linux 4.14 его значение равно 340 строкам. Также, количество байт, записываемых в файл, должно быть меньше размера системной страницы, и запись должна выполняться в начало файла (т. е., нельзя использовать lseek(2) и pwrite(2) для записи в файл при ненулевом смещении).

- Диапазон пользовательских ID (групповых ID), указанный в каждой строке, не должен перекрываться с диапазонами в других строках. В первой реализации (Linux 3.8) это требование удовлетворялось простейшим способом, который задавал другое требование: значения в полях 1 и 2 следующих одна за одной строк, должны увеличиваться, что не давало создавать некоторые корректные отображения. В Linux 3.9 и новее это ограничение было снято, и допустим любой набор не перекрывающихся отображений.

- В файл должна быть записана, как минимум, одна строка.

Попытки записи, нарушающие перечисленные выше правила, завершаются с ошибкой EINVAL.

Чтобы процесс мог записывать в файл /proc/[pid]/uid_map (/proc/[pid]/gid_map) должны быть удовлетворены все условия:

1.

Записывающий процесс должен иметь мандат CAP_SETUID (CAP_SETGID) в пользовательском пространстве имён процесса pid.

2.

Записывающий процесс должен находиться в пользовательском пространстве имён процесса pid или быть родительским пользовательским пространством имён процесса pid.

3.

Отображаемые пользовательские ID (групповые ID) должны иметь соответствующее отображение в родительском пользовательском пространстве имён.

4.

Применимо к одному из двух случаев:

*

Или записывающий процесс имеет мандат CAP_SETUID (CAP_SETGID) в родительском пользовательском пространстве имён.

+

В дальнейшем ограничения не применяются: процесс может создавать отображения в произвольные пользовательские ID (групповые ID) в родительском пользовательском пространстве имён.

*

Или в противном случае накладываются следующие (все) ограничения:

+

Данные, записываемые в uid_map (gid_map), должны состоять из одной строки, которая отображает эффективный пользовательский ID (групповой ID) записывающего процесса в родительском пользовательском пространстве имён в пользовательский ID (групповой ID) в пользовательском пространстве имён.

+

Записывающий процесс должен иметь мандат тот же эффективный пользовательский ID что и процесс, который создал пользовательское пространство имён.

+

В случае gid_map, перед записью в gid_map сначала нужно использовать системный setgroups(2) для записи "deny" в файл /proc/[pid]/setgroups (смотрите ниже).

Попытки записи, нарушающие перечисленные выше правила, завершаются с ошибкой EPERM.

### Взаимодействие с системными вызовами, которые изменяют UID или GID процесса.
В пользовательском пространстве имён, в котором не выполнялась запись в файл uid_map, системные вызовы, изменяющие ID пользователя, будут завершаться с ошибкой. Подобными образом, если не выполнялась запись в файл gid_map, то системные вызовы, изменяющие ID группы, будут завершаться с ошибкой. После записи в файл uid_map и gid_map только отображённые значения могут использоваться в системных вызовах, изменяющих ID пользователя или группы.

Для ID пользователя, это относится к следующим системным вызовам: setuid(2), setfsuid(2), setreuid(2) и setresuid(2). Для ID группы, это относится к следующим системным вызовам: setgid(2), setfsgid(2), setregid(2), setresgid(2) и setgroups(2).

Запись "deny" в файле /proc/[pid]/setgroups перед записью в /proc/[pid]/gid_map насовсем отключает setgroups(2) в пользовательском пространстве имён, а также разрешает запись в /proc/[pid]/gid_map без мандата CAP_SETGID в родительском пользовательском пространстве имён.

### Файл /proc/[pid]/setgroups
Файл /proc/[pid]/setgroups содержит строку "allow", если процессам в пользовательском пространстве имён, которые содержат процесс с pid, разрешено выполнять системный вызов setgroups(2); в файл содержится строка "deny", если setgroups(2) запрещён в этом пользовательском пространстве имён. Заметим, что независимо от значения в файле /proc/[pid]/setgroups (и независимо от мандатов процесса) вызовы setgroups(2) также запрещены, если /proc/[pid]/gid_map до этого не был настроен.

Привилегированный процесс (с мандатом CAP_SYS_ADMIN в пространстве имён) может записать строку "allow" или "deny" в этот файл до записи отображения ID групп для этого пользовательского пространства имён в файл /proc/[pid]/gid_map. Запись строки "deny" запрещает любому процессу в пользовательском пространстве имён выполнять setgroups(2).

Сущность ограничений, описанных в предыдущем абзаце в том, чтобы разрешить запись в /proc/[pid]/setgroups только когда запрещено вызывать setgroups(2), так как /proc/[pid]/gid_map не настроен. Это гарантирует, что процесс не сможет перейти из состояния, в котором setgroups(2) разрешён, в состояние, в котором setgroups(2) запрещён; процесс может переходить только из состояния, когда setgroups(2) запрещён, в состояние, когда setgroups(2) разрешён.

Значение по умолчанию в этом файле для начального состояния пользовательского пространства имён равно "allow".

После записи в /proc/[pid]/gid_map (что действует как разрешение работы setgroups(2) в пользовательском пространстве имён), больше невозможно запретить setgroups(2) записью "deny" в /proc/[pid]/setgroups (запись завершается ошибкой EPERM).

Дочернее пользовательское пространство имён наследует значение /proc/[pid]/setgroups своего родителя.

Если файл setgroups содержит значение "deny", то системный вызов setgroups(2) не может быть повторно включён в дальнейшем (записью "allow" в файл) в этом пользовательском пространстве имён (попытка это сделать завершается ошибкой EPERM). Это ограничение также распространяется на всех потомков пользовательского пространства имён этого пользовательского пространства имён.

Файл /proc/[pid]/setgroups был добавлен в Linux 3.19, но перенесён и в старшие серии ядра, так как служит для обеспечения безопасности. В частности, это влияет на файлы с правами доступа «rwx---rwx». Эти файлы меньшие права «группе» по сравнению с группой «остальные». Это означает, что удаление группы с помощью setgroups(2) может открыть доступ к файлу, тем которые не имели его. До существования пользовательских пространств имён это не было проблемой, так как только привилегированный процесс (с мандатом CAP_SETGID) мог вызывать setgroups(2). Однако с введение пользовательских пространств имён стало возможным и непривилегированному процессу создавать новое пространство имён, в котором пользователь имеет все права. После этого ранее непривилегированный пользователь может удалить группы и таким образом получить доступ к файлу, которого раньше не имел. Файл /proc/[pid]/setgroups был добавлен для решения этой проблемы безопасности; он запрещает непривилегированному процессу удалять группы с помощью setgroups(2).

### Неотображённые пользовательские и групповые ID
Есть несколько мест, где в пользовательском пространстве могут появиться неотображённые пользовательские ID (групповые ID). Например, первый процесс в новом пользовательском пространстве имён может вызвать getuid(2) до определения отображения пользовательских ID для пространства имён. В большинстве случаев, неотображённый пользовательский ID преобразуется в пользовательский ID (групповой ID) переполнения (overflow); значение по умолчанию для пользовательского ID (группового ID) переполнения равно 65534. Смотрите описание /proc/sys/kernel/overflowuid и /proc/sys/kernel/overflowgid в proc(5).

Случаи, где неотображённые ID отображаются в таком виде, относятся к системным вызовам, которые возвращают пользовательские ID (getuid(2), getgid(2) и подобные), мандаты, передаваемые через доменный сокет UNIX, мандаты, возвращаемые stat(2), waitid(2) и System V IPC «ctl»-операциями IPC_STAT, мандаты, показываемые в /proc/[pid]/status и файлах в /proc/sysvipc/*, мандаты, возвращаемые в поле si_uid структуры siginfo_t, полученной по сигналу (смотрите sigaction(2)), мандаты, записываемые в файл учёта процесса (смотрите acct(5)), и мандаты, возвращаемые с уведомлениями очереди сообщений POSIX (смотрите mq_notify(3)).

Есть один известный случай, где неотображённый пользовательский и групповой ID не преобразуется в соответствующее значение ID переполнения. Если при просмотре файла uid_map или gid_map обнаруживается, что для второго поля нет отображения, то поле отображается как 4294967295 (-1 для беззнакового целого).

### Доступ к файлам
Чтобы определить права, с которыми непривилегированный процесс обращается к файлу, берутся идентификаторы процесса (UID, GID) и идентификаторы файла, отображаемые в те, которые были бы в исходном пространстве имён пользователя, и затем сравниваются для определения прав, которые процесс имеет на файл. Это также выполняется и для других объектов, использующих идентификаторы плюс модель маски прав доступа, например для объектов System V IPC.

### Операции с файловыми мандатами
Некоторые мандаты позволяют процессу обходить различные ограничения, налагаемые ядром на выполнение операций над файлами, принадлежащими другим пользователям или группам. Список мандатов: CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER и CAP_FSETID.

Внутри пользовательского пространства имён эти мандаты позволяют процессу обходить правила, если процесс имеет соответствующий мандат на файле, подразумевающий что:

- процесс имеет соответствующий эффективный мандат в своём пространстве имён пользователя; и

- файловые ID пользователя и группы корректно отображаются в пользовательскомпространстве имён.

Мандат CAP_FOWNER учитывается по-другому: it allows a process to bypass the corresponding rules so long as at least the file's user ID has a mapping in the user namespace (т. е., файловый ID группы может не иметь корректного отображения).

### Программы с установленными битами set-user-ID и set-group-ID
Когда процесс внутри пользовательского пространства имён выполняет программу с установленным битом set-user-ID (set-group-ID), то эффективный ID пользователя (группы) внутри пространства имён изменяется на значение, отображённое для ID пользователя (группы) файла. Однако, если ID пользователя или группы файла не имеет отображения внутри пространства имён, то бит set-user-ID (set-group-ID) просто игнорируется: выполняется новая программа, но эффективный ID пользователя (группы) остаётся не изменённым (такое поведение зеркально семантике выполнения программы с set-user-ID или set-group-ID, располагающейся в файловой системе, которая была смонтирована с флагом MS_NOSUID, как описано в mount(2)).

### Разное
Когда ID пользователя и группы процесса передаются через доменный сокет UNIX в процесс в другом пользовательском пространстве имён (смотрите описание SCM_CREDENTIALS в unix(7)), то они транслируются в соответствующие значения согласно отображению ID пользователя и группы принимающего процесса.

## СООТВЕТСТВИЕ СТАНДАРТАМ
Пространства имён есть только в Linux.

## ЗАМЕЧАНИЯ
За эти годы в ядро Linux добавлено много свойств, которые были доступны только привилегированным пользователям, так как их возможности слишком велики, чтобы наделять ими приложения с set-user-ID. В целом, становится безопасно разрешать пользователю root в пользовательском пространстве имён использовать эти свойства, так как будучи в пользовательском пространстве имён, он не может получить больше прав, чем имеет root в пользовательском пространстве имён.

### Доступность
Для использования пользовательских пространств имён ядро должно быть собрано с параметром CONFIG_USER_NS. Пользовательские пространства имён требуют поддержки во многих подсистемах ядра. Если в ядре задействована неподдерживаемая подсистема, то включить поддержку пользовательских пространств имён невозможно.

В Linux 3.8 самые важные подсистемы поддерживают пользовательские пространства имён, но значительное количество файловых систем не имеют инфраструктуры для отображения пользовательских и групповых ID между пользовательскими пространствами имён. В Linux 3.9 добавлена требуемая поддержка инфраструктуры во многие неподдерживаемые файловые системы (Plan 9 (9P), Andrew File System (AFS), Ceph, CIFS, CODA, NFS и OCFS2). В Linux 3.12 добавлена поддержка в последние основные файловые системы (XFS).

## ПРИМЕР
Представленная далее программа разработана для экспериментов с пользовательскими пространствами имён. Она создаёт пространства имён согласно параметрам командной строки и затем выполняет команду внутри этих пространств имён. В комментариях и функции usage() предоставлено полное описание программы. Следующий сеанс оболочки показывает её работу.

Сначала, посмотрим на окружение выполнения:

```
$ uname -rs     # требуется Linux 3.8 или новее
Linux 3.8.0
$ id -u         # работа от непривилегированного пользователя
1000
$ id -g
1000
```

Теперь запустим новую оболочку в новых пользовательском (-U), монтирования (-m) и PID (-p) пространствах имён с пользовательским (-M) и групповым ID (-G) 1000, отображающимся в 0 внутри пользовательского пространства имён:

```
$ ./userns_child_exec -p -m -U -M '0 1000 1' -G '0 1000 1' bash
```

У оболочки PID равен 1, так как это первый процесс в новом пространстве имён PID:

```
bash$ echo $$
1
```

Смонтируем новую файловую систему /proc и просмотрим все процессы, видимые в новом пространстве имён PID; убедимся, что оболочка не видит ни одного процесса вне своего пространства имён PID:

```
bash$ mount -t proc proc /proc
bash$ ps ax

  PID TTY      STAT   TIME COMMAND

    1 pts/3    S      0:00 bash

   22 pts/3    R+     0:00 ps ax
```

Внутри пользовательского пространства имён идентификаторы пользователя и группы оболочки равны 0, и она имеет полный набор разрешённых и эффективных мандатов:

```
bash$ cat /proc/$$/status | egrep '^[UG]id'
Uid:	0	0	0	0
Gid:	0	0	0	0
bash$ cat /proc/$$/status | egrep '^Cap(Prm|Inh|Eff)'
CapInh:	0000000000000000
CapPrm:	0000001fffffffff
CapEff:	0000001fffffffff
```

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

```
/* userns_child_exec.c

   Лицензируется на условиях Универсальной общественной лицензии

   GNU версии 2 и новее

   Создаёт дочерний процесс, который запускает командную оболочку

   в новых пространствах имён; может выполнять отображение UID и GID,

   если они указаны при создании пользовательского пространства имён.
*/
#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
/* Простая функция обработки ошибок: выводит сообщение об ошибке согласно

   значению в «errno» и завершает вызвавший процесс */
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \

                        } while (0)
struct child_args {

    char **argv;        /* команда, выполняемая потомком с параметрами */

    int    pipe_fd[2];  /* канал для синхронизации родителя и потомка */
};
static int verbose;
static void
usage(char *pname)
{

    fprintf(stderr, "Использование: %s [параметры] кмд [арг…]\n\n", pname);

    fprintf(stderr, "Создаёт дочерний процесс, который запускает командную "

            "оболочку в новом пользовательском пространстве имён,\n"

            "и, возможно, также в других новых пространствах имён.\n\n");

    fprintf(stderr, "Параметры:\n\n");
#define fpe(str) fprintf(stderr, "    %s", str);

    fpe("-i          Новое пространство имён IPC\n");

    fpe("-m          Новое пространство имён монтирования\n");

    fpe("-n          Новое сетевое пространство имён\n");

    fpe("-p          Новое пространство имён PID\n");

    fpe("-u          Новое пространство имён UTS\n");

    fpe("-U          Новое пользовательское пространство имён\n");

    fpe("-M uid_map  карта UID для пользовательского пространства имён\n");

    fpe("-G gid_map  карта GID для пользовательского пространства имён\n");

    fpe("-z          Отображать пользовательский UID и GID в 0 в пользовательском пространстве имён\n");

    fpe("            (эквивалентно: -M '0 <uid> 1' -G '0 <gid> 1')\n");

    fpe("-v          показывать дополнительные сообщения\n");

    fpe("\n");

    fpe("Если указан -z, -M или -G, то требуется -U.\n");

    fpe("Нельзя указывать -z вместе с -M или -G.\n");

    fpe("\n");

    fpe("Строка карты для -M и -G состоит из записей вида:\n");

    fpe("\n");

    fpe("    ID-внутри-ns   ID-вне-ns   длина\n");

    fpe("\n");

    fpe("Строка карты может содержать несколько записей через запятую;\n");

    fpe("запятые замещаются на символы новой строки перед записью"

        " в файлы карт.\n");

    exit(EXIT_FAILURE);
}
/* Обновляем файл отображения «map_file» значением из

   «mapping» — строкой, в которой определены отображения UID или GID.

   Отображения UID или GID состоят из одной или более записей

   (разделённых символом новой строки) вида:

       ID-внутри-ns    ID-снаружи-ns   длина

   Требовать от пользователя указывать строку с символами новой строки

   в командной строке неприемлемо. Поэтому мы позволим использовать

   для разделения записей запятые и заменим их символами новой строки

   перед записью строки в файл. */
static void
update_map(char *mapping, char *map_file)
{

    int fd, j;

    size_t map_len;     /* длина «mapping» */

    /* Заменяем запятые на символы новой строки в строке отображения */

    map_len = strlen(mapping);

    for (j = 0; j < map_len; j++)

        if (mapping[j] == ',')

            mapping[j] = '\n';

    fd = open(map_file, O_RDWR);

    if (fd == -1) {

        fprintf(stderr, "ОШИБКА: open %s: %s\n", map_file,

                strerror(errno));

        exit(EXIT_FAILURE);

    }

    if (write(fd, mapping, map_len) != map_len) {

        fprintf(stderr, "ОШИБКА: write %s: %s\n", map_file,

                strerror(errno));

        exit(EXIT_FAILURE);

    }

    close(fd);
}
/* В Linux 3.19 изменена работа с setgroups(2) и добавлен файл

   'gid_map' для обеспечения безопасности. Задача в том,

   чтобы запретить удалять группы *непривилегированному* пользователю

   через создание пространства имён. Результат изменений 3.19 в том, что

   для обновления файла 'gid_maps', сначала нужно запретить системный

   вызов setgroups() в этом пользовательском пространстве имён, записав

   "deny" в один из файлов /proc/PID/setgroups в этом пространстве имён.

   Это цель данной функции. */
static void
proc_setgroups_write(pid_t child_pid, char *str)
{

    char setgroups_path[PATH_MAX];

    int fd;

    snprintf(setgroups_path, PATH_MAX, "/proc/%ld/setgroups",

            (long) child_pid);

    fd = open(setgroups_path, O_RDWR);

    if (fd == -1) {

        /* Система может не поддерживать

           /proc/PID/setgroups. В этом случае файл не существует,

           и система не закладывает ограничений, добавленных в Linux 3.19.

           Хорошо, нам не нужно ничего делать, чтобы разрешить

           обновлять 'gid_map'.

           Однако, если ошибка open() отличается от

           ENOENT, сообщим об этом пользователю. */

        if (errno != ENOENT)

            fprintf(stderr, "ERROR: open %s: %s\n", setgroups_path,

                strerror(errno));

        return;

    }

    if (write(fd, str, strlen(str)) == -1)

        fprintf(stderr, "ОШИБКА: write %s: %s\n", setgroups_path,

            strerror(errno));

    close(fd);
}
static int              /* Начальная функция клонированного потомка */
childFunc(void *arg)
{

    struct child_args *args = (struct child_args *) arg;

    char ch;

    /* Ждём пока родитель обновит отображения UID и GID.

       Смотрите комментарий в main(). Мы ждём конца файла в канале,

       который будет закрыт родительским процессом после обновления

       отображений. */

    close(args->pipe_fd[1]);    /* закрываем наш дескриптор для записи

                                   конца канала для того, чтобы мы

                                   увидели EOF, когда родитель закроет

                                   свой дескриптор */

    if (read(args->pipe_fd[0], &ch, 1) != 0) {

        fprintf(stderr,

                "Ошибка в потомке: при чтении из канала получен != 0\n");

        exit(EXIT_FAILURE);

    }

    close(args->pipe_fd[0]);

    /* Запускаем командную оболочку */

    printf("О exec %s\n", args->argv[0]);

    execvp(args->argv[0], args->argv);

    errExit("execvp");
}
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];    /* место под стек в потомке */
int
main(int argc, char *argv[])
{

    int flags, opt, map_zero;

    pid_t child_pid;

    struct child_args args;

    char *uid_map, *gid_map;

    const int MAP_BUF_SIZE = 100;

    char map_buf[MAP_BUF_SIZE];

    char map_path[PATH_MAX];

    /* Разбираем параметры командной строки. Начальный символ «+» в

       последнем аргументе getopt() предотвращает подстановку параметров

       командной строки в стиле GNU. Это полезно, так как иногда

       «команда», выполняемая этой программой, сама имеет параметры

       командной строки. Мы не хотим, чтобы getopt() передала их

       нашей программе. */

    flags = 0;

    verbose = 0;

    gid_map = NULL;

    uid_map = NULL;

    map_zero = 0;

    while ((opt = getopt(argc, argv, "+imnpuUM:G:zv")) != -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 'v': verbose = 1;                  break;

        case 'z': map_zero = 1;                 break;

        case 'M': uid_map = optarg;             break;

        case 'G': gid_map = optarg;             break;

        case 'U': flags |= CLONE_NEWUSER;       break;

        default:  usage(argv[0]);

        }

    }

    /* -M или -G без -U не имеют смысла */

    if (((uid_map != NULL || gid_map != NULL || map_zero) &&

                !(flags & CLONE_NEWUSER)) ||

            (map_zero && (uid_map != NULL || gid_map != NULL)))

        usage(argv[0]);

    args.argv = &argv[optind];

    /* Мы используем канал для синхронизации родителя и потомка, чтобы

       родитель настроил отображения UID и GID до того, как потомок

       вызовет execve(). Это гарантирует, что потомок предъявит свои

       мандаты при execve(); обычно мы хотим отобразить эффективный

       пользовательский ID потомка в 0 в новом пользовательском

       пространстве имён. Без этой синхронизации потомок потерял

       бы свои мандаты при вызове execve() с ненулевым пользовательским

       ID (смотрите в справочной странице capabilities(7) подробности

       преобразования мандатов процесса при execve()). */

    if (pipe(args.pipe_fd) == -1)

        errExit("pipe");

    /* создаём потомка в новом пространстве имён */

    child_pid = clone(childFunc, child_stack + STACK_SIZE,

                      flags | SIGCHLD, &args);

    if (child_pid == -1)

        errExit("clone");

    /* предок попадает сюда */

    if (verbose)

        printf("%s: PID потомка, созданного clone(): %ld\n",

                argv[0], (long) child_pid);

    /* обновляем отображения UID и GID в потомке */

    if (uid_map != NULL || map_zero) {

        snprintf(map_path, PATH_MAX, "/proc/%ld/uid_map",

                (long) child_pid);

        if (map_zero) {

            snprintf(map_buf, MAP_BUF_SIZE, "0 %ld 1", (long) getuid());

            uid_map = map_buf;

        }

        update_map(uid_map, map_path);

    }

    if (gid_map != NULL || map_zero) {

        proc_setgroups_write(child_pid, "deny");

        snprintf(map_path, PATH_MAX, "/proc/%ld/gid_map",

                (long) child_pid);

        if (map_zero) {

            snprintf(map_buf, MAP_BUF_SIZE, "0 %ld 1", (long) getgid());

            gid_map = map_buf;

        }

        update_map(gid_map, map_path);

    }

    /* закрываем конец канала на стороне записи для сообщения потомку

       о том, что мы обновили отображения UID и GID */

    close(args.pipe_fd[1]);

    if (waitpid(child_pid, NULL, 0) == -1)      /* ждём потомка */

        errExit("waitpid");

    if (verbose)

        printf("%s: завершение\n", argv[0]);

    exit(EXIT_SUCCESS);
}
```

## СМОТРИТЕ ТАКЖЕ
newgidmap(1), newuidmap(1), clone(2), ptrace(2), setns(2), unshare(2), proc(5), subgid(5), subuid(5), capabilities(7), cgroup_namespaces(7) credentials(7), namespaces(7), pid_namespaces(7)

Файл из дерева исходного кода ядра Documentation/namespaces/resource-control.txt.