ptrace

title: ptrace(2)

summary: трассировка процесса

text:
# ptrace(2)

## ИМЯ
ptrace - трассировка процесса

## ОБЗОР

```
#include <sys/ptrace.h>
```

```
long ptrace(enum __ptrace_request request, pid_t pid,
            void *addr, void *data);
```

## ОПИСАНИЕ
Системный вызов ptrace() позволяет указать какому процессу («трассировщику») можно наблюдать и контролировать выполнение другого процесса («трассироемого»), просматривать и изменять его память и регистры. Обычно, он используется для реализации отладочных точек прерывания и для отслеживания системных вызовов.

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

ptrace(PTRACE_foo, pid, …)

где pid — идентификатор соответствующей нити Linux.

Заметим, что в этой странице «многонитевой процесс» означает группу нитей, состоящую из нитей, созданных с помощью clone(2) с флагом CLONE_THREAD.

Процесс может начать трассировку с вызова fork(2), в получившемся дочернем процессе выполнить действие PTRACE_TRACEME, после чего (обычно) выполнить execve(2). Или же один процесс может начать отладку другого процесса при помощи PTRACE_ATTACH или PTRACE_SEIZE.

При трассировке трассируемая нить останавливается каждый раз при получении сигнала, даже если этот сигнал игнорируется (исключением является SIGKILL, работающий обычным образом). Трассировщик будет уведомлён об этом при следующем вызове waitpid(2) (или подобном «ожидающем» системном вызове); этот вызов вернёт значение status, в котором содержится информация, указывающая на причину остановки трассируемой нити. Так как трассируемая нить остановлена, трассировщик может использовать различные запросы ptrace для обследования и изменения трассируемой нити. По окончании трассировщик разрешает трассируемой нити продолжить работу, возможно подавляя посылаемый ему сигнал (или даже отправляя вместо него другой сигнал).

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

По окончании трассировки трассировщик может заставить трассируемую нить продолжить свою работу в обычном не трассируемом режиме с помощью PTRACE_DETACH.

Значение аргумента request определяет выполняемое действие:

PTRACE_TRACEME

Указывает, что этот процесс будет трассирован своим родительским процессом. Вероятно, процессу не следует посылать этот запрос, если родительский процесс не готов к трассировке (аргументы pid, addr и data игнорируются).

Действие PTRACE_TRACEME используется только в трассируемой нити; остальные действия предназначены только для трассировщика. Для значений, описанных ниже, в параметре pid задаётся идентификатор трассируемой нити, над которой будет производиться действие. Перед выполнением действий (кроме PTRACE_ATTACH, PTRACE_SEIZE, PTRACE_INTERRUPT и PTRACE_KILL) трассируемая нить должна быть остановлена.

PTRACE_PEEKTEXT, PTRACE_PEEKDATA

Читает слово по адресу addr, находящееся в памяти трассируемой нити, возвращая это слово как результат вызова ptrace() Linux не разделяет адресные пространства текста и данных, поэтому оба вызова абсолютно идентичны (значение data игнорируется; но смотрите ЗАМЕЧАНИЯ).

PTRACE_PEEKUSER

Читает слово по смещению addr из области USER трассируемой нити, которая содержит информацию о регистрах и процессе (смотрите <sys/userh>). Слово возвращается в качестве результата вызова ptrace(). Обычно, смещение должно быть выровнено по границе слова, хотя это может зависеть от архитектуры системы. Смотрите ЗАМЕЧАНИЯ (значение data игнорируется; но смотрите ЗАМЕЧАНИЯ).

PTRACE_POKETEXT, PTRACE_POKEDATA

Копирует слово data в память трассируемой нити по адресу addr. В настоящее время, как и для PTRACE_PEEKTEXT и PTRACE_PEEKDATA, эти два действия одинаковы.

PTRACE_POKEUSER

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

PTRACE_GETREGS, PTRACE_GETFPREGS

Копирует, соответственно, регистры общего назначения или регистры сопроцессора трассируемой нити в память трассировщика по адресу data. Формат передаваемой структуры описан в файле <sys/user.h> (значение addr игнорируется). Заметим, что в системах SPARC предназначение data и addr поменяны местами; то есть data игнорируется, а регистры копируются по адресу addr. PTRACE_GETREGS и PTRACE_GETFPREGS есть не на всех архитектурах.

PTRACE_GETREGSET (начиная с Linux 2.6.34)

Читает регистры трассируемой нити. В addr указывается, в зависящей от архитектуры форме, тип читаемых регистров. Значение NT_PRSTATUS (равно 1) обычно служит для чтения регистров общего назначения. Если в ЦП есть, например, векторные регистры и для плавающей запятой, то их можно получить назначив addr соответствующую константу NT_foo. Значение data указывает на struct iovec, которая описывает расположение буфера назначения и длину. При возврате ядро изменяет iov.len, возвращая реальное количество возвращаемых байт.

PTRACE_SETREGS, PTRACE_SETFPREGS

Копирует, соответственно, регистры общего назначения или регистры для плавающей запятой трассируемой нити из памяти трассировщика по адресу data. Как и в случае c PTRACE_POKEUSER, изменения некоторых регистров общего назначения запрещены (значение addr игнорируется). Заметим, что в системах SPARC предназначение data и addr переставлены местами; то есть data игнорируется, а регистры копируются из памяти, на которую указывает адрес addr. PTRACE_SETREGS и PTRACE_SETFPREGS есть не для всех архитектур.

PTRACE_SETREGSET (начиная с Linux 2.6.34)

Изменяет регистры трассируемой нити. Значение addr и data аналогичны PTRACE_GETREGSET.

PTRACE_GETSIGINFO (начиная с Linux 2.3.99-pre6)

Получает информацию о сигнале, который вызвал остановку. Копирует структуру siginfo_t (смотрите sigaction(2)) из трассируемой нити в память трассировщика по адресу data (значение addr игнорируется).

PTRACE_SETSIGINFO (начиная с Linux 2.3.99-pre6)

Устанавливает информацию о сигнале. Копирует структуру siginfo_t, расположенную по адресу data трассировщика, в память трассируемой нити. Влияет только на сигналы, которые обычно были бы доставлены трассируемой нити и были пойманы трассировщиком. Затруднительно отличить обычные сигналы от созданных самим ptrace() (значение addr игнорируется).

PTRACE_PEEKSIGINFO (начиная с Linux 3.10)

Получает структуры siginfo_t не удаляя сигналы из очереди. Значение addr указывает на структуру ptrace_peeksiginfo_args, которая задаёт начальную позицию, из которой нужно начать копирование сигналов, а также их количество. Структуры siginfo_t копируются в буфер, указываемый в data. Возвращаемое значение содержит количество скопированных сигналов (ноль означает, что сигналов в указанной позиции нет). Внутри возвращаемых структур siginfo в поле si_code включается информация (__SI_CHLD, __SI_FAULT и т. д.), которая по-другому никак не выдаётся в пользовательское пространство.

```
struct ptrace_peeksiginfo_args {

    u64 off;    /* начальная позиция в очереди, с которой

                   начинается копирование сигналов */

    u32 flags;  /* PTRACE_PEEKSIGINFO_SHARED или 0 */

    s32 nr;     /* количество копируемых сигналов */
};
```

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

PTRACE_GETSIGMASK (начиная с Linux 3.11)

Помещает копию маски блокированных сигналов (смотрите sigprocmask(2)) в буфер, указанный в data (должен быть указателем на буфер с типом sigset_t). Аргумент addr содержит размер буфера, указанного в data (т. е. sizeof(sigset_t)).

PTRACE_SETSIGMASK (начиная с Linux 3.11)

Изменяет маску блокированных сигналов (смотрите sigprocmask(2)) на значение из буфера, указанного в data (должен быть указателем на буфер с типом sigset_t). Аргумент addr содержит размер буфера, указанного в data (т. е. sizeof(sigset_t)).

PTRACE_SETOPTIONS (начиная с Linux 2.4.6; см. предостережения в разделе ДЕФЕКТЫ)

Устанавливает флаги ptrace из data (значение addr игнорируется). Значение data воспринимается как битовая маска, в которой задаются следующие флаги:

PTRACE_O_EXITKILL (начиная с Linux 3.8)

Посылать сигнал SIGKILL трассируемому, если трассировщик существует. Этот параметр полезен для надзирателей ptrace, которые хотят убедиться, что трассируемые никогда не выйдут из-под контроля трассировщика.

PTRACE_O_TRACECLONE (начиная с Linux 2.5.46)

Останавливать трассируемую нить при следующем вызове clone(2) и автоматически запускать трассировку только что склонированного процесса, который начнёт выполнение с обработки сигнала SIGSTOP или PTRACE_EVENT_STOP, если используется PTRACE_SEIZE. Вызов waitpid(2) вернёт трассировщику значение status, которое равно

```
  status>>8 == (SIGTRAP | (PTRACE_EVENT_CLONE<<8))
```

Значение PID нового процесса можно получить с помощью PTRACE_GETEVENTMSG.

В некоторых случаях вызовы clone(2) могут быть не пойманы. Если трассируемая нить вызывает clone(2) с флагом CLONE_VFORK, то будет доставлен PTRACE_EVENT_VFORK, если установлен PTRACE_O_TRACEVFORK; в противном случае, если трассируемая нить вызывает clone(2) с установленным сигналом выхода равным SIGCHLD, то будет доставлен PTRACE_EVENT_FORK, если установлен PTRACE_O_TRACEFORK.

PTRACE_O_TRACEEXEC (начиная с Linux 2.5.46)

Останавливать трассируемую нить при следующем вызове execve(). Вызов waitpid(2) вернёт трассировщику значение status, которое равно

```
  status>>8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8))
```

Если исполняемая нить не является лидером группы нитей, то идентификатор нити сбрасывается в значение идентификатора лидера группы нитей перед его остановкой. Начиная с Linux 3.0, предыдущий идентификатор нити может быть получен с помощью PTRACE_GETEVENTMSG.

PTRACE_O_TRACEEXIT (начиная с Linux 2.5.60)

Останавливать трассируемую нить при его завершении (exit). Вызов waitpid() вернёт трассировщику значение status, которое равно

```
  status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))
```

Значение кода завершения трассируемой нити можно получить с помощью PTRACE_GETEVENTMSG.

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

PTRACE_O_TRACEFORK (начиная с Linux 2.5.46)

Останавливать трассируемую нить при следующем вызове fork(2) и автоматически запускать трассировку только что созданного с помощью fork процесса, который начнёт выполнение с обработки сигнала SIGSTOP или PTRACE_EVENT_STOP, если используется PTRACE_SEIZE. Вызов waitpid(2) вернёт трассировщику значение status, которое равно

```
  status>>8 == (SIGTRAP | (PTRACE_EVENT_FORK<<8))
```

Значение PID нового процесса можно получить с помощью PTRACE_GETEVENTMSG.

PTRACE_O_TRACESYSGOOD (начиная с Linux 2.4.6)

При доставке сигналов ловушек системных вызовов, устанавливать бит 7 в номере сигнала (т. е., доставляется SIGTRAP|0x80). Это позволяет трассировщику легко отличить обычные ловушки от тех, которые были вызваны системным вызовом.

PTRACE_O_TRACEVFORK (начиная с Linux 2.5.46)

Останавливать трассируемую нить при следующем вызове vfork(2) и автоматически запускать трассировку только что созданного с помощью vfork процесса, который начнёт выполнение с обработки сигнала SIGSTOP или PTRACE_EVENT_STOP, если используется PTRACE_SEIZE. Вызов waitpid(2) вернёт трассировщику значение status, которое равно

```
  status>>8 == (SIGTRAP | (PTRACE_EVENT_VFORK<<8))
```

Значение PID нового процесса можно получить с помощью PTRACE_GETEVENTMSG.

PTRACE_O_TRACEVFORKDONE (начиная с Linux 2.5.60)

Останавливать трассируемую нить при следующем вызове vfork(2). Вызов waitpid(2) вернёт трассировщику значение status, которое равно

```
  status>>8 == (SIGTRAP | (PTRACE_EVENT_VFORK_DONE<<8))
```

Значение PID нового процесса можно получить (начиная с Linux 2.6.18) с помощью PTRACE_GETEVENTMSG.

PTRACE_O_TRACESECCOMP (начиная с Linux 3.5)

Останавливать трассируемую нить при возникновении правила seccomp(2) SECCOMP_RET_TRACE. Вызов waitpid(2) вернёт трассировщику значение status, которое равно

```
  status>>8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP<<8))
```

Так как это останавливает PTRACE_EVENT, это похоже на syscall-enter-stop. Дополнительную информацию смотрите в замечании к PTRACE_EVENT_SECCOMP ниже. Данные сообщения события seccomp (из части SECCOMP_RET_DATA правила фильтрации seccomp) можно получить с помощью PTRACE_GETEVENTMSG.

PTRACE_O_SUSPEND_SECCOMP (начиная с Linux 4.3)

Приостановить защиты seccomp трассируемого. Это применяется независимо от режима и может быть использовано когда трассируемый ещё не установил фильтры seccomp. То есть, корректным действием будет приостановка защит seccomp трассируемого, до того как их установит трассируемый, позволить трассируемому установить фильтры, и затем очистить этот флаг, когда нужно возобновить работу фильтров. Установка этого параметра требует, чтобы трассировщик имел мандат CAP_SYS_ADMIN, не было установлено никаких защит seccomp и не установлен сам PTRACE_O_SUSPEND_SECCOMP.

PTRACE_GETEVENTMSG (начиная с Linux 2.5.46)

Возвращает сообщения (с типом unsigned long) о событии ptrace, которое только что произошло, помещая его по адресу data в памяти трассировщика. Для PTRACE_EVENT_EXIT это код завершения трассируемой нити. Для PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK, PTRACE_EVENT_VFORK_DONE и PTRACE_EVENT_CLONE это PID нового процесса. Для PTRACE_EVENT_SECCOMP это SECCOMP_RET_DATA из фильтра seccomp(2), связанного со сработавшим правилом (значение addr игнорируется).

PTRACE_CONT

Возобновляет работу остановленной трассируемой нити. Если значение data не равно нулю, то оно считается номером сигнала, который надо доставить трассируемой нити; в противном случае сигнал не передаётся. Таким образом, например, трассировщик может контролировать передачу сигнала трассируемой нити (значение addr игнорируется).

PTRACE_SYSCALL, PTRACE_SINGLESTEP

Аналогично PTRACE_CONT они перезапускают остановленную трассируемую нить, но указывают, что процесс должен быть остановлен перед входом/выходом из системного вызова, или после исполнения одной инструкции, соответственно (трассируемая нить также, как обычно, будет остановлена при получении сигнала). С точки зрения трассировщика кажется, что трассируемая нить остановлена из-за получения сигнала SIGTRAP. Так, PTRACE_SYSCALL например, позволяет изучить содержимое аргументов перед системным вызовом, а при следующем PTRACE_SYSCALL можно просмотреть результат исполнения системного вызова. Аргумент data используется как в PTRACE_CONT (значение addr игнорируется).

PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP (начиная с Linux 2.6.14)

Действие PTRACE_SYSEMU приводит к продолжению и остановке на входе в следующий системный вызов, который не будет выполнен . Смотрите описание по syscall-stop ниже. Действие PTRACE_SYSEMU_SINGLESTEP выполняет тоже самое, но для одиночной инструкции, если это не системный вызов. Это действие используется программами, подобными User Mode Linux, которым нужно эмулировать все системные вызовы трассируемых нитей. Аргумент data используется также как у PTRACE_CONT. Аргумент addr игнорируется. Эти запросы поддерживаются только на x86.

PTRACE_LISTEN (начиная с Linux 3.4)

Перезапускает остановленную трассируемую нить, но её выполнение не начинается. Полученное состояние трассируемой нити подобно процессу, который был остановлен по SIGSTOP (или другим останавливающим сигналом). Дополнительную информацию смотрите в подразделе «group-stop». PTRACE_LISTEN работает только для трассируемых нитей, присоединённых с помощью PTRACE_SEIZE.

PTRACE_KILL

Посылает трассируемой нити сигнал SIGKILL для его уничтожения (значения addr и data игнорируются).

Это действие устарело; не используйте его! Вместо него отправляйте SIGKILL напрямую с помощью kill(2) или tgkill(2). Проблема с действием PTRACE_KILL в том, что оно требует, чтобы трассируемая нить была режиме signal-delivery-stop, в противном случае оно может не сработать (т. е., может завершиться без ошибок, но трассируемая нить не будет уничтожена). В отличие от него, отправка SIGKILL напрямую не имеет данного ограничения.

PTRACE_INTERRUPT (начиная с Linux 3.4)

Остановить трассируемый объект. Если трассируемый объект выполняется или спит в пространстве ядра и действует PTRACE_SYSCALL, то системный вызов прерывается и сообщается о syscall-exit-stop (прерванный системный вызов перезапускается при перезапуске трассируемого объекта). Если трассируемый объект был уже остановлен по сигналу и ему был послан PTRACE_LISTEN, то трассируемый объект останавливается с PTRACE_EVENT_STOP и WSTOPSIG(status) возвращает сигнал остановки. Если в этот же момент генерируется любое другое событие ptrace-stop (например, если трассируемому посылается сигнал), то возникает ptrace-stop. Если ничего из вышеперечисленного не происходит (например, если трассируемый выполняется в пространстве пользователя), то он останавливается с PTRACE_EVENT_STOP и WSTOPSIG(status) == SIGTRAP. PTRACE_INTERRUPT работает только для трассируемых объектов, к которым подключились с помощью PTRACE_SEIZE.

PTRACE_ATTACH

Выполняет присоединение к процессу с указанным pid, делая его трассируемым для вызывающего процесса. Трассируемой нити посылается SIGSTOP, но нет жёсткого правила, что она будет остановлен по завершению этого вызова; используйте waitpid(2) для ожидания остановки трассируемой нити. Дополнительную информацию смотрите в подразделе «Присоединение и отсоединение» (значения addr и data игнорируются).

Право выполнять PTRACE_ATTACH определяется проверкой режима доступа ptrace PTRACE_MODE_READ_REALCREDS; смотрите ниже.

PTRACE_SEIZE (начиная с Linux 3.4)

Выполняет присоединение к процессу, указанному в pid, делает его трассируемым для вызывающего процесса. В отличие от PTRACE_ATTACH, PTRACE_SEIZE не останавливает процесс. О group-stops сообщается как PTRACE_EVENT_STOP и WSTOPSIG(status) возвращает сигнал остановки. Автоматически присоединённые потомки останавливаются с PTRACE_EVENT_STOP и WSTOPSIG(status) возвращает SIGTRAP вместо доставки им сигнала SIGSTOP. При вызове execve(2) дополнительный SIGTRAP не посылается. Только подключённые с помощью PTRACE_SEIZE процессы могут принимать команды PTRACE_INTERRUPT и PTRACE_LISTEN. Только что описанное поведение «захвата» (seized) наследуется потомками, которые автоматически присоединяются с помощью PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK и PTRACE_O_TRACECLONE. Значение addr должно быть равно нулю. В data содержится битовая маска параметров ptrace, которая применяется сразу же.

Право выполнять PTRACE_SEIZE определяется проверкой режима доступа ptrace PTRACE_MODE_READ_REALCREDS; смотрите ниже.

PTRACE_SECCOMP_GET_FILTER (начиная с Linux 4.4)

Эта операция позволяет трассировщику получить дамп классических фильтров BPF трассируемого.

Значение addr имеет тип integer и задаёт индекс фильтра для дампа. Индекс последнего установленного фильтра равен 0. Если addr больше количества установленных фильтров, то операция завершается ошибкой ENOENT.

Значение data является указателем на массив struct sock_filter достаточного размера для сохранения программы BPF, или NULL, если программа не будет сохраняться.

При успешном выполнении возвращаемое значение — количество инструкций в программе BPF. Если data равнялось NULL, то возвращается значение, которое можно использовать для создания массива struct sock_filter корректного размера, который будет передан в последующем вызове.

Данная операция завершается ошибкой EACCES, если вызывающий не имеет мандата CAP_SYS_ADMIN, или если вызывающий находится в ограничительном или фильтрующем режиме. Если фильтр, на который ссылается адрес addr, не является классическим фильтром BPF, то операция завершается ошибкой EMEDIUMTYPE.

Эта операция доступна только, если ядро собрано с параметрами CONFIG_SECCOMP_FILTER и CONFIG_CHECKPOINT_RESTORE.

PTRACE_DETACH

Возобновляет работу остановленной трассируемой нити, аналогично PTRACE_CONT, но сначала отсоединяется от него. В Linux при помощи этого вызова трассируемая нить может быть отсоединёна независимо от того, каким методом была запущена трассировка (значение addr игнорируется).

PTRACE_GET_THREAD_AREA (начиная с Linux 2.6.0)

Данная операция выполняет задачу, подобную get_thread_area(2). Она читает элемент TLS из GDT, чей индекс передан в addr, помещает копию элемента в struct user_desc, указанную в data (в отличие от get_thread_area(2), значение entry_number структуры struct user_desc игнорируется).

PTRACE_SET_THREAD_AREA (начиная с Linux 2.6.0)

Данная операция выполняет задачу, подобную set_thread_area(2). Она изменяет элемент TLS в GDT, чей индекс указан в addr, данными, переданными в структуре struct user_desc, на которую указывает data (в отличие от set_thread_area(2), значение entry_number структуры struct user_desc игнорируется; иначе говоря, данная операция ptrace не может использоваться для выделения свободного элемента TLS).

PTRACE_GET_SYSCALL_INFO (начиная с Linux 5.3)

Возвращает информацию о системном вызове, который привёл к останову. Информация помещается в буфер, указанный в аргументе data, который должен быть указателем на буфер с типом struct ptrace_syscall_info. Аргумент addr содержит размер буфера, на который указывает аргумент data (т. е., sizeof(struct ptrace_syscall_info)). Возвращаемое значение равно количеству байт, записанных ядром. Если размер записываемых ядром данных больше значения из аргумента addr, то данные результата обрезаются.

Структура ptrace_syscall_info содержит следующие поля:

```
struct ptrace_syscall_info {

    __u8 op;         /* тип останова системного вызова */

    __u32 arch;      /* значение AUDIT_ARCH_*; смотрите seccomp(2) */

    __u64 instruction_pointer; /* указатель инструкции ЦП */

    __u64 stack_pointer;       /* указатель стека ЦП */

    union {

        struct {     /* op == PTRACE_SYSCALL_INFO_ENTRY */

            __u64 nr;          /* номер системного вызова */

            __u64 args[6];     /* аргументы системного вызова */

        } entry;

        struct {     /* op == PTRACE_SYSCALL_INFO_EXIT */

            __s64 rval;        /* возвращаемое значение системного вызова */

            __u8 is_error;     /* флаг ошибки системного вызова;

                                  логический: в rval содержится

                                  значение ошибки (-ERRCODE) или

                                  или не ошибки? */

        } exit;

        struct {     /* op == PTRACE_SYSCALL_INFO_SECCOMP */

            __u64 nr;          /* номер системного вызова */

            __u64 args[6];     /* аргументы системного вызова */

            __u32 ret_data;    /* часть SECCOMP_RET_DATA

                                  возвращаемого значения

                                  SECCOMP_RET_TRACE */

        } seccomp;

    };
};

```

Поля op, arch, instruction_pointer и stack_pointer определены для всех видов остановов системных вызовов ptrace. Оставшаяся структура является объединением; из неё нужно читать только те поля, которые имеют смысл для конкретного вызова останова системного вызова, указанного в поле op.

В поле op указывается одно из следующих значений (определены в <linux/ptrace.h>), определяющих тип возникшего останова и какая часть объединения заполнена:

PTRACE_SYSCALL_INFO_ENTRY

Компонент объединения entry содержит информацию о входе в останов системного вызова.

PTRACE_SYSCALL_INFO_EXIT

Компонент объединения exit содержит информацию о выходе из останова системного вызова.

PTRACE_SYSCALL_INFO_SECCOMP

Компонент объединения exit содержит информацию об останове PTRACE_EVENT_SECCOMP.

PTRACE_SYSCALL_INFO_NONE

Компонент объединения не содержит информации.

### Смерть в момент ptrace
Когда (возможно, многонитевой) процесс получает уничтожающий сигнал (из-за того, что обработчик равен SIG_DFL и что действием по умолчанию является уничтожение процесса), все нити завершают работу (exit). Трассируемые нити сообщают о своей смерти своим трассировщикам. Уведомления об этом событии доставляется с помощью waitpid(2).

Заметим, что уничтожающий сигнал сначала вызовет вхождение в режим signal-delivery-stop (только для одной трассируемой нити), и только после этого будет внедрён трассировщиком (или после того, как был отослан нити, которая не является трассируемой), затем все трассируемые нити в многонитевом процессе завершаются по сигналу (термин «signal-delivery-stop» объяснён далее).

Сигнал SIGKILL не генерирует режим signal-delivery-stop и поэтому трассировщик не может подавить его. Сигнал SIGKILL уничтожает даже внутри системных вызовов (syscall-exit-stop не генерируется перед уничтожением по SIGKILL). Конечным результатом SIGKILL всегда является уничтожение процесса (всех его нитей), даже если для некоторых нитей процесса выполняется трассировка.

Когда трассируемая нить вызывает _exit(2), он сообщает о своём уничтожении своему трассировщику. На оставшиеся нити ни какого влияния не оказывается.

Если какая-нибудь нить вызывает exit_group(2), то каждая трассируемая нить в этой группе нитей сообщает о своём уничтожении своему трассировщику.

Если установлен флаг PTRACE_O_TRACEEXIT, то перед непосредственным уничтожением возникает PTRACE_EVENT_EXIT. Это случается при выходе посредством exit(2), exit_group(2) и из-за уничтожения по сигналу (за исключением SIGKILL, в зависимости от версии ядра; смотрите ДЕФЕКТЫ ниже) и когда нити многонитевой процесса разрушаются при execve(2).

Трассировщик не может предполагать, что трассируемая нить, остановленная по ptrace, существует. Если много случаев, когда трассируемая нить может быть уничтожена будучи в остановленном состоянии (например, по SIGKILL). Поэтому, трассировщик должен быть готов обработать ошибку ESRCH при любом действии ptrace. К сожалению, эта же ошибка возвращается, если трассируемая нить существует, но не остановлена по ptrace (для действий, которые требуют остановленной трассируемой нити), или если она не трассируется процессом, который вызвал ptrace. Трассировщику необходимо отслеживать состояние остановки/работы трассируемой нити и воспринимать ESRCH как «трассируемая нить была неожиданно уничтожена» только, если он знает, что трассируемая нить была в состоянии ptrace-stop. Заметим, что нет гарантии того, что waitpid(WNOHANG) всегда сообщит о состоянии уничтожения трассируемой нити, если действие ptrace вернуло ESRCH. Вызов waitpid(WNOHANG) вместо этого может вернуть 0. Другими словами, трассируемая нить может быть «ещё не полностью уничтожена», но уже отклонять действия ptrace.

Трассировщик не может предполагать, что всегда поймает завершение существования трассируемой нити с помощью WIFEXITED(status) или WIFSIGNALED(status); есть несколько случаев, когда этого не происходит. Например, если нить — не лидер группы нитей — вызывает execve(2) и исчезает; её PID больше не появится снова, и все последующие остановки по ptrace будут приходить от PID лидера группы нитей.

### Состояния останова
Трассируемый может находиться в двух состояниях: выполнения или остановки. В целях ptrace, трассируемый, заблокированный в системном вызове (например, read(2), pause(2) и т.д.), считается выполняющимся, даже если он заблокирован уже долго. Состояние трассируемого после PTRACE_LISTEN находится, отчасти, в «сумеречной зоне»: не не в каком-либо из ptrace-stop (команды ptrace не будут с ним работать и он будет посылать уведомления waitpid(2)), но он также может считаться за «остановленный», так как он не выполняет инструкций (не планируется для этого), и если он был в group-stop до PTRACE_LISTEN, он не будет отвечать на сигналы до тех пор, пока не получит SIGCONT.

Есть много разновидностей останова, и в обсуждении ptrace они часто объединены. Поэтому очень важно использовать точную терминологию.

В этой справочной странице любое состояние останова, в котором трассируемая нить готова выполнить действия ptrace трассировщика, называется ptrace-stop. В свою очередь, ptrace-stop можно разделить на signal-delivery-stop, group-stop, syscall-stop, PTRACE_EVENT stops и так далее. Далее эти состояния останова будут описаны подробней.

Когда выполняющаяся трассируемая нить входит в ptrace-stop, это видит трассировщик с помощью waitpid(2) (или через другой системный вызов «wait»). В большей части текста данной справочной страницы предполагается, что трассировщик ждёт с помощью:

pid = waitpid(pid_или_минус_1, &состояние, __WALL);

О трассируемой нити в состоянии ptrace-stop сообщается возвратом pid большим 0 и значением истины по WIFSTOPPED(status).

Флаг __WALL не содержит флагов WSTOPPED и WEXITED, но подразумевает их назначение.

Устанавливать флаг WCONTINUED при вызове waitpid(2) не рекомендуется: состояние «continued» относится к определённому процессу и его поглощение может запутать реального родителя трассируемой нити.

Использование флага WNOHANG может привести к тому, что waitpid(2) вернёт 0 («не ждать результатов, если они не готовы»), даже если трассировщик знает, что должно быть уведомление. Пример:

```
errno = 0;

    ptrace(PTRACE_CONT, pid, 0L, 0L);

    if (errno == ESRCH) {

        /* трассируемая нить мертва */

        r = waitpid(tracee, &status, __WALL | WNOHANG);

        /* r может быть 0 ! */

    }
```

Существуют следующие разновидности ptrace-stop: signal-delivery-stop, group-stop, остановки PTRACE_EVENT, syscall-stop. Все они могут быть получены по waitpid(2) с значением истинным по WIFSTOPPED(status). Их можно различить, если проверить значение status>>8, и, если есть неоднозначность этого значения, то запросив PTRACE_GETSIGINFO (замечание: для выполнения этой проверки не может использоваться макрос WSTOPSIG(status), так как он возвращает значение (status>>8) & 0xff).

### Signal-delivery-stop
Когда процесс (возможно, многонитевой) принимает какой-либо сигнал кроме SIGKILL, ядро выбирает произвольную нить для его обработки (если сигнал генерируется с помощью tgkill(2), то назначаемая нить может быть явно выбрана вызывающим). Если над выбранной нитью выполняется трассировка, то она попадает в режим signal-delivery-stop. В этот момент сигнал ещё не доставлен процессу и может быть отменён трассировщиком. Если трассировщик не отменил сигнал, то он передаётся трассируемой нити при следующем запросе перезапуска ptrace. Этот второй этап доставки сигнала называется в этой справочной странице внедрением сигнала. Заметим, что если сигнал блокируется, то signal-delivery-stop не происходит пока сигнал не будет разблокирован (исключением, как обычно, является SIGSTOP, который нельзя заблокировать).

Signal-delivery-stop наблюдается трассировщиком посредством waitpid(2), возвращающим истинное значения для WIFSTOPPED(status) с сигналом, который возвращается по WSTOPSIG(status). Если возвращается сигнал SIGTRAP, то он может быть разновидностью ptrace-stop; смотрите разделы «Syscall-stops» и «execve» далее. Если WSTOPSIG(status) возвращает останавливающий сигнал, то это может быть group-stop, смотрите далее.

### Внедрение и подавление сигнала
После обнаружения трассировщиком signal-delivery-stop, он должен перезапустить трассируемую нить вызовом

ptrace(PTRACE_restart, pid, 0, sig)

где PTRACE_restart — одно из перезапускающих действий ptrace. Если значение sig равно 0, то сигнал не доставляется. В противном случае, доставляется сигнал sig. Данная операция в справочной странице называется внедрением сигнала для того, чтобы можно отличить её от signal-delivery-stop.

Значение sig может отличаться от значения WSTOPSIG(status): трассировщик может поменять внедряемый сигнал.

Заметим, что подавленный сигнал всё равно заставит системные вызовы завершиться как можно скорее. В этом случае системные вызовы будут перезапущены: если трассировщик использует PTRACE_SYSCALL, то обнаружит, когда трассируемая нить повторно выполнила прерванный системный вызов (или системный вызов restart_syscall(2) для некоторых системных вызовов, которые используют другой механизм перезапуска). Даже системные вызовы (такие как poll(2)), которые не перезапускаются по сигналу, будут перезапущены после подавления сигнала; однако, в ядре существуют дефекты, из-за которых некоторые системные вызовы завершаются с ошибкой EINTR, даже если наблюдаемый сигнал не был внедрён в трассируемую нить.

Перезапускающие действия ptrace, выдаваемые в ptrace-stops, отличные от signal-delivery-stop, не гарантируют внедрения сигнала, даже если значение sig не равно нулю. Об ошибках не сообщается; ненулевое значение sig может быть просто проигнорировано. Пользователи ptrace не должны пытаться «создать новый сигнал» таким способом — используйте вместо этого tgkill(2).

Тот факт, что запросы внедрения сигнала могут игнорироваться при перезапуске трассируемой нити после остановок ptrace не из signal-delivery-stops, вызывает путаницу у пользователей ptrace. Типичный сценарий: трассировщик обнаруживает group-stop, принимает его за signal-delivery-stop, перезапускает трассируемую нить с помощью

ptrace(PTRACE_restart, pid, 0, stopsig)

пытаясь внедрить stopsig, но stopsig игнорируется и трассируемая нить продолжает выполняться.

Сигнал SIGCONT имеет побочный эффект — пробуждает (все нити) процесс, находящийся в group-stop. Это случается перед signal-delivery-stop. Трассировщик не может повлиять на это побочное действие (он может только подавить внедрение сигнала, что приводит к тому, что обработчик SIGCONT не будет выполнен в трассируемой нити, если он установлен). Фактически, пробуждение из group-stop может следовать после signal-delivery-stop для сигнала(ов) отличных от SIGCONT, если они ожидают момента доставки SIGCONT. Другими словами, SIGCONT может быть не первым сигналом, который обнаруживает трассируемую нить после её посылки.

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

В Linux 2.4 и более ранних версиях, сигнал SIGSTOP не может быть внедрён.

Действие PTRACE_GETSIGINFO может использоваться для получения структуры siginfo_t, которая соответствует доставленному сигналу. Для её изменения можно использовать PTRACE_SETSIGINFO. Если PTRACE_SETSIGINFO использовалась для изменения siginfo_t, то поле si_signo и параметр sig в перезапускающем действии должны совпадать, иначе результат непредсказуем.

### Group-stop
Когда (возможно многонитевой) процесс получает останавливающий сигнал, все нити останавливаются. Если для какой-то нити выполняется трассировка, то она входит в режим group-stop. Заметим, что останавливающий сигнал сначала приведёт к signal-delivery-stop (только в одной трассируемой нити) и только затем будет внедрён трассировщиком (или после того, как будет отправлен нити, над которой не выполняется трассировка), будет начат group-stop в всех трассируемых нитях многонитевого процесса. Как обычно, каждая трассируемая нить сообщает о group-stop соответствующему трассировщику.

Group-stop обнаруживается трассировщиком с помощью waitpid(2), который возвращается с истинным значением WIFSTOPPED(status) и останавливающим сигналом из WSTOPSIG(status). Тот же результат возвращается другими классами ptrace-stops, поэтому рекомендуется выполнять вызов

ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo)

Вызова можно избежать, если сигнал не равен SIGSTOP, SIGTSTP, SIGTTIN или SIGTTOU; только эти четыре сигнала являются останавливающими. Если трассировщик видит что-то другое, то это не может быть group-stop. В противном случае, трассировщику нужно вызвать PTRACE_GETSIGINFO. Если PTRACE_GETSIGINFO завершается с ошибкой EINVAL, то это определённо group-stop (возможны другие коды ошибок, например, ESRCH («нет такого процесса»), если трассируемая нить уничтожена по SIGKILL).

Если подключение к трассируемому было сделано с помощью PTRACE_SEIZE, то group-stop указывается PTRACE_EVENT_STOP: status>>16 == PTRACE_EVENT_STOP. Это позволяет обнаружить group-stop без дополнительного вызова PTRACE_GETSIGINFO.

Начиная с Linux 2.6.38, после того как трассировщик увидит ptrace-stop трассируемой нити и пока он не перезапустит или завершит её, трассируемая нить не будет выполняться, и не будет посылать уведомления (за исключением уничтожения по SIGKILL) трассировщику, даже если трассировщик войдёт в другой вызов waitpid(2).

Поведение ядра, описанное в предыдущем параграфе, вызывает проблемы с прозрачностью обработки останавливающих сигналов. Если трассировщик перезапускает трассируемую нить после group-stop, то останавливающий сигнал просто игнорируется — трассируемая нить продолжает выполняться. Если трассировщик не перезапускает трассируемую нить перед входом в следующий waitpid(2), то о будущих сигналах SIGCONT не будет сообщено трассировщику; это привело бы к тому, что сигналы SIGCONT не повлияли бы на трассируемую нить.

Начиная с Linux 3.4, появился способ преодоления этой проблемы: вместо PTRACE_CONT для перезапуска трассируемой нити можно использовать PTRACE_LISTEN, при которой она не выполняется, а ждёт нового события, и это можно прочитать с помощью waitpid(2) (например, когда был перезапуск по SIGCONT).

### Остановки PTRACE_EVENT
Если трассировщик устанавливает флаги PTRACE_O_TRACE_*, то трассируемая нить будет входить в ptrace-stop-ы, называемые остановками PTRACE_EVENT.

Остановки PTRACE_EVENT обнаруживаются трассировщиком когда waitpid(2) возвращается с WIFSTOPPED(status) и WSTOPSIG(status) возвращает SIGTRAP. В старшем байте слова состояния устанавливается дополнительный бит: значение status>>8 будет равно

(SIGTRAP | PTRACE_EVENT_foo << 8).

Могут происходить следующие события:

PTRACE_EVENT_VFORK

Остановка перед возвратом из vfork(2) или clone(2) с флагом CLONE_VFORK. Когда трассируемая нить продолжает выполняться после этой остановки, она будет ждать выхода/exec потомка перед продолжением своего исполнения (другими словами, обычное поведение при vfork(2)).

PTRACE_EVENT_FORK

Остановка перед возвратом из fork(2) или clone(2) с установленным сигналом выхода SIGCHLD.

PTRACE_EVENT_CLONE

Остановка перед возвратом из clone(2).

PTRACE_EVENT_VFORK_DONE

Остановка перед возвратом из vfork(2) или clone(2) с установленным флагом CLONE_VFORK, но после того, как потомок разблокирует эту трассируемую нить, завершив работу или выполнив exec.

Для всех четырёх остановок, описанных выше, остановка происходит в родителе (т. е., трассируемой нити), а не в только что созданной нити. Для получения ID новой нити может использовать PTRACE_GETEVENTMSG.

PTRACE_EVENT_EXEC

Остановка перед возвратом из execve(2). Начиная с Linux 3.0, PTRACE_GETEVENTMSG возвращает ID бывшей нити.

PTRACE_EVENT_EXIT

Остановка перед выходом (включая уничтожение из exit_group(2)), уничтожение от сигнала или выход, вызванный execve(2) в многонитевом процессе. PTRACE_GETEVENTMSG возвращает код выхода. Можно прочитать значения регистров (в отличие от случая, когда происходит «реальный» выход). Трассируемая нить всё ещё существует; для завершения выхода должно быть выполнено отсоединение с помощью PTRACE_CONT или PTRACE_DETACH.

PTRACE_EVENT_STOP

Остановка вызвана командой PTRACE_INTERRUPT, или group-stop, или начальным ptrace-stop при присоединении к новому потомку (только если для присоединения использовалась PTRACE_SEIZE).

PTRACE_EVENT_SECCOMP

Остановка вызвана правилом seccomp(2) из-за элемента syscall трассируемого, когда PTRACE_O_TRACESECCOMP была установлена трассировщиком. Данные сообщения события seccomp (из части SECCOMP_RET_DATA фильтрующего правила seccomp) можно получить с помощью PTRACE_GETEVENTMSG. Семантика данного останова подробно описана в отдельном разделе далее.

PTRACE_GETSIGINFO при остановке PTRACE_EVENT возвращает SIGTRAP в si_signo, а значение si_code устанавливается в (event<<8) | SIGTRAP.

### Syscall-stop
Если трассируемый был перезапущен по PTRACE_SYSCALL или PTRACE_SYSEMU, то он входит в режим syscall-enter-stop сразу перед тем как войти в какой-либо системный вызов (который не будет выполняться, если перезапуск был из-за PTRACE_SYSEMU, независимо от каких-либо изменений в регистрах на этот момент или если трассируемый перезапускается после своей остановки). Неважно какой метод вызвал syscall-entry-stop, если трассировщик перезапускает трассируемого с помощью PTRACE_SYSCALL, то трассируемый входит в syscall-exit-stop при окончании системного вызова, или если он прерывается сигналом (то есть, signal-delivery-stop никогда не возникает между syscall-enter-stop и syscall-exit-stop; он возникает после syscall-exit-stop). Если трассируемый продолжает выполнение с помощью какого-то другого метода (включая PTRACE_SYSEMU), то syscall-exit-stop не возникает. Заметим, что все упоминания PTRACE_SYSEMU применяются и к PTRACE_SYSEMU_SINGLESTEP.

Однако даже если трассируемый продолжил выполнение с помощью PTRACE_SYSCALL, это не гарантирует, что следующая остановка будет syscall-exit-stop. Также, трассируемый может остановиться в остановке PTRACE_EVENT (включая остановку seccomp), при выходе (если он вошёл в _exit(2) или exit_group(2)), уничтожении по сигналу SIGKILL, или тихом уничтожении (если он является лидером группы нитей, возникает execve(2) в другой нити, и эта нить не трассируется тем же трассировщиком; эта ситуация описана далее).

Syscall-enter-stop и syscall-exit-stop обнаруживаются трассировщиком из waitpid(2), возвращающем истинное значение WIFSTOPPED(status) и WSTOPSIG(status) выдающем SIGTRAP. Если трассировщиком был установлен флаг PTRACE_O_TRACESYSGOOD, то WSTOPSIG(status) выдаст значение (SIGTRAP | 0x80).

Syscall-stop можно отличить от signal-delivery-stop по SIGTRAP, запросив PTRACE_GETSIGINFO в следующих случаях:

si_code <= 0

SIGTRAP был доставлен в результате действия из пространства пользователя, например, системного вызова (tgkill(2), kill(2), sigqueue(3) и т. д.), истечении таймера POSIX, изменении состояния очереди сообщений POSIX или выполнении асинхронного запроса ввода/вывода.

si_code == SI_KERNEL (0x80)

SIGTRAP был послан ядром.

si_code == SIGTRAP или si_code == (SIGTRAP|0x80)

Это syscall-stop.

Однако, syscall-stop происходят очень часто (дважды за системный вызов), и выполнение PTRACE_GETSIGINFO для каждого syscall-stop может быть отчасти накладно.

Некоторые архитектуры позволяют отличать эти случаи по значениям регистров. Например, на x86, при syscall-enter-stop rax == -ENOSYS. Так как SIGTRAP (как и любой сигнал) всегда возникает после syscall-exit-stop, и в этот момент rax почти никогда не содержит ENOSYS, SIGTRAP выглядит как «syscall-stop, который не syscall-enter-stop»; другими словами, это выглядит как «блуждающий syscall-exit-stop» и таким способом может быть обнаружен. Но определение этим способом очень ненадёжно и лучше его не использовать.

Использование флага PTRACE_O_TRACESYSGOOD — рекомендуемый метод различения syscall-stop от похожих на них других ptrace-stop, так как это надёжно и не приводит к ухудшению производительности.

Syscall-enter-stop и syscall-exit-stop неотличимы друг от друга трассировщиком. Трассировщику требуется отслеживать последовательность ptrace-stop, чтобы правильно истолковать syscall-enter-stop как syscall-exit-stop или наоборот. В общем, за syscall-enter-stop всегда следует syscall-exit-stop, PTRACE_EVENT остановка или уничтожение трассируемого; никаких других ptrace-stop не может возникнуть между ними. Однако заметим, что остановки seccomp (смотрите ниже) могут приводить к syscall-exit-stop без предварительного syscall-entry-stop. Если используется seccomp, необходима осторожность чтобы не посчитать такие остановки за syscall-entry-stop.

Если после syscall-enter-stop трассировщик использует перезапускающее действие, отличное от PTRACE_SYSCALL, то syscall-exit-stop не генерируется.

PTRACE_GETSIGINFO при syscall-stop возвращает SIGTRAP в si_signo, значение si_code устанавливается в SIGTRAP или (SIGTRAP|0x80).

### Остановки PTRACE_EVENT_SECCOMP (Linux 3.5 по 4.7)
Поведение остановок PTRACE_EVENT_SECCOMP и их взаимодействие с другими видами остановок ptrace различается от версии к версии. Здесь описано поведение от их появления до Linux 4.7 (включительно). Поведение в более новых версиях описано в следующем разделе.

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

Перезапуск из этой остановки будет вести себя как если бы остановка возникла прямо перед рассматриваемым системным вызовом. В частности, PTRACE_SYSCALL и PTRACE_SYSEMU как обычно вызовут последующий syscall-entry-stop. Однако, если после PTRACE_EVENT_SECCOMP номер системного вызова отрицательный, то и syscall-entry-stop и сам системный вызов будут пропущены. Это означает, что если номер системного вызова отрицательный после PTRACE_EVENT_SECCOMP и трассируемый перезапущен с помощью PTRACE_SYSCALL, то следующая наблюдаемая остановка будет syscall-exit-stop, а не syscall-entry-stop, как можно было ожидать.

### Остановки PTRACE_EVENT_SECCOMP (начиная с Linux 4.8)
Начиная с Linux 4.8, остановка PTRACE_EVENT_SECCOMP была преобразована при возникновении между syscall-entry-stop и syscall-exit-stop. Заметим, что seccomp больше не выполняется (и PTRACE_EVENT_SECCOMP не будет выдан), если системный вызов пропускается из-за PTRACE_SYSEMU.

Функционально, остановка PTRACE_EVENT_SECCOMP работает как syscall-entry-stop (т. е., продолжение использования PTRACE_SYSCALL приведёт к syscall-exit-stop, может измениться номер системного вызова и любые другие регистры, видимые выполняемому в будущем системному вызову). Заметим, что это может быть, но не обязательно предваряется syscall-entry-stop.

После остановки PTRACE_EVENT_SECCOMP, seccomp будет выполнен повторно с правилом SECCOMP_RET_TRACE, которое теперь работает также как SECCOMP_RET_ALLOW. Точнее говоря, это означает, что если регистры не изменились во время остановки PTRACE_EVENT_SECCOMP,то после этого системный вызов будет разрешён.

### Остановки PTRACE_SINGLESTEP
[Пока не описаны.]

### Информационные и перезапускающие действия ptrace
Для большинства действий ptrace (все, за исключением PTRACE_ATTACH, PTRACE_SEIZE, PTRACE_TRACEME, PTRACE_INTERRUPT и PTRACE_KILL) требуется, чтобы трассируемая нить была в режиме ptrace-stop, в противном случае они завершаются с ошибкой ESRCH.

Когда трассируемая нить в ptrace-stop, трассировщик может читать и записывать данные в трассируемую нить с помощью информационных действий. Эти действия оставляют трассируемую нить в состоянии ptrace-stop:

```
ptrace(PTRACE_PEEKTEXT/PEEKDATA/PEEKUSER, pid, addr, 0);
ptrace(PTRACE_POKETEXT/POKEDATA/POKEUSER, pid, addr, long_val);
ptrace(PTRACE_GETREGS/GETFPREGS, pid, 0, &struct);
ptrace(PTRACE_SETREGS/SETFPREGS, pid, 0, &struct);
ptrace(PTRACE_GETREGSET, pid, NT_foo, &iov);
ptrace(PTRACE_SETREGSET, pid, NT_foo, &iov);
ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo);
ptrace(PTRACE_SETSIGINFO, pid, 0, &siginfo);
ptrace(PTRACE_GETEVENTMSG, pid, 0, &long_var);
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_flags);
```

Заметим, что о некоторых ошибках не сообщается. Например, установка информации о сигнале (siginfo) может никак не отразиться в некоторых ptrace-stop, при этом вызов может завершиться без ошибок (возвращается 0 и значение errno не устанавливается); действие PTRACE_GETEVENTMSG может выполниться без ошибок и вернуть произвольное значение, если текущий ptrace-stop не описан как возвращающий какое-то осмысленное сообщение о событии.

Вызов

ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_flags);

затрагивает одну трассируемую нить. Текущие флаги трассируемой нити заменяются. Флаги, наследуемые новой трассируемой нитью, создаются и «автоматически присоединяются» через активные флаги PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK или PTRACE_O_TRACECLONE.

Другая группа действий заставляет трассируемую нить, находящуюся в ptrace-stop, выполняться. Они могут иметь вид:

ptrace(cmd, pid, 0, sig);

где значение cmd равно PTRACE_CONT, PTRACE_LISTEN, PTRACE_DETACH, PTRACE_SYSCALL, PTRACE_SINGLESTEP, PTRACE_SYSEMU или PTRACE_SYSEMU_SINGLESTEP. Если трассируемая нить в signal-delivery-stop, то в sig указывается сигнал, который будет внедрён (если не равен нулю). В противном случае, sig может игнорироваться (при перезапуске трассируемой нити из ptrace-stop в отличный от signal-delivery-stop, рекомендуется передавать в sig значение 0).

### Присоединение и отсоединение
Нить можно присоединить к трассировщику с помощью вызова

ptrace(PTRACE_ATTACH, pid, 0, 0);

или

ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_flags);

PTRACE_ATTACH посылает в нить SIGSTOP. Если трассировщик хочет отменить действие SIGSTOP, ему нужно его подавить. Заметим, что если при присоединении в эту нить в тоже время посылаются другие сигналы, то трассировщик может увидеть, что трассируемая нить сначала вошла в signal-delivery-stop из этих сигналов! Обычной практикой является повторное внедрение этих сигналов до тех пор, пока не будет обнаружен SIGSTOP, а затем подавление внедрения SIGSTOP. Здесь есть ошибка в проектировании в том, что присоединение ptrace и одновременно доставляемый SIGSTOP могут состязаться и одновременный SIGSTOP может быть утерян.

Так как при присоединении посылается SIGSTOP и трассировщик обычно подавляет его, то это может привести к блуждающему возврату EINTR из в данный момент выполняемого системного вызова в трассируемой нити, как описано в разделе «Внедрение и отмена сигнала».

Начиная с Linux 3.4, вместо PTRACE_ATTACH можно использовать PTRACE_SEIZE. PTRACE_SEIZE не останавливает присоединённый. Если вам нужно остановить его после присоединения (или в любое другое время) без отправки каких-либо, используйте действие PTRACE_INTERRUPT.

Запрос

ptrace(PTRACE_TRACEME, 0, 0, 0);

включает трассировку вызвавшей нити. Нить продолжает выполняться (не входит в ptrace-stop). Обычно, за PTRACE_TRACEME следует

raise(SIGSTOP);

и это позволяет родителю (который теперь трассировщик) отследить signal-delivery-stop.

Если включены флаги PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK или PTRACE_O_TRACECLONE, то потомок, создаваемый, соответственно, vfork(2) или clone(2) с флагом CLONE_VFORK, fork(2) или clone(2) с установленным выходным сигналом равным SIGCHLD, и другими видами clone(2), автоматически присоединяется к тому же трассировщику, которой трассирует их родителя. Сигнал SIGSTOP доставляется потомку, заставляя его войти в signal-delivery-stop после завершения системного вызова, который его создал.

Отсоединение от трассируемой нити выполняется с помощью:

ptrace(PTRACE_DETACH, pid, 0, sig);

PTRACE_DETACH является перезапускающей операцией, поэтому она требует, чтобы трассируемая нить была в ptrace-stop. Если трассируемая нить в signal-delivery-stop, то может быть внедрён сигнал. В противном случае параметр sig может быть проигнорирован.

Если трассировщик хочет отсоединиться,а трассируемая нить выполняется, то решением является посылка сигнала SIGSTOP (с помощью tgkill(2), чтобы точно достичь текущей нити), ожидание пока трассируемая нить не войдёт в signal-delivery-stop для доставки SIGSTOP и затем отсоединение от неё (подавив внедрение SIGSTOP). Ошибкой проектирования является возможность гонок с параллельно отправленными SIGSTOP. Другая трудность в том, что трассируемая нить может войти в другой ptrace-stop и потребуется его перезапуск и повторное ожидание появления SIGSTOP. Ещё одной сложностью является в проверки, что трассируемая нить уже не в ptrace-stop, так как в этом случае доставки сигнала не происходит — даже SIGSTOP.

Если трассировщик завершает работу, то все трассируемые нити автоматически отсоединяются и перезапускаются, если они не в group-stop. Выполнение перезапуска из group-stop в настоящее время содержит ошибки, но «плановым» поведением считается оставить трассируемую нить остановленной и подождать SIGCONT. Если трассируемая нить перезапускается из signal-delivery-stop, то внедряется ожидающий сигнал.

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

- Все остальные нити останавливаются в останове PTRACE_EVENT_EXIT, если включён флаг PTRACE_O_TRACEEXIT. Затем все остальные нити, за исключением лидера группы нитей, сообщают о завершении, как если бы они кончили работу с помощью _exit(2) с кодом выхода 0.

- У исполняемой трассируемой нити изменяется ID, так как она выполняет execve(2) (помните, что в ptrace «pid», возвращаемый из waitpid(2) или подаваемый в вызовы ptrace, это ID трассируемой нити). То есть ID трассируемой нити сбрасывается в значение ID своего процесса, который равен ID лидера группы нитей.

- Затем происходит остановка PTRACE_EVENT_EXEC, если включён флаг PTRACE_O_TRACEEXEC.

- Если в это время лидер группы нитей сообщил о своей остановке PTRACE_EVENT_EXIT в это время, то трассировщику кажется, что завершившийся лидер группы «возник из ниоткуда» (замечание: лидер группы нитей не сообщает о завершении через WIFEXITED(status) до тех пор, пока есть одна работающая нить. Это не даёт возможности трассировщику увидеть его завершение и повторное появление). Если лидер группы нитей всё ещё выполнялся, то для трассировщика может казаться, что лидер группы нитей вернулся из другого системного вызова в который входил, или даже «вернулся из системного вызова, хотя не был ни в каком системном вызове». Если лидер группы нитей не трассируется (или трассируется другим трассировщиком), то во время execve(2) он выглядит так, как если бы стал трассируемым трассировщиком выполняющейся трассируемой нити.

Все перечисленные выше эффекты происходят из-за смены ID трассируемой нити.

В этой ситуации рекомендуется использовать флаг PTRACE_O_TRACEEXEC. Во-первых, он включает остановку PTRACE_EVENT_EXEC, которая происходит перед возвратом из execve(2). В этой остановке трассировщик может использовать PTRACE_GETEVENTMSG для получения предыдущего ID трассируемой нити (эта возможность появилась в Linux 3.0). Во-вторых, флаг PTRACE_O_TRACEEXEC отключает устаревшую генерацию SIGTRAP при execve(2).

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

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

ID нити == ID группы нитей == ID процесса.

Пример: две нити вызывают execve(2) одновременно:

```
*** мы получаем syscall-enter-stop в нити 1: **
PID1 execve("/bin/foo", "foo" <unfinished ...>
*** мы выдаём PTRACE_SYSCALL для нити 1 **
*** мы получаем syscall-enter-stop в нити 2: **
PID2 execve("/bin/bar", "bar" <unfinished ...>
*** мы выдаём PTRACE_SYSCALL для нити 2 **
*** мы получаем PTRACE_EVENT_EXEC for PID0, мы выдаём PTRACE_SYSCALL **
*** мы получаем syscall-exit-stop для PID0: **
PID0 <... execve resumed> )             = 0
```

Если флаг PTRACE_O_TRACEEXEC не действует на выполняющуюся трассируемую нить и если трассируемая нить подключена с помощью PTRACE_ATTACH, а не PTRACE_SEIZE, то ядро доставляет ей дополнительный SIGTRAP после возврата из execve(2). Это обычный сигнал (похожий на тот, который генерируется с помощью kill -TRAP), а не какая-то специальная разновидность ptrace-stop. Выдача PTRACE_GETSIGINFO для этого сигнала возвращает si_code равный 0 (SI_USER). Этот сигнал может быть блокирован маской сигналов и поэтому может быть доставлен (намного) позже.

Обычно, трассировщик (например, strace(1)) не хотел бы показывать этот дополнительный пост-execve SIGTRAP сигнал пользователю, и хотел бы подавить его доставку в трассируемую нить (если обработчик SIGTRAP равен SIG_DFL, то это уничтожающий сигнал). Однако, определить какой SIGTRAP подавлять, нелегко. Рекомендуется установить флаг PTRACE_O_TRACEEXEC или PTRACE_SEIZE и затем подавить этот дополнительный SIGTRAP.

### Настоящий родитель
Программный интерфейс ptrace использует стандартный обмен сигналами UNIX между родителем и потомком через waitpid(2). Это приводит к тому, что настоящий родитель процесса перестаёт получать некоторые виды уведомлений waitpid(2), когда дочерний процесс трассируется другим процессом.

Многие из этих дефектов были исправлены, но на момент версии Linux 2.6.38 некоторые из них всё ещё существуют; смотрите ДЕФЕКТЫ далее.

На момент версии Linux 2.6.38 работает правильно:

*

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

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении запросы PTRACE_PEEK* возвращают запрашиваемые данные (но смотрите ЗАМЕЧАНИЯ), запрос PTRACE_SECCOMP_GET_FILTER возвращает количество инструкций в программе BPF, а другие запросы возвращают ноль.

При ошибке для всех действий возвращается -1, а переменной errno присваивается номер ошибки. Так как значение, возвращаемое при удачном выполнении PTRACE_PEEK*, может равняться -1, перед вызовом вызывающий должен очистить содержимое errno, чтобы узнать, возникала ошибка или нет.

## ОШИБКИ
EBUSY

(только для i386) Произошла ошибка при размещении или освобождении отладочного регистра.

EFAULT

Была сделана попытка чтения или записи информации в область памяти трассируемой нити или трассировщика, но, скорее всего, эта память не отображена или недоступна. К сожалению, в Linux в разных ситуациях в результате этой ошибки возвращаются значения EIO или EFAULT, что не всегда поддается объяснению.

EINVAL

Попытка установить недопустимое значение.

EIO

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

EPERM

Указанный процесс не может быть трассирован. Это может произойти потому, что трассировщик не имеет прав на трассировку (требуется мандат CAP_SYS_PTRACE); непривилегированные процессы не могут трассировать процессы, так как они не могут посылать сигналы, или если у них установлен set-user-ID/set-group-ID бит. Также, процесс может уже трассироваться или (на ядрах до версии 2.6.26) быть init(1) (PID 1).

ESRCH

Указанный процесс не существует, в данный момент не трассируется вызывающим процессом, или не остановлен (для выполнения действий, которые требуют остановки трассируемой нити).

## СООТВЕТСТВИЕ СТАНДАРТАМ
SVr4, 4.3BSD.

## ЗАМЕЧАНИЯ
Хотя параметры ptrace() воспринимаются согласно заданному прототипу, в настоящее время в glibc ptrace() объявлена как функция с переменным числом параметров, в которой фиксирован только параметр request. Рекомендуется всегда передавать четыре параметра, даже если в запрашиваемом действии они не используются (неиспользуемые аргументы указывайте как 0L или (void *) 0).

В ядрах Linux до версии 2.6.26, процесс init(1) с PID 1 не может быть трассирован.

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

Структура памяти и области USER зависят от ОС и архитектуры системы. Указываемое смещение и возвращаемые данные могут не полностью соответствовать определению struct user.

Размер «слова» определяется вариантом ОС (например, для 32-битного варианта Linux слово будет 32-битным).

Эта страница описывает работу системного вызова ptrace() в Linux. Его работа значительно отличается от поведения в других системах UNIX. В любом случае, использование ptrace() очень сильно зависит от ОС и архитектуры.

### Проверка режима доступа ptrace
Для различных частей программного интерфейса ядро — пользовательское пространство (не только операции ptrace()) требуются так называемые проверки «режима доступа ptrace», чьими результатами определяется будет ли разрешена операция (или это, в некоторых случаях, заставит операцию «чтения» цензурировать возвращаемые данные). Эти проверки выполняются в случаях, когда один процесс может просмотреть конфиденциальную информацию или, в некоторых случаях, изменять состояние другого процесса. Проверки основываются на учётных данных и мандатах двух процессов, разрешено ли создание дампа «целевого» процесса, а результаты проверок выполняются любым включённым модулем безопасности Linux (LSM) — например, SELinux, Yama и Smack — и commoncap LSM (который вызывается всегда).

До Linux 2.6.27 все проверки доступа были одного вида. Начиная с Linux 2.6.27 различают два уровня проверок доступа:

PTRACE_MODE_READ

Для операций «чтения» или других менее опасных операций: get_robust_list(2); kcmp(2); чтение /proc/[pid]/auxv, /proc/[pid]/environ или /proc/[pid]/stat, или readlink(2) для файла /proc/[pid]/ns/*.

PTRACE_MODE_ATTACH

Для операций «записи» или других более опасных операций: присоединение ptrace (PTRACE_ATTACH) к другому процессу или вызов process_vm_writev(2) (PTRACE_MODE_ATTACH было эффективным значением по умолчанию до Linux 2.6.27).

Начиная с Linux 4.5 приведённые выше проверки режима доступа объединяются (ИЛИ) с одним из следующих модификаторов:

PTRACE_MODE_FSCREDS

Использовать UID и GID файловой системы у вызывающего (смотрите credentials(7)) или эффективные мандаты для проверок LSM.

PTRACE_MODE_REALCREDS

Использовать реальные UID и GID у вызывающего или разрешающие мандаты для проверок LSM. Это было эффективным значением по умолчанию до Linux 4.5.

Так как объединение модификаторов мандатов с одной из вышеупомянутых режимов доступа часто используется, то в исходном коде ядра было определено несколько макросов:

PTRACE_MODE_READ_FSCREDS

Определён как PTRACE_MODE_READ | PTRACE_MODE_FSCREDS.

PTRACE_MODE_READ_REALCREDS

Определён как PTRACE_MODE_READ | PTRACE_MODE_REALCREDS.

PTRACE_MODE_ATTACH_FSCREDS

Определён как PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS.

PTRACE_MODE_ATTACH_REALCREDS

Определён как PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS.

Один из следующих модификаторов может быть объединённых с режимом доступа:

PTRACE_MODE_NOAUDIT (начиная с Linux 3.3)

Не протоколировать (audit) проверку режима доступа. Данный модификатор применяется для проверок режима доступа ptrace (например, проверки при чтении /proc/[pid]/stat), которые просто фильтруют или цензурируют вывод, вместо возврата ошибки вызывающему. В этих случаях, доступ к файлу не нарушает правила безопасности и нет причины генерировать запись о нарушении. Данный модификатор отключает создание подобных протокольных записей для определённых проверок доступа.

Заметим, что все константы PTRACE_MODE_*, описанные в данном разделе, доступны только для ядра и не видны из пользовательского пространства. Имена упомянутых констант служат обозначением различных видов проверок режима доступа ptrace, которые выполняются для разных системных вызовов и получении доступа к разным псевдо-файлам (например, в каталоге /proc). Эти имена используются в других справочных страницах как простые сокращения различных проверок ядра.

Задействуемый алгоритм проверок режима доступа ptrace определяется по разрешению вызывающему процессу выполнять соответствующее действие над процессом назначения (в случае открытия файлов /proc/[pid] «вызывающий процесс» это открывающий файл, а процесс с соответствующим PID — «процесс назначения»). Возможные алгоритмы:

1.

Если вызывающая нить и нить назначения в одной группе нитей, то доступ всегда разрешён.

2.

Если режим доступа равен PTRACE_MODE_FSCREDS, то для проверки на следующем шаге используется UID и GID файловой системы у вызывающего (как упомянуто в credentials(7), UID и GID файловой системы почти всегда равны значениям соответствующих эффективных идентификаторов).

В противном случае режим доступа равен PTRACE_MODE_REALCREDS, и на следующем шаге проверок используются реальные UID и GID вызывающего (большинство программных интерфейсов, которые проверяют UID и GID вызывающего, используют эффективные идентификаторы. В проверке PTRACE_MODE_REALCREDS используются реальные ID только в силу исторических причин).

3.

Запрещается доступ, если срабатывает любое из этих правил:

- Реальные, эффективные и сохранённые пользовательские ID назначения совпадают с пользовательским ID вызывающего, и Реальные, эффективные и сохранённые групповые ID назначения совпадают с групповым ID вызывающего.

- Вызывающий имеет мандат CAP_SYS_PTRACE в пользовательском пространстве имён назначения.

4.

Доступ запрещается, если у процесса назначения значение атрибута «возможности дампа» не равно 1 (SUID_DUMP_USER; смотрите обсуждение PR_SET_DUMPABLE в prctl(2)), и вызывающий не имеет мандата CAP_SYS_PTRACE в пользовательском пространстве имён назначения.

5.

Для проверки того, что доступ ptrace разрешён, вызывается интерфейс ядра LSM security_ptrace_access_check(). Результат зависит от LSM. Реализация интерфейса в commoncap LSM выполняет следующие шаги:

а)

Если режим доступа включает PTRACE_MODE_FSCREDS, то используется эффективный набор мандатов вызывающего в последующей проверке; в противном случае (режим доступа равен PTRACE_MODE_REALCREDS) используется допускающий набор мандатов.

б)

Запрещается доступ, если срабатывает любое из этих правил:

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

- Вызывающий имеет мандат CAP_SYS_PTRACE в пользовательском пространстве имён процесса назначения.

Заметим, что commoncap LSM не различает PTRACE_MODE_READ и PTRACE_MODE_ATTACH.

6.

Если доступ не был запрещён в предыдущих шагах, то доступ разрешается.

### /proc/sys/kernel/yama/ptrace_scope
В системах с установленным модулем Yama Linux Security Module (LSM) (т. е., ядро было настроено с параметром CONFIG_SECURITY_YAMA) можно использовать файл /proc/sys/kernel/yama/ptrace_scope (доступен, начиная с Linux 3.4) для того, чтобы огранить возможность трассировки процесса с помощью ptrace() (то есть ограничить использование таких инструментов как strace(1) и gdb(1)). Целью ограничения является предотвращение возможности атаки, посредством которой скомпроментированный процесс подключается через ptrace к другим ответственным процессам (например, к агенту GPG или сеансу SSH), принадлежащим пользователю, чтобы получить дополнительные полномочия (которые могут существовать в памяти) и, таким образом, расширить атакуемое пространство.

Более точно, Yama LSM ограничивает два типа операций:

- Все операции, которые выполняют проверку режима доступа ptrace PTRACE_MODE_ATTACH, например, ptrace() PTRACE_ATTACH (смотрите «Проверка режима доступа ptrace» выше).

- ptrace() PTRACE_TRACEME.

Процесс, имеющий мандат CAP_SYS_PTRACE, может записать в файл /proc/sys/kernel/yama/ptrace_scope одно из следующих значений:

0 («обычные права ptrace»)

Без дополнительных ограничений на операции, выполняющие проверки PTRACE_MODE_ATTACH (кроме накладываемых commoncap и другими LSM).

Использование PTRACE_TRACEME не изменяется.

1 («ограниченный ptrace») [значение по умолчанию]

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

Процесс назначения может выполнить операцию prctl(2) PR_SET_PTRACER для объявления дополнительного PID, которому разрешено выполнять операции PTRACE_MODE_ATTACH над процессом назначения. Подробности смотрите в файле исходного кода ядра Documentation/admin-guide/LSM/Yama.rst (или Documentation/security/Yama.txt до Linux 4.13).

Использование PTRACE_TRACEME не изменяется.

2 («только администраторское присоединение»)

Только процессы с мандатом CAP_SYS_PTRACE в пользовательском пространстве имён процесса назначения могут выполнять операции PTRACE_MODE_ATTACH или трассировать потомков, выполнивших PTRACE_TRACEME.

3 («присоединение заблокировано»)

Никакие процессы не могут выполнять операции PTRACE_MODE_ATTACH или трассировать потомков, выполнивших PTRACE_TRACEME.

После записи такого значения в файл, его нельзя изменить.

Относительно значений 1 и 2 заметим, что создание нового пользовательского пространства имён фактически удаляет защиту, предлагаемую Yama. Это происходит из-за того, что процесс в родительском пользовательском пространстве имён, у которого эффективный UID совпадает с UID создателя дочернего пространства имён, имеет все мандаты (включая CAP_SYS_PTRACE) при выполнении им операций внутри дочернего пользовательского пространства имён (вплоть до удаления потомков этого пространства имён). В результате, когда процесс сам пытается использовать пользовательские пространства имён для песочницы, это непреднамеренно ослабляет защиту, предлагаемую Yama LSM.

### Отличия между библиотекой C и ядром
На уровне системных вызовов запросы PTRACE_PEEKTEXT, PTRACE_PEEKDATA и PTRACE_PEEKUSER имеют разный программный интерфейс: они сохраняют результат по адресу, указанному в параметре data, а возвращаемое значение является индикатором ошибки. Обёрточная функция glibc предоставляет программный интерфейс, описанные ОПИСАНИЕ выше, а результат возвращается в виде возвращаемого значения функции.

## ДЕФЕКТЫ
На машинах с заголовочными файлами ядра 2.6 значение PTRACE_SETOPTIONS отличается от использованного в версии 2.4. Это приводит к тому, что приложения, скомпилированные с такими заголовочными файлами, не работают при использовании ядер 2.4. Этого можно избежать сделав PTRACE_SETOPTIONS равным PTRACE_OLDSETOPTIONS, если данная константа определена.

Уведомления group-stop посылаются трассировщику, но не реальному родителю. Последнее подтверждение в версии 2.6.38.6.

Если трассируется лидер группы нитей и завершается с помощью вызова _exit(2), то происходит его останов PTRACE_EVENT_EXIT (если это запрашивалось), но последующее уведомление WIFEXITED не будет доставлено пока все остальные нити не завершат работу. Как объяснялось выше, если одна из остальных нитей вызывает execve(2), то о завершении лидера группы никогда не будет сообщено. Если исполняемая нить не трассируется этим трассировщиком, то трассировщик никогда не узнает, что происходил execve(2). Одним из обходных вариантов решения в этом случае является выполнение PTRACE_DETACH для лидера группы вместо перезапуска. Последнее подтверждение в версии 2.6.38.6.

Сигнал SIGKILL всё ещё может вызвать остановку PTRACE_EVENT_EXIT перед настоящем завершением процесса по сигналу. Это поведение может измениться в будущем; SIGKILL всегда подразумевает немедленное завершение задач даже под ptrace. Последняя подтверждённая версия Linux — 3.13.

Некоторые системные вызовы возвращаются с EINTR, если сигнал был послан трассируемой нити, но доставка была подавлена трассировщиком (это очень распространённая операция: она обычно выполняется отладчиками при каждом присоединении, чтобы не вызывать ненужный SIGSTOP). Начиная с Linux 3.2.9, подвержены следующие системные вызовы (вероятно, это не полный список): epoll_wait(2) и read(2) из файлового дескриптора inotify(7). Обычный симптом этой ошибки: когда вы присоединяетесь к неактивному процессу с помощью команды

```
strace -p <process-ID>
```

то вместо обычного и ожидаемого вывода одной строки

```
restart_syscall(<... resuming interrupted call ...>_
```

или

```
select(6, [5], NULL, [5], NULL_
```

(«_» означает позицию курсора), вы видите несколько строк. Пример:

```
    clock_gettime(CLOCK_MONOTONIC, {15370, 690928118}) = 0

    epoll_wait(4,_
```

Здесь не видно, что процесс был заблокирован в epoll_wait(2) до того, как strace(1) присоединился к нему. Присоединение заставляет epoll_wait(2) вернуться в пользовательское пространство с ошибкой EINTR. В этом частном случае, программа отвечает на EINTR проверкой текущего времени и затем вызывает epoll_wait(2) снова (программы, которые не ожидают таких «побочных» ошибок EINTR, при присоединении strace(1) могут повести себя непредсказуемо).

В отличие от обычных правил, обёрточная функция glibc для ptrace() может присваивать errno значение нуля.

## СМОТРИТЕ ТАКЖЕ
gdb(1), ltrace(1), strace(1), clone(2), execve(2), fork(2), gettid(2), prctl(2), seccomp(2), sigaction(2), tgkill(2), vfork(2), waitpid(2), exec(3), capabilities(7), signal(7)


package: man-pages-ru

title: query_module(2)

text:
# query_module(2)

## ИМЯ
query_module - запросить ядро о различных параметрах, касающихся модулей

## ОБЗОР

```
#include <linux/module.h>
```

```
int query_module(const char *name, int which, void *buf,
                 size_t bufsize, size_t *ret);
```

Замечание: В заголовочных файлах glibc этой системный вызов отсутствует; смотрите ЗАМЕЧАНИЯ.

## ОПИСАНИЕ
Замечание: Данный системный вызов доступен только в ядрах до Linux 2.6.

query_module() запрашивает информацию у ядра о загружаемых модулях. Возвращаемая информация помещается в буфер, указанный в buf. Вызывающий должен указать размер buf в bufsize. Смысл и формат возвращаемой информации зависит от операции, задаваемой в which. Для некоторых операций требуется заполнить name для указания на уже загруженный модуль, для некоторых в name можно указать NULL, что указывает на получения свойства ядра.

В which можно указать следующие значения:

0

Завершается успешно, если ядро поддерживает query_module(). Используется для проверки доступности системного вызова.

QM_MODULES

Возвращает имена всех загруженных модулей. Возвращаемый буфер содержит последовательность строк, оканчивающихся null; в ret указано количество модулей.

QM_DEPS

Возвращает имена всех модулей, используемых указанным модулем. Возвращаемый буфер содержит последовательность строк, оканчивающихся null; в ret указано количество модулей.

QM_REFS

Возвращает имена всех модулей, использующих указанный модуль. Это обратная по отношению к QM_DEPS операция. Возвращаемый буфер содержит последовательность строк, оканчивающихся null; в ret указано количество модулей.

QM_SYMBOLS

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

```
struct module_symbol {

    unsigned long value;

    unsigned long name;
};

```

завершаемого строками, оканчивающимися null. Значение name содержит смещение строки в знаках относительно начала buf; в ret указано количество символов.

QM_INFO

Возвращает различную информацию об указанном модуле. Формат результата в буфере:

```
struct module_info {

    unsigned long address;

    unsigned long size;

    unsigned long flags;
};

```

где address — это адрес ядра, где располагается модуль, size — размер модуля в байтах, flags — маска MOD_RUNNING, MOD_AUTOCLEAN и т. п., показывающая текущее состояние модуля (смотрите файл исходного кода ядра Linux include/linux/module.h). В ret указан размер структуры module_info.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении возвращается ноль. В случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.

## ОШИБКИ
EFAULT

Одно из значений в name, buf или ret находится вне доступного программного адресного пространства.

EINVAL

Неправильное значение which; name равно NULL (указывающее "параметры ядра"), но оно не разрешено для указанного значения which.

ENOENT

Модуль с именем, указанным в name, не существует.

ENOSPC

Указанный размер буфера слишком мал. В ret возвращается минимальный необходимый размер.

ENOSYS

query_module() не поддерживается в этой версии ядра (например, в 2.6 или более поздних).

## ВЕРСИИ
Данный системный вызов есть только в Linux до версии 2.4; он был удалён в Linux 2.6.

## СООТВЕТСТВИЕ СТАНДАРТАМ
query_module() есть только в Linux.

## ЗАМЕЧАНИЯ
Некоторую информацию, которая была ранее доступна через query_module(), можно получить из /proc/modules, /proc/kallsyms и файлов из каталога /sys/modules.

Системный вызов query_module() не поддерживается glibc. В заголовочных файлах glibc он не объявлен, но в недавнем прошлом glibc экспортировал ABI для этого системного вызова. Поэтому чтобы получить данный системный вызов достаточно вручную объявить интерфейс в своём коде; или же вы можете вызвать его через syscall(2).

## СМОТРИТЕ ТАКЖЕ
create_module(2), delete_module(2), get_kernel_syms(2), init_module(2), lsmod(8), modinfo(8)


quotactl

title: quotactl(2)

summary: управление дисковыми квотами

text:
# quotactl(2)

## ИМЯ
quotactl - управление дисковыми квотами

## ОБЗОР

```
#include <sys/quota.h>
#include <xfs/xqm.h> /* для квот XFS */
```

```
int quotactl(int cmd, const char *special, int id, caddr_t addr);
```

## ОПИСАНИЕ
С помощью системы квот можно задать каждому пользователю, группе или проекту лимит использования дискового пространства. Для пользователя или группы в каждой файловой системе можно указать необязательный (soft) и обязательный (hard) лимиты. Обязательный лимит не может быть превышен. Необязательный лимит превышать можно, но будет выдано соответствующее предостережение. Более того, пользователь может превышать необязательный лимит только в течении льготного срока (по умолчанию,одна неделя); после этого необязательный лимит будет считаться обязательным.

Управление квотами выполняется с помощью вызова quotactl(). В аргументе cmd задаётся команда, которая должна быть применена для пользовательского или группового идентификатора, указанного в id. Для инициализации значения аргумента cmd используйте макрос QCMD(subcmd, type). Значение type может быть USRQUOTA (для пользовательских квот), GRPQUOTA (для групповых квот) или (начиная с Linux 4.1) PRJQUOTA (для проектных квот). Значение subcmd описано ниже.

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

Аргумент addr представляет собой адрес необязательной, зависящей от команды, структуры данных, которые копируются в или из системы. Интерпретация addr указана ниже (для каждой команды).

Значением subcmd может быть одно из:

Q_QUOTAON

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

QFMT_VFS_OLD

Самая первая версия формата квот.

QFMT_VFS_V0

Стандартный формат квот VFS v0, позволяет работать с 32-битными UID и GID и ограничениями по квотам до 2^42 байт и 2^32 inode.

QFMT_VFS_V1

Данный формат квот позволяет работать с 32-битными UID и GID и ограничениями по квотам до 2^64 байт и 2^64 inode.

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

Q_QUOTAOFF

Выключает учёт квот в файловой системе. Аргументы addr и id игнорируются. Данная операция требует дополнительных прав (CAP_SYS_ADMIN).

Q_GETQUOTA

Возвращает данные по лимитам и текущее значение использованного пространства для пользователя или группы с заданным id. Аргумент addr является указателем на структуру dqblk, определённую в <sys/quota.h> следующим образом:

```
/* uint64_t имеет тип unsigned 64-bit integer;

   uint32_t имеет тип unsigned 32-bit integer */
struct dqblk {      /* определение, действующее с Linux 2.4.22 */

    uint64_t dqb_bhardlimit;  /* абсолютный лимит на выделяемые

                                 блоки дисковых квот */

    uint64_t dqb_bsoftlimit;  /* предпочтительный лимит на выделяемые

                                 блоки дисковых квот */

    uint64_t dqb_curspace;    /* занятое в данный момент пространство

                                 (в байтах) */

    uint64_t dqb_ihardlimit;  /* максимальное количество

                                 выделенных инод */

    uint64_t dqb_isoftlimit;  /* предпочтительный лимит на иноды */

    uint64_t dqb_curinodes;   /* текущее количество

                                 выделенных инод */

    uint64_t dqb_btime;       /* временной лимит по превышению

                                 использования диска */

    uint64_t dqb_itime;       /* временной лимит по превышению

                                 файлов */

    uint32_t dqb_valid;       /* битовая маска констант

                                 QIF_* */
};
/* Флаги в dqb_valid указывают, какие поля в

   структуре dqblk являются рабочими. */
#define QIF_BLIMITS   1
#define QIF_SPACE     2
#define QIF_ILIMITS   4
#define QIF_INODES    8
#define QIF_BTIME     16
#define QIF_ITIME     32
#define QIF_LIMITS    (QIF_BLIMITS | QIF_ILIMITS)
#define QIF_USAGE     (QIF_SPACE | QIF_INODES)
#define QIF_TIMES     (QIF_BTIME | QIF_ITIME)
#define QIF_ALL       (QIF_LIMITS | QIF_USAGE | QIF_TIMES)

```

Поле dqb_valid представляет собой битовую маску, показывающую какие поля в структуре dqblk являются рабочими. В настоящее время ядро заполняется все поля структуры dqblk и маркирует их как рабочие в поле dqb_valid. Непривилегированные пользователи могут получить данные только по своим квотам; привилегированный пользователь (имеющий мандат CAP_SYS_ADMIN) может получить данные по квотам любого пользователя.

Q_GETNEXTQUOTA (начиная с Linux 4.6)

Эта операция подобна Q_GETQUOTA, но возвращает информацию о квоте для следующего ID, большего или равного id, у которого установлена квота.

Аргумент addr представляет собой указатель на структуру nextdqblk с полями как у dqblk, но имеющей дополнительное поле dqb_id, используемое для возврата ID, для которого возвращается информация по квоте:

```
struct nextdqblk {

    uint64_t dqb_bhardlimit;

    uint64_t dqb_bsoftlimit;

    uint64_t dqb_curspace;

    uint64_t dqb_ihardlimit;

    uint64_t dqb_isoftlimit;

    uint64_t dqb_curinodes;

    uint64_t dqb_btime;

    uint64_t dqb_itime;

    uint32_t dqb_valid;

    uint32_t dqb_id;
};

```

Q_SETQUOTA

Устанавливает квоты для пользователя или группы с указанным id, используя информацию из структуры dqblk, на которую указывает addr. Полем dqb_valid в структуре dqblk определяется какие элементы структуры установлены вызывающим. Эта операция заменяет предоставляемые прежде операции работы с квотами Q_SETQLIM и Q_SETUSE. Эта операция требует дополнительных прав (CAP_SYS_ADMIN).

Q_GETINFO (начиная с Linux 2.4.22)

Возвращает информацию (например, льготное время (grace times)) о quotafile. Аргумент addr должен содержать указатель на структуру dqinfo. Эта структура определена в <sys/quota.h> следующим образом:

```
/* uint64_t имеет тип unsigned 64-bit integer;

   uint32_t имеет тип unsigned 32-bit integer */
struct dqinfo {         /* определена начиная с ядра 2.4.22 */

    uint64_t dqi_bgrace;  /* период времени, после которого блоковый

                             необязательный лимит становится обязательным */

    uint64_t dqi_igrace;  /* период времени, после которого инодовый

                             необязательный лимит становится обязательным */

    uint32_t dqi_flags;   /* флаги quotafile

                             (DQF_*) */

    uint32_t dqi_valid;
};
/* биты из dqi_flags */
/* формат квот QFMT_VFS_OLD */
#define DQF_ROOT_SQUASH (1 << 0) /* включено ограничение для */

              /* суперпользователя; до Linux v4.0 это было закрытым

                 определением с именем V1_DQF_RSQUASH */
/* формат квот QFMT_VFS_V0 / QFMT_VFS_V1 */
#define DQF_SYS_FILE    (1 << 16)   /* квота хранится в

                                       системном файле */
/* флаги в dqi_valid, которые показывают какие поля в

   структуре dqinfo рабочие. */
#define IIF_BGRACE  1
#define IIF_IGRACE  2
#define IIF_FLAGS   4
#define IIF_ALL     (IIF_BGRACE | IIF_IGRACE | IIF_FLAGS)

```

Значение поля dqi_valid в структуре dqinfo указывает на рабочие элементы. В настоящее время ядро заполняет все элементы структуры dqinfo и помечает их как рабочие в поле dqi_valid. Аргумент id игнорируется.

Q_SETINFO (начиная с Linux 2.4.22)

Задаёт информацию о quotafile. Значение аргумента addr должно быть указателем на структуру dqinfo. Полем dqi_valid в структуре dqinfo определяется, какие элементы структуры установлены вызывающим. Эта операция заменяет операции Q_SETGRACE и Q_SETFLAGS из предоставляемых прежде операций работы с квотами. Аргумент id игнорируется. Эта операция требует дополнительных прав (CAP_SYS_ADMIN).

Q_GETFMT (начиная с Linux 2.4.22)

Возвращает формат квоты, используемый в указанной файловой системе. В аргументе addr должен содержаться указатель на 4-байтовый буфер, в который будет записан номер формата.

Q_SYNC

Обновляет дисковую копию используемых квот в файловой системе. Если значение special равно NULL, то действующие квоты будут синхронизированы на всех файловых системах. Аргументы addr и id игнорируются.

Q_GETSTATS (поддерживалась до Linux 2.4.21)

Возвращает статистику и другую общую информацию о подсистеме квот. Аргумент addr должен содержать указатель на структуру dqstats, в которую нужно сохранить данные. Эта структура определена в <sys/quota.h>. Аргументы special и id игнорируются.

Эта операция устарела и была удалена в Linux 2.4.22. Информацию можно получить из файлов в /proc/sys/fs/quota/.

Для файловых систем XFS, использующих XFS Quota Manager (XQM), приведённые выше команды не выполняются, а используются следующие команды:

Q_XQUOTAON

Включает квоты в файловой системе XFS. XFS позволяет включать/выключать лимиты по квотам с ведением учёта. Поэтому для XFS в addr ожидается указатель на unsigned int, который представляет собой комбинацию следующих флагов (определены в <xfs/xqm.h>):

```
#define XFS_QUOTA_UDQ_ACCT (1<<0) /* учёт пользовательских

                                     квот */
#define XFS_QUOTA_UDQ_ENFD (1<<1) /* применение лимитов

                                     пользовательских квот */
#define XFS_QUOTA_GDQ_ACCT (1<<2) /* учёт групповых

                                     квот */
#define XFS_QUOTA_GDQ_ENFD (1<<3) /* применение лимитов

                                     групповых квот */
#define XFS_QUOTA_PDQ_ACCT (1<<4) /* учёт проектных

                                     квот */
#define XFS_QUOTA_PDQ_ENFD (1<<5) /* применение лимитов

                                     проектных квот */

```

Для этой операции требуются права (CAP_SYS_ADMIN). Аргумент id игнорируется.

Q_XQUOTAOFF

Выключает квоты для файловой системы XFS. Как в Q_QUOTAON, для файловых систем XFS ожидается указатель на unsigned int, в котором задаётся что нужно отключить: учёт или применение квот (используются флаги из операции Q_XQUOTAON). Эта операция требует прав (CAP_SYS_ADMIN). Аргумент id игнорируется.

Q_XGETQUOTA

Возвращает данные по лимитам и текущее значение использованного пространства для пользователя id. Аргумент addr является указателем на структуру fs_disk_quota, определённую в <xfs/xqm.h> следующим образом:

```
/* все части blk в BB (Basic Blocks)

   размером 512 байт */
#define FS_DQUOT_VERSION  1  /* fs_disk_quota.d_version */
#define XFS_USER_QUOTA    (1<<0)  /* тип пользовательской квоты */
#define XFS_PROJ_QUOTA    (1<<1)  /* тип проектной квоты */
#define XFS_GROUP_QUOTA   (1<<2)  /* тип групповой квоты */
struct fs_disk_quota {

    int8_t   d_version;   /* версия данной структуры */

    int8_t   d_flags;     /* XFS_{USER,PROJ,GROUP}_QUOTA */

    uint16_t d_fieldmask; /* определитель поля */

    uint32_t d_id;        /* ID пользователя, группы или проекта */

    uint64_t d_blk_hardlimit; /* абсолютный лимит на

                                 дисковые блоки */

    uint64_t d_blk_softlimit; /* предпочтительный лимит на

                                 дисковые блоки */

    uint64_t d_ino_hardlimit; /* максимальное кол-во выделяемых

                                 инод */

    uint64_t d_ino_softlimit; /* предпочтительный лимит на иноды */

    uint64_t d_bcount;    /* # кол-во дисковых блоков,

                             принадлежащих пользователю */

    uint64_t d_icount;    /* # кол-во инод, принадлежащих пользователю */

    int32_t  d_itimer;    /* ноль, если лимит на иноды не превышен */

                          /* если нет, то нам отказывают */

    int32_t  d_btimer;    /* подобное предыдущему, но для

                             дисковых блоков */

    uint16_t d_iwarns;    /* кол-во предупреждений в соответствии с

                             кол-вом инод */

    uint16_t d_bwarns;    /* кол-во предупреждений в соответствии с

                             кол-вом дисковых блоков */

    int32_t  d_padding2;  /* заполнитель, для использования в будущем */

    uint64_t d_rtb_hardlimit; /* абсолютный лимит на работу с дисковыми

                                 блоками в реальном времени (RT) */

    uint64_t d_rtb_softlimit; /* предпочтительный лимит на дисковые блоки

                                 в RT */

    uint64_t d_rtbcount;  /* кол-во дисковых блоков под реальное время */

    int32_t  d_rtbtimer;  /* подобное предыдущему, но

                             для дисковых блоков RT */

    uint16_t d_rtbwarns;  /*  кол-во предупреждений в соответствии с

                             дисковыми блоками RT */

    int16_t  d_padding3;  /* заполнитель, для использования в будущем */

    char     d_padding4[8];   /* ещё заполнитель */
};

```

Непривилегированные пользователи могут получить данные только по своим квотам; привилегированный пользователь (с CAP_SYS_ADMIN) может получить информацию о квотах любого пользователя.

Q_XGETNEXTQUOTA (начиная с Linux 4.6)

Эта операция подобна Q_XGETQUOTA, но возвращает (in the fs_disk_quota structure pointed by addr) информацию о квоте для следующего ID, большего или равного id, у которого установлена квота. Заметим, что так как в fs_disk_quota уже есть поле q_id, то отдельного типа для структуры не требуется (как для Q_GETQUOTA и Q_GETNEXTQUOTA).

Q_XSETQLIM

Устанавливает дисковую квоту для пользователя с указанным id. В аргументе addr задаётся указатель на структуру fs_disk_quota. Эта операция требует прав (CAP_SYS_ADMIN).

Q_XGETQSTAT

Возвращает доступную только в XFS информацию о квоте в структуре fs_quota_stat, на которую указывает addr. Это полезно для определения количества пространства, использованного для хранения информации о квоте, а также для получения состояния включения/отключения квоты определённой локальной файловой системы XFS. Структура fs_quota_stat определена следующим образом:

```
#define FS_QSTAT_VERSION 1  /* fs_quota_stat.qs_version */
struct fs_qfilestat {

    uint64_t qfs_ino;       /* номер иноды */

    uint64_t qfs_nblks;     /* количество BB блоков

                               размером 512 байт */

    uint32_t qfs_nextents;  /* количество экстентов */
};
struct fs_quota_stat {

    int8_t   qs_version; /* номер версии

                            для изменений в будущем */

    uint16_t qs_flags; /* XFS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */

    int8_t   qs_pad;   /* не используется */

    struct fs_qfilestat qs_uquota;  /* информация о хранилище

                                       пользовательской квоты */

    struct fs_qfilestat qs_gquota;  /* информация о хранилище

                                       групповой квоты */

    uint32_t qs_incoredqs;   /* количество dquots в ядре */

    int32_t  qs_btimelimit;  /* лимит для таймера на блоки*/

    int32_t  qs_itimelimit;  /* лимит для таймера на иноды */

    int32_t  qs_rtbtimelimit;/* лимит для таймера на

                                блоки RT */

    uint16_t qs_bwarnlimit;  /* лимит на количество предупреждений */

    uint16_t qs_iwarnlimit;  /* лимит на количество предупреждений */
};

```

Аргумент id игнорируется.

Q_XGETQSTATV

Возвращает доступную только в XFS информацию о квоте в структуре fs_quota_statv, на которую указывает addr. Данная версия операции учитывается версию структуры, а также соответствие расположения (все поля корректно выровнены) и заполнителей, чтобы не выполнять обработку совместимости; также она предоставляет возможность получения статистики оп файлу квот проекта. Структура fs_quota_statv определена следующим образом:

```
#define FS_QSTATV_VERSION1 1 /* fs_quota_statv.qs_version */
struct fs_qfilestatv {

    uint64_t qfs_ino;       /* номер иноды */

    uint64_t qfs_nblks;     /* количество BB блоков

                               размером 512 байт */

    uint32_t qfs_nextents;  /* количество экстентов */

    uint32_t qfs_pad;       /* заполнитель для 8-байтового выравнивания */
};
struct fs_quota_statv {

    int8_t   qs_version;    /* версия для изменений

                               в будущем */

    uint8_t  qs_pad1;       /* заполнитель для 16-битного выравнивания */

    uint16_t qs_flags;      /* флаги XFS_QUOTA_.* */

    uint32_t qs_incoredqs;  /* количество dquots incore */

    struct fs_qfilestatv qs_uquota;  /* информация

                                        о пользовательской квоте */

    struct fs_qfilestatv qs_gquota;  /* информация

                                        о групповой квоте */

    struct fs_qfilestatv qs_pquota;  /* информация

                                        о проектной квоте */

    int32_t  qs_btimelimit;   /* лимит по таймеру на блоки */

    int32_t  qs_itimelimit;   /* лимит по таймеру на иноды */

    int32_t  qs_rtbtimelimit; /* лимит по таймеру

                                 на блоки RT */

    uint16_t qs_bwarnlimit;   /* лимит на кол-во предупреждений */

    uint16_t qs_iwarnlimit;   /* лимит на кол-во предупреждений */

    uint64_t qs_pad2[8];      /* для использования в будущем */
};

```

Поле qs_version должно быть заполнено версией структуры, поддерживаемой вызываемым (пока поддерживается только FS_QSTAT_VERSION1). Ядро заполнит структуру согласно предоставленной версии. Аргумент id игнорируется.

Q_XQUOTARM

Освободить место на диске, занятое квотами. В аргументе addr должен быть указатель на значение unsigned int, содержащее флаги (те же, что и для поля d_flags структуры fs_disk_quota), которые показывают какие типы квот должны быть удалены (заметим, что тип квот, передаваемый в аргументе cmd, игнорируется, но должен быть корректным, чтобы пройти предварительные проверки обработчика системного вызова quotactl).

Квоты должны быть предварительно выключены. Аргумент id игнорируется.

Q_XQUOTASYNC (начиная с Linux 2.6.15; ничего не делает начиная с Linux 3.4)

Данная команда была эквивалентом Q_SYNC в XFS, но начиная с Linux 3.4 она ничего не делает, так как информации о квоте на диск теперь записывает sync(1) (вместе с записью других метаданных файловой системы). Аргументы special, id и addr игнорируются.

## ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении quotactl() возвращается 0; при ошибке возвращается -1, а в errno содержится код ошибки.

## ОШИБКИ
EACCES

Значение cmd равно Q_QUOTAON и файл квот, указанный в addr, существует, но не является обычным файлом или находится не в файловой системе, указанной в special.

EBUSY

Значение cmd равно Q_QUOTAON, но уже выполняется другой запуск Q_QUOTAON.

EFAULT

Неверное значение addr или special.

EINVAL

Неверное значение cmd или type.

EINVAL

Значение cmd равно Q_QUOTAON, но указанный файл квот повреждён.

ENOENT

Файл, указанный в special или addr, не существует.

ENOSYS

Ядро собрано с выключенным параметром CONFIG_QUOTA.

ENOTBLK

Значение special не указывает на блочное устройство.

EPERM

Вызывающий не имеет необходимых прав (CAP_SYS_ADMIN) для выполнения указанной операции.

ERANGE

Значение cmd равно Q_SETQUOTA, но заданный лимит вне диапазона допустимого форматом квот.

ESRCH

Не найдена дисковая квота для заданного пользователя. Квоты выключены в файловой системе.

ESRCH

Значение cmd равно Q_QUOTAON, но заданный формат квот не найден.

ESRCH

Значение cmd равно Q_GETNEXTQUOTA или Q_XGETNEXTQUOTA, но нет ID, который больше или равен id с активной квотой.

## ЗАМЕЧАНИЯ
Вместо <xfs/xqm.h> может быть использован <linux/dqblk_xfs.h>, но следует учесть, что есть несколько несоответствий названий:

- Флаги включения квот (формата XFS_QUOTA_[UGP]DQ_{ACCT,ENFD}) определены без начального «X» в виде FS_QUOTA_[UGP]DQ_{ACCT,ENFD}.

- Это же верно и для флагов типов квот XFS_{USER,GROUP,PROJ}_QUOTA, которые определены как FS_{USER,GROUP,PROJ}_QUOTA.

- В заголовочном файле dqblk_xfs.h определены свои константы XQM_USRQUOTA, XQM_GRPQUOTA и XQM_PRJQUOTA для доступных типов квот, но их значения совпадают с константами без префикса XQM_.

## СМОТРИТЕ ТАКЖЕ
quota(1), getrlimit(2), quotacheck(8), quotaon(8)