Чтение онлайн

ЖАНРЫ

UNIX: взаимодействие процессов

Стивенс Уильям Ричард

Шрифт:

Когда дейтаграмма UDP поступает в порт 32779, демон inetd обнаруживает готовность этой дейтаграммы к обработке и вызывает fork, а затем exec для запуска программы /usr/lib/netsvc/rstat/rpc.rstatd. Перед вызовами fork и exec концевая точка XTI будет скопирована в дескрипторы 0, 1 и 2, а все прочие дескрипторы inetd будут закрыты (рис. 12.7 [24]). Демон inetd также прекратит слушать эту конечную точку XTI, не реагируя на запросы пользователей до тех пор, пока сервер (дочерний процесс по отношению к inetd) не завершит работу. Это поведение определяется атрибутом wait.

Предположим, что эта программа была создана с помощью rpcgen. Тогда она сможет распознать конечную точку XTI, подключенную к стандартному

потоку ввода, и инициализировать ее как конечную точку сервера RPC. Это осуществляется вызовом функций RPC svc_tli_create и svc_reg, которые в данной книге не рассматриваются. Вторая функция (вопреки названию) не регистрирует сервер в программе отображения портов — это делается лишь однажды, при запуске сервера. Функция svc_run прочитает пришедшую дейтаграмму и вызовет соответствующую процедуру сервера для обработки запроса клиента.

В обычной ситуации серверы, запускаемые демоном inetd, обрабатывают один запрос клиента и завершают работу, после чего inetd переходит в режим ожидания следующего запроса. Для оптимизации работы системы серверы RPC, созданные rpcgen, ждут поступления нового запроса от клиента в течение некоторого времени (по умолчанию 2 минуты). В этом случае дейтаграмма обрабатывается уже запущенным сервером. Это исключает накладные расходы на вызов fork и exec при поступлении нескольких клиентских запросов подряд. По истечении периода ожидания сервер завершает работу, а демону inetd отсылается сигнал SIGCHLD, после чего он переходит в режим ожидания дейтаграмм по XTI.

16.4. Аутентификация

По умолчанию в запросе RPC не содержится информации о клиенте. Сервер отвечает на запрос клиента, не беспокоясь о том, что это за клиент. Это называется нулевой аутентификацией, или AUTH_NONE.

Следующий уровень проверки подлинности называется аутентификацией Unix, или AUTH_SYS. Клиент должен сообщить библиотеке RPC времени выполнения информацию о себе (имя узла, действующий идентификатор пользователя, действующий идентификатор группы, дополнительные идентификаторы группы) для включения в каждый запрос. Изменим программу из листинга 16.2 таким образом, чтобы она включала возможность осуществления аутентификации Unix. В листинге 16.7 приведен новый текст программы-клиента.

Листинг 16.7. Клиент, осуществляющий аутентификацию unix

//sunrpc/square4/client.с

1 #include "unpipc.h"

2 #include "square.h"

3 int

4 main(int argc, char **argv)

5 {

6 CLIENT *cl;

7 square_in in;

8 square_out out;

9 if (argc != 3)

10 err_quit("usage: client <hostname> <integer-value>");

11 cl = Clnt_create(argv[1], SQUARE_PROG, SQUARE_VERS, "tcp");

12 auth_destroy(cl->cl_auth);

13 cl->cl_auth = authsys_create_default;

14 in.arg1 = atol(argv[2]);

15 if (squareproc_2(&in, &out, cl) != RPC_SUCCESS)

16 err_quit("%s", clnt_sperror(cl, argv[1]));

17 printf("result: %ld\n", out.resl);

18 exit(0);

19 }

12-13 Эти строки были добавлены в данной версии программы. Сначала мы вызываем auth_destroy для удаления предыдущей аутентификационной

информации, связанной с данным дескриптором клиента (то есть дескриптор нулевой аутентификации, создаваемый по умолчанию). Затем вызов authsys_create_default создает соответствующую аутентификационную структуру Unix и мы сохраняем ее в поле cl_auth структуры CLIENT. Оставшаяся часть клиента не претерпела изменений по сравнению с листингом 16.5.

В листинге 16.8 приведен текст процедуры сервера, измененный по сравнению с листингом 16.6. Мы не приводим текст процедуры square_prog_2_freeresult, которая не меняется.

Листинг 16.8. Процедура сервера, запрашивающая аутентификацию Unix

//sunrpc/square4/server.c

1 #include "unpipc.h"

2 #include "square.h"

3 bool_t

4 squareproc_2_svc(square_in *inp, square_out *outp, struct svc_req *rqstp)

5 {

6 printf("thread %Id started, arg = %ld, auth = %d\n",

7 pr_thread_id(NULL), inp->arg1, rqstp->rq_cred.oa_flavor);

8 if (rqstp->rq_cred.oa_flavor == AUTH_SYS) {

9 struct authsys_parms *au;

10 au = (struct authsys_parms *)rqstp->rq_clntcred;

11 printf("AUTH_SYS: host %s, uid %ld, gid %ld\n",

12 au->aup_machname, (long) au->aup_uid, (long) au->aup_gid);

13 }

14 sleep(5);

15 outp->res1 = inp->arg1 * inp->arg1;

16 printf("thread %ld done\n", pr_thread_id(NULL));

17 return(TRUE);

18 }

6-8 Теперь мы используем указатель на структуру svc_req, которая всегда передается в качестве одного из аргументов процедуры сервера:

struct svc_req {

 u_long rq_prog; /* номер программы */

 u_long rq_vers; /* номер версии */

 u_long rq_proc; /* номер процедуры */

 struct opaque_auth rq_cred:/* данные о клиенте */

 caddr_t rq_clntcred; /* готовые данные (только для чтения) */

 SVCXPRT *rq_xprt; /* транспортный дескриптор */

};

struct opaque_auth {

 enum_t oa_flavor; /* flavor: константа AUTH_xxx */

 caddr_t oa_base; /* адрес дополнительной аутентификационной информации */

 u_int oa_length; /* не должно превосходить MAX_AUTH_BYTES */

};

Поле rq_cred содержит неформатированную информацию о клиенте, а его поле oa_flavor содержит целое число, определяющее тип аутентификации. Термин «неформатированная» означает, что библиотека не обработала информацию, на которую указывает oa_base. Но если тип идентификации относится к одному из поддерживаемых библиотекой, то в готовой информации о клиенте, на которую указывает rq_clntcred, содержится некоторая структура, соответствующая данному типу аутентификации. Программа выводит тип аутентификации и прове-9_12 ряет, соответствует ли он AUTH_SYS.

Поделиться с друзьями: