Rpcgen является средством создания Си-программы, реализующей протокол RPC. На вход компилятора поступает файл на языке RPCL (Язык удаленных процедурных вызовов). Как правило, компилятор из одного файла на входе (первый вариант) генерирует четыре выходных файла. Если входной файл имеет имя proto.x, генерируемые файлы именуются следующим образом: proto.h - файл заголовков, proto_xdr.c - процедуры XDR, proto_svc.c - разделы, имеющие отношение к серверу, а proto_clnt.c - к клиентам.
Все остальные варианты вызова компилятора связаны с созданием только одного выходного файла. К рассмотрению каждого из вариантов мы перейдем ниже.
Все выходные файлы перед интерпретацией rpcgen проходят обработку Си-препроцессором cpp(CP), поэтому директивы cpp во входном файле rpcgen допустимы. Каждому типу выходного файла соответствует специальный символ cpp: RPC_HDR, RPC_XDR, RPC_SVC и RPC_CLNT.
Rpcgen, кроме того, сам выполняет некоторую предварительную обработку входных данных. Любая строка, открывающаяся символом "%", передается сразу в выходной файл, без интерпретации.
Отдельные XDR-процедуры можно настраивать, не определяя их тип. При этом rpcgen будет предполагать существование процедуры с именем, состоящим из имени неопределенной структуры и приставки xdr_.
Настоятельно рекомендуем вам, прежде чем приступить к использованию утилиты rpcgen, прочитать главы, посвященные RPC и XDR, в "Руководстве программиста SCO NFS".
ОПЦИИ
-c
Компиляция процедур XDR.
-h
Компиляция определений данных на Си (файл заголовков).
-o вых_файл
Указание имени выходного файла. По умолчанию используется файл стандартного вывода (только для режимов -c, -h, -s).
-l
Получение разделов, имеющих отношение к клиенту.
-s транспорт
Получение разделов, имеющих отношение к серверу, с использованием указанного транспортного протокола. Поддерживаемые типы транспортного протокола: udp и tcp. При использовании нескольких протоколов опция вызывается несколько раз.
-m
Получение разделов, имеющих отношение к серверу, без создания процедуры main(). Используется в том случае, если пользователь создает свою собственную процедуру main().
ИСПОЛЬЗОВАНИЕ
Краткое описание синтаксиса RPCL
Элементарные типы данных [unsigned] char [unsigned] short [unsigned] int [unsigned] long unsigned float double void bool Если не считать типа bool (булевы данные), во всем остальном RPCL подобен Си.
В выходном файле заголовков rpcgen преобразует определения bool в определения int (точнее говоря, в определения bool_t, которые операцией #define описаны как int). Кроме того, тип void может появиться только в определениях union и program. Для типов с приставкой unsigned допустимо использование аббревиатуры uchar, ushort, uint и ulong.
ОПРЕДЕЛЕНИЯ
В RPCL разрешены только три типа определений:
простое_определение
имя_типа идентификатор_объекта
Пример:
long a;
определение_с_указателем
имя_типа *идентификатор_объекта
Пример:
char *b;
векторное_определение
имя_типа идентификатор_объекта[размер]
("размер" может быть целой или символьной константой) Пример:
opaque c[10];
По сравнению с Си, определения в языке RPCL имеют где сокращенную, где расширенную форму. Ограничения касаются невозможности описания многомерных массивов или вложенных указателей (хотя их можно определить с помощью оператора typedef). Два расширения:
Данные типа opaque описываются как вектор:
opaque идентификатор_объекта[размер]
В соответствии с протоколом будет создан объект указанного размера (в байтах). Обратите внимание на то, что размер в байтах не равен размеру в символах, поскольку символы в XDR размещаются в 32 битах каждый. Определения типа opaque компилируются в выходном файле заголовков в описания массивов символов.
Строки описываются особо, по типу векторного определения:
string идентификатор_объекта[максимальный_размер]
Если максимальный размер не указан, длина строки практически не ограничивается. Строковые определения преобразуются в:
char *идентификатор_объекта
Описания типов
Rpcgen используется для генерации процедуры XDR и/или файла заголовков, описывающего типы данных из входного файла. Для каждого описываемого zetype утилита rpcgen создает соответствующую процедуру XDR, именуемую xdr_zetype и обязательную для создания RPC-программ.
Первые три очень похожи на своих тезок в Си. Вместе с тем в Си отсутствует формальный механизм описания массивов переменной длины, а XDR-объединения совершенно не похожи на соответствующие объекты в Си.
Вложенность описаний типов в XDR не допускается. Например, следующая запись для rpcgen будет непонятна:
Оператор typedef в XDR выглядит следующим образом: typedef:
typedef имя_типа идентификатор_объекта ;
"Идентификатор объекта" является именем нового типа, в то время как "имя_типа" относится к исходному типу. Например:
typedef longa;
Синтаксис описания перечислимого типа:
перечислимый_тип-def: enum идентификатор_типа { список_типов }; список_типов: символьный_идентификатор [=присваивание] символьный_идентификатор [=присваивание], список_типов (в правой части оператора, после знака равенства может распо- лагаться целая или символьная константа)
Если явного присваивания нет, неявно будет присвоено значение предыдущего элемента перечисления, увеличенное на 1. Первый элемент по умолчанию имеет нулевое значение.
Обратите внимание на то, что имя объединяющей компоненты в выходной структуре совпадает с именем самого типа.
Описания программ:
программа-def: program идентификатор_программы { список_версий }=номер_программы; список_версий: версия версия список_версий версия: version идентификатор_версии { список_процедур }=номер_версии; список_процедур: описание_процедуры описание_процедуры список_процедур описание_процедуры: имя_типа идентификатор_процедуры(имя_типа)=номер_процедуры;
Описания программ не похожи ни на что из ранее виденного вами, поэтому нам ничего больше не остается, как прибегнуть к примеру. Предположим, что нам нужно создать механизм (серверный) получения или установки даты. Его описание может выглядеть следующим образом:
program DATE_PROG { version DATE_VERS { date DATE_GET(timezone) = 1; void DATE_SET(date) = 2; /* время по Гринвичу */ } = 1; } = 100;
В файле заголовков эта запись будет иметь следующий вид:
Если вы используете rpcgen для компиляции серверных процедур, вам необходимо ознакомиться с некоторыми важными моментами. Сервер взаимодействует с вашими локальными процедурами через Си-функцию, имя которой совпадает с именем в описании программы, но записывается строчными буквами и оканчивается номером версии.
Рассмотрим локальную процедуру реализации DATE_GET:
date * /* всегда возвращает указатель на результаты */ date_get_l(tz) timezone *tz; /* всегда получает указатель на аргументы */ { static date d; /* должна быть статической! */ /* * получение даты * и сохранение ее в d */ return(&d); }
Имя процедуры совпадает с именем, объявленным в #define, но записывается строчными буквами и оканчивается номером версии. XDR рекурсивно освобождает аргумент после получения результатов из локальной процедуры, поэтому всю необходимую вам информацию из аргумента следует скопировать между обращениями. При этом XDR не манипулирует вашими результатами. Вам следует позаботиться об их сохранении самим.
Вывод правил компиляции заголовков
Правила преобразования суффиксов в при компиляции процедур XDR и заголовков (по условию файлы протоколов RPCL имеют расширение .x):
В программных определениях могут встретиться совпадающие имена, при этом сфера действия имен четко не разграничивается. Избежать подобных ситуаций поможет использование уникальных идентификаторов для программ, версий, процедур и типов.
Вложенность также не поддерживается. Чтобы сымитировать эффект вложенности, объявление структур можно производить на верхнем уровне с тем, чтобы использовать их имена внутри других процедур.