Прежде чем изучать системные вызовы, полезно познакомиться с командой
strace
, которая отслеживает выполнение заданной программы, выводя список всех запрашиваемых системных вызовов и получаемых сигналов. Эта команда ставится в начале строки вызова программы, например: [26]
% strace hostname
В результате будет получено несколько экранов выходной информации. Каждая строка соответствует одному системному вызову. В строке указываются имя вызова, его аргументы (или их сокращенные обозначения, если аргументы слишком длинные) и возвращаемое значение.
По возможности команда
strace
старается отображать не числовые значения, а символические константы. Показываются также поля структур, переданных по указателю. Вызовы обычных функций не регистрируются.
26
Команда
hostname
, вызванная без флагов, отображает имя компьютера.
execve("/bin/hostname", ["hostname"], [/* 49 vars */]) = 0
Первый аргумент — это имя запускаемой программы. За ним идет список аргументов, состоящий из одного элемента. Дальше указан список переменных среды, который команда
strace
опустила для краткости.
Следующие примерно 30 строк отражают работу механизма загрузки стандартной библиотеки языка С из библиотечного файла. Ближе к концу наконец встречаются системные вызовы, связанные непосредственно с работой программы. Системный вызов
uname
запрашивает имя компьютера у ядра:
27
В Linux семейство функции
exec
реализовано на основе системного вызова
execve
.
uname({sys="Linux", node="myhostname", ...}) = 0
Заметьте, что команда strace показала метки полей структуры, в которой хранятся аргументы. Эта структура заполняется в системном вызове: Linux помещает в поле
sys
имя операционной системы, а в поле node — имя компьютера. Функция
uname
будет описана ниже, в разделе 8.15. "Функция uname".
Системный вызов
write
выводит полученные результаты на экран. Вспомните, что дескриптор 1 соответствует стандартному выходному потоку. Третий аргумент — это количество отображаемых символов. Функция возвращает число действительно записанных символов.
write(1, "myhostname\n", 11) = 11
Эта строка может отобразиться искаженной, поскольку вывод программы
hostname
смешивается с результатами работы команды
strace
. Если запускаемая программа создает слишком много выходных данных, лучше перенаправить вывод команды
strace
в файл с помощью опции
– о имя_файла
.
8.2. Функция access: проверка прав доступа к файлу
Функция
access
определяет, имеет ли вызывающий ее процесс право доступа к заданному файлу. Функция способна проверить любую комбинацию привилегий чтения, записи и выполнения, а также факт существования файла.
Функция
access
принимает два аргумента: путь к проверяемому файлу и битовое объединение флагов
R_OK
,
W_OK
и
X_OK
, соответствующих правам чтения, записи
и выполнения. При наличии у процесса всех необходимых привилегий функция возвращает 0. Если файл существует, а нужные привилегии на доступ к нему у процесса отсутствуют, возвращается -1 и в переменную errno записывается код ошибки
EACCES
(или
EROFS
, если проверяется право записи в файл, который расположен в файловой системе, смонтированной только для чтения).
Если второй аргумент равен
F_OK
, функция
access
проверяет лишь факт существования файла. В случае обнаружения файла возвращается 0, иначе — -1 (в переменную
errno
помещается также код ошибки
ENOENT
). Когда один из каталогов на пути к файлу недоступен, в переменную
errno
будет помещён код
EACCES
.
Программа, показанная в листинге 8.1, с помощью функции
access
проверяет существование файла и определяет, разрешен ли к нему доступ на чтение/запись. Имя файла задается в командной строке.
Листинг 8.1. (check-access.c) Проверка прав доступа к файлу
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
char* path = argv[1];
int rval;
/* Проверка существования файла. */
rval = access(path, F_OK);
if (rval == 0)
printf("%s exists\n", path);
else {
if (errno == ENOENT)
printf("%s does not exist\n", path);
else if (errno == EACCES)
printf("%s is not accessible\n", path);
return 0;
}
/* Проверка права доступа. */
rval = access(path, R_OK);
if (rval == 0)
printf("%s is readable\n", path);
else
printf("%s is not readable (access denied)\n", path);
/* проверка права записи. */
rval = access(path, W_OK);
if (rval == 0)
printf("%s is writable\n", path);
else if (errno == EACCES)
printf("%s is not writable (access denied)\n", path);
else if (errno == EROFS)
printf("%s is not writable (read-only filesystem)\n",
path);
return 0;
}
Вот как, к примеру, проверить права доступа к файлу
README
, расположенному на компакт-диске:
% ./check-access /mnt/cdrom/README
/mnt/cdrom/README exists
/mnt/cdrom/README is readable
/mnt/cdrom/README is not writable (read-only filesystem)