Консольную команду делают встроенной по одной из двух причин: повышенная эффективность или доступ к ресурсам оболочки. Некоторые часто используемые команды, такие как pwd, echo и test, достаточно простые для того, чтобы их имело смысл реализовывать как встроенные. Другие команды встраиваются в оболочку для того, чтобы иметь возможность изменять ее внутреннюю информацию, устанавливать атрибуты ее процесса или как-то влиять на ее работу. Например, команда cd должна изменять текущий каталог самой командной оболочки, поэтому ее нельзя выполнять в рамках отдельного процесса. Среди прочих команд, встроенных для того, чтобы пользоваться внутренними ресурсами оболочки, можно выделить exec, exit, read, set, source, ulimit, umask, wait и команды управления заданиями оболочки — jobs, fg и bg. Полный перечень встроенных команд, поддерживаемых вашей командной оболочкой, ищите на соответствующей справочной странице.

Флаг закрытия при выполнении (close-on-exit) FD_CLOEXEC

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

• Дескриптор файла может быть открыт библиотечной функцией. Эта функция не может принудить главную программу к закрытию дескриптора до выполнения вызова exec() (следует взять за правило, что библиотечные функции всегда должны устанавливать флаг FD_CLOEXEC для любых открываемых ими файлов, используя описанный ниже метод).

• Если вызов exec() по какой-то причине завершается неудачно, может иметь смысл оставить файловые дескрипторы открытыми. Если они уже закрылись, их восстановление со ссылками на те же файлы может оказаться затруднительным или вообще невозможным.

В связи с этим ядро предоставляет для каждого файлового дескриптора флаг FD_CLOEXEC. Если он установлен, дескриптор автоматически закрывается после успешного выполнения exec(), но остается открытым, если вызов завершается неудачей. Доступ к данному флагу можно получить с помощью системного вызова fcntl() (см. раздел 5.2). fcntl() поддерживает операцию F_GETFD, которая извлекает копию флагов файлового дескриптора:

int flags;

flags = fcntl(fd, F_GETFD);

if (flags == –1)

errExit("fcntl");

После извлечения этих флагов мы можем их обновить с помощью второго вызова fcntl() с аргументом F_SETFD, предварительно изменив бит FD_CLOEXEC:

flags |= FD_CLOEXEC;

if (fcntl(fd, F_SETFD, flags) == –1)

errExit("fcntl");

FD_CLOEXEC на самом деле является единственным битом, который используется в флагах файлового дескриптора. Он соответствует значению 1. В старых программах иногда можно встретить установку флага FD_CLOEXEC с помощью кода fcntl(fd, F_SETFD, 1), который полагается на тот факт, что при этом не будут затронуты никакие другие биты. Теоретически это может быть не так (в будущем какая-нибудь UNIX-система может обзавестись дополнительными битами в списке флагов), поэтому вам следует использовать методику, приведенную в основном тексте.

Многие реализации UNIX, включая Linux, позволяют изменять флаг FD_CLOEXEC с помощью нестандартных вызовов ioctl(). Установить флаг FD_CLOEXEC для дескриптора fd можно посредством кода ioctl(fd, FIOCLEX); для его сброса достаточно вызвать ioctl(fd, FIONCLEX).

Когда для создания дубликата файлового дескриптора используются вызовы dup(), dup2() или fcntl(), в полученной копии флаг FD_CLOEXEC всегда сбрасывается (это поведение сложилось исторически и закреплено в SUSv3).

Изменение флага FD_CLOEXEC продемонстрировано в листинге 27.6. В зависимости от наличия аргумента командной строки (любого набора символов) данная программа сначала устанавливает флаг FD_CLOEXEC для стандартного вывода, а затем выполняет команду ls. Вот что мы увидим после ее запуска:

$ ./closeonexec Выполняем ls, не закрывая стандартный вывод

— rwxr-xr-x 1 mtk users 28098 Jun 15 13:59 closeonexec

$ ./closeonexec n Устанавливаем для стандартного вывода флаг FD_CLOEXEC

ls: write error: Bad file descriptor

Мы можем видеть, что при втором запуске команда ls обнаруживает факт закрытия стандартного вывода и возвращает сообщение в стандартный вывод ошибок.

Листинг 27.6. Установка флага FD_CLOEXEC для файлового дескриптора

procexec/closeonexec.c

#include

#include "tlpi_hdr.h"

int

main(int argc, char *argv[])

{

int flags;

if (argc > 1) {

flags = fcntl(STDOUT_FILENO, F_GETFD); /* Извлекаем флаги */

if (flags == –1)

errExit("fcntl — F_GETFD");

Перейти на страницу:

Похожие книги