| Содержание | Предисловие | Введение | Ссылки
| Глава 1 | Глава 2 | Глава 3 | Глава 4 | Глава 5 | Глава 6 | Глава 7 | Глава 8 | Глава 9 | Глава 10
| Глава 11 | Глава 12 | Глава 13 | Глава 14 | Глава 15 | Глава 16 | Глава 17 | Глава 18 | Глава 19
| Приложение А | Приложение Б | Приложение В | Приложение Г |

Глава 16

    В этой главе:


Доступ к системным базам данных

Получение информации о паролях и группах

Информация о вашем пользовательском имени и идентификаторе, кото-рая имеется в системе UNIX, практически открыта. По сути дела, любая программа, которая не сочтет за труд заглянуть в файл /etc/passwd, поможет вам увидеть почти все, кроме незашифрованного пароля. Зтот файл имеет особый формат, определяемый в passwd(5), и выглядит приблизительно так:

name:pa3swd:uid:gid:gcos:dir:shell

Поля определены следующим образом:

name

Регистрационное имя пользователя

passwd

Зашифрованими пароль или что-нибудь простое, если используется теневой файл паролей

uid

Идентификатор пользователя (для пользователя root ≈ 0, для обычных пользователей ненулевое число)

gid

Регистрационная группа по умолчанию (группа 0 может быть привиле-гированной, но не обязательно)

gcos

Как правило, содержит полное имя пользователя, за которым через запятую следует и другая информация

dir

Начальний каталог (каталог, в который вы переходите, когда да╨те команду cd без аргументов, и в котором хранится большинство ваших файлов, имена которых начинаются с точки)

shell

Ваш регистрационный shell, как правило, /bin/sh или /Ып/csh (а, может быть, даже /usr/bin/perl, если вы большой оригинал)

Типичные злементы файла паролей выглядят так:

fred:*:123:15:Fred Flintstone,,,:/home/fred:/bin/csh barney:*:125:15:Barney Rubble,,,:/home/barney:/bin/csh

Сейчас в Perl достаточно инструментов для того, чтобы можно было легко выполнить разбор такой строки (например, с помощью функции split), не прибегая к специальным программам. Тем не менее в библиотеке UNIX все же ╨сть набор специальных программ: getpwent(3), getpwuid(3), gelpwnam(3) и т.д. Зти программы доступны в Perl под теми же именами, с похожими аргументами и возвращаемыми значеннями.

Например, программа getpwnam в Perl становится функцией getpwnam. Ее единственный аргумент пользовательское имя (например, fred или barney), а возвращаемое значение строка файла /etc/passwd, преобра-зованная в массив со следующими значеннями:

($name, $passwd, $uid, $gid, $quota, $cominent, $gcos, $dir, $shell)

Обратите внимание: здесь несколько больше значений, чем в файле паролей. Обычно в UNIX-системах, по крайней мере в тех, которые мы видели, поле $quota всегда пусто, а поля $comment и $gcos часто оба содержат персональную информацию о пользователе (поле GCOS). Так, для старины Фреда мы получаем

("fred", "*", 123, 15, "", "Fred Flintstone,,,", "Fred Flintstone,,,", "/home/gred"," /bin/csh")

посредством любого из следующих вызовов:

getpwuid(123) getpwnam("fred")

Отметим, что в качестве аргумента функция getpwuid принимает иден-тификатор пользователя, a getpwnam ≈ регистрационное имя.

При вьгзове в скалярном контексте функции getpwnam и getpwuid таюке имеют возвращаемое значение данные, которые вы запросили с их помощью. Например:

$idnum = getpwuid("daemon");

$login ╟= getpwnam (25);

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

($fred home) = (getpwnam ("fred"))[7]; # начальний каталог Фреда

Как просмотреть весь файл паролей? Для зтого можно бьгло бн поступить, к примеру, так:

for($id = 0; $id <= 10_000; $id++) f @stuff = getpwuid $id;

} ### не рекомендуется!

Зто, однако, неверный путь. Наличие нескольких способов само по себе еще не означает, что все они в равной степени зффективны.

Функции getpwuid и getpwnam можно считать функциями произволь-ного доступа; они извлекают конкретний злемент по его ключу, позтому для начала у вас должен быть ключ. Другой метод доступа к файлу паролейпоследовательный, т.е. поочередное получение его записей.

Программами последовательного доступа к файлу паролей являются функции setpwent, getpwent и endpwent. В совокупности зти три функции выполняют последовательный проход по всем записям файла паролей. Функция setpwent инициализирует просмотр. После инициализации каж-дый вызов getpwent возвращает следующую запись файла паролей. Если данных для обработки больше нет, getpwent возвращает пустой список. Наконец, вызов endpwent освобождает ресурсы, используемне программой просмотра; зто делается автоматически и при выходе из программы.

Приведенное описание может сказаться не совсем понятным без приме-ра, позтому дадим его:

setpwent (); # инициализировать просмотр while (@list " getpwent ()) ( # выбрать следующий злемент

($login,$home) = @list[0,7]; # получить регистрационное имя # и начальний каталог

print "Home directory for $login is $home\n"; # сообщить зто } endpwent(); # все сделано

Зта программа сообщает имена начальних каталогов всех пользователей, перечисленные в файле паролей. А если вы хотите расставить начальные каталоги в алфавитном порядке? В предыдущей главе мы изучили функцию sort, давайте воспользуемся ею:

setpwentf); # инициализировать просмотр while (@list = getpwentO) ( # вибрать следующий злемент

($login,$home) = @list[0,7]; # долучить регистрационное имя # и начальний каталог

$home($login} = $home; # сохранить их )

endpwent(); # все сделано Skeys = sort ( $home($a( cmp $home($b) } keys %home;

foreach $login (Skeys) ( # пройти по рассортированньпл именам

print "home of $login is $home($login)\n";

}

Зтот несколько более длинный фрагмент иллюстрирует важную особен-ность последовательного просмотра файла паролей: вы можете сохранять соответствующие фрагменты данных в структурах данных, выбираемых по сво╨му усмотрению. Первая часть примера зто код просмотра всего файла паролей с созданием хеша, в котором ключ регистрационное имя, а значение начальний каталог, соответствующий зтому регистрационному имени. Строка sort получает ключи хеша и сортирует их в соответствии со строковим значением. Завершающий цикл зто проход по рассортирован-ным ключам и поочередный вывод всех значений.

В общем случае для просмотра небольшого количества значений реко-мендуется использовать программы произвольного доступа (getpwuid и getpwnam). Если значений много или необходим просмотр всех значений, проще выполнить проход с последовательним доступом (с помощью функ-ций setpwent, getpwent и endpwent) и помостить конкретные значення, которые вы будете искать, в хеш*.

Доступ к файлу /etc/group осуществляется аналогичным образом. После-довательный доступ обеспечивается вызовами функций setgrent, getgrent и endgrent. Вызов getgrent возвращает значення в следующем формате:

<$name, $passwd, $gid, $members)

Зти четыре значення примерно соответствуют четырем полям файла /etc/group, позтому за подробной информацией обращайтесь к описанням, приведенным на man-страницах, относящихся к формату зтого файла. Соответствующие функций произвольного доступа ≈ getgrgid (по иден-тификатору группы) и getgrnam (по имени группы).

* Если у вас узел с большой NIS-картой, то по соображениям производительности такой способ предобработки файла паролей лучше не использовать.

Упаковка и распаковка двоичных данных

Данные о паролях и группах удобно использовать в текстовом виде. Информацию в других системных базах данных более естественно представлять иначе. Например, IP-адрес интерфейса обрабатывается внутренними механизмами как четырехбайтовое число. Хотя его часто представляют в текстовом виде (как четыре небольших целых числа, разделенных точками), такое преобразование пустая трата времени, если зти данные в промежутке между преобразованиями не выводятся на зкран пользователя.

По зтой причино написанные на Perl сетевые программы, ожидающие или возвращающие IP-адрес, используют четырехбайтовую строку, одному символу которой соответстввует один байт в памяти. Хотя конструирование и интерпретация такой байтовой строки довольно простая задача, решае-мая с помощью функций chr и ord (здесь не представленных), в Perl используется более зффективное решение, которое в равной степени при-менимо и к более сложньш структурам.

Функция pack по принципу работы немного похожа на функцию sprintf. Она получает строку, задающую формат, и список значений и упаковывает значення в одну строку. Однако в pack строка, задающая формат, предназначена для создания двоичной структури данных. Напри-мер, вот как можно взять четыре небольших целых числа и упаковать их в виде последовательности байтов без знака в строке:

5buf = packf'CCCC", 140, 186, 65, 25);

Здесь строка формата pack ≈ четыре буквы С. Каждая С соответствует отдельному значенню, взятому из приведенного следом списка (подобно тому, что делает спецификация % в функций sprintf). Формат С (согласно man-страницам Perl, краткому справочнику, книге Programming Perl, HTML-файлам и даже видеоролику Perl: The Motion Picture) обозначает один байт, вычисляемый из символьного значення без знака (короткого целого). Отрока-результат в переменной $buf представляет собой четырехсимвольную строку, в которой каждый символ задан одним байтом. Зти байты имеют значення 140, 186, 65 и 25 соответственно.

Аналогичным образом формат 1 генерирует длинное значение со знаком. На многих машинах зто четырехбайтовое число, хотя зтот формат зависит от конкретной машины. На четырехбайтовой "длинной" машине оператор

$buf = packC'l",0х41424344) ;

генерирует четырехсимвольную строку, состоящую из символов abcd или dcba -- в зависимости от того, какой порядок хранения байтов используется на данной машине: "младший в младшем" или "старший в младшем" (либо что-то совершенно иное, если зта машина "не говорит" на ASCII). Зто обЁясняется тем, что мы упаковываем одно значение в четыре символа (для представлення длинного целого отводится четыре байта), а зто одно значение как раз состоит из байтов, представляющих коды ASCII первых четырех букв алфавита. Аналогичньш образом,

$buf = pack("ll", 0х41424344, 0х45464748);

создает восьмибайтовую строку, состоящую из букв abcdefgh или dcbahgfe, опять-таки в зависимости от того, какой порядок хранения байтов используется в данной машине "младший в младшем" или "старший в младшем".

Полный перечень различных форматов, используемых для упаковки, приведен в справочной документации (perlfunc(l) или Programming Perl). Мы приведем некоторые из них как примеры, но все, конечно, давать не будем.

Допустим, вам дали восьмибайтовую строку abcdefgh и сказали, что она является представлением хранящихся в памяти (один символ один байт) двух длинных (четырехбайтовых) значений со знаком. Как ее интерпретиро-вать? Нужно воспользоваться функцией, обратной функции pack,≈ функ-цией unpack. Она берет строку управления форматом (как правило, иден-тичную той, которую вы указывали в функции pack) и строку данных и возвращает список значений, которые хранятся в соответствующих ячейках памяти. Давайте, например, распакуем такую строку:

($vall,$val2) = unpack("ll","ABCDEFGH");

Зто даст нам в переменной $vall нечто вроде 0х41424344, а может быть, и 0х44434241 (в зависимости от порядка хранения байтов). По сути дела, по возвращаемым значенням мы можем определить, на какой машине работаем с порядком "младший в младшем" или "старший в младшем".

Пробельные символы в строке, задающей формат, игнорируются и ис-пользуются лишь для удобочитаемости. Число в зтой строке, как правило, задает повторение предндущей спецификации соответствующее количество раз. Например, сссс можно записать как С4 или С2С2, смысл от зтого не изменится. (Однако в некоторых спецификациях число, указанное после символа, задающего формат, является частью спецификации, позтому их подобным образом записывать нельзя.)

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

$bu£ ■ pack("C*", 140, 186, 65, 25);

Здесь указанные четыре значення полностью обрабатываются одной спецификацией формата. Если бы вам требовались два коротких целых и "максимально возможное количество символов без знака", то можно было бы написать примерно так:

$buf = pack("s2 C*", 3141, 5926, 5, 3, 5, 8, 9, 7, 9, 3, 2);

Здесь мы получаем первые два значення как короткие (й генерируем, вероятно, четыре или восемь символов), а остальные девять как символы без знака (й генерируем, почти наверняка, девять символов).

Функция unpack co звездочкой в качестве спецификации может форми-ровать список злементов, длина которых заранее не определена. Например, при распаковке с использованием формата с* создается один злемент списка (число) для каждого символа строки. Так, оператор

@values = unpack("С*", "hello, world!\n");

позволяет сформировать список из 14 злементов, по одному для каждого символа строки.

Получение информации о сети

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

Один из параметров, который вам приходится часто определять,≈ зто IP-адрес, соответствующий сетевому имени (или наоборот). В С вы преоб-разуете сетевое имя в сетевой адрес с помощью программы gethostbyname(3). Затем, используя полученный адрес, вн устанавливаете связь между своей программой и другой программой, которая работает где-то в другом месте.

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

($name, $aliases, $addrtype, $length, @addrs) = gethostbyname($name);

# основная форма функций gethostbyname

Параметр зтой функций имя хоста, например, slate.bedrock.com, а возвращаемое значение список из четырех и более параметров (в зави-симости от того, сколько адресов связано с данным именем). Если имя хоста недействительно, функция возвращает пустой список.

Когда gethostbyname вызывается в скалярном контексте, возвращается только первый адрес.

Если gethostbyname завершается успешно, то переменной $name в качестве значення присваивается каноническое имя, которое, если входное имя псевдоним, отличается от входного имени. Значение переменной $aliases ≈ зто список разделенных пробелами имен, под которыми данный хост известен в сети. Переменная $addrtype содержит кодовое обозначение формата представлення адреса. Для имени slate. bedrock. corn мы можем предположить, что зто значение указывает на IP-адрес, обычно представляемый как четыре числа из диапазона от 1 до 256, разделенных точками. Переменная $length содержит количество адресов. Зто лишняя информация, так как в любом случае можно посмотреть на размер массива @addrs.

Наиболее полезная часть возвращаемого значення массив @addrs. Каждый злемент данного массива зто отдельный IP-адрес, представлен-ный во внутреннем формате и обрабатываемый в Perl как четырехсимвольная строка*. Зта четырехсимвольная строка представлена в формо, понятной для других сетевых Perl-функций. Однако предположим, что нам требуется вывести результат в виде, удобном для пользователя. В данном случае нам нужно с помощью функции unpack и еще нескольких операций преобра-зовать возвращаемое значение в удобочитаемый формат. Вот код, который обеспечивает вывод одного из ╡Р-адресов хоста slate.bedrock.com:

($addr) = (gethostbyname("slate.bedrock.com"))[4];

print "Slate's address is ",

join(".",unpack ("C4", $addr)), "\n";

Функция unpack получает четырехбайтовую строку и возвращает четыре числа. Оказывается, они стоят именно в том порядке, который нужен функции join для того, чтобы она вставила между каждой парой чисел точку и представила таким образом все зто в удобочитаемой форме. Информация о простих программах-клиентах приведена в приложении В.

Упражнение

Ответ см. в приложении А.

1. Напишите программу, которая создает таблицу соответствия идентифи-каторов пользователей и реальних имен из записей файла паролей, а затем с помощью зтой таблицы выводит список реальних имен, принад-лежащих каждой группе, упомянутой в файле групп. (Включает ли ваш список тех пользователей, у которых в записи файла паролей стоит группа по умолчанию, но в записи файла групп явного упоминания зтой группы нет? Если не включает, как зто сделать?)




|     Назад     |     Вперед     |


| Содержание | Предисловие | Введение | Ссылки
| Глава 1 | Глава 2 | Глава 3 | Глава 4 | Глава 5 | Глава 6 | Глава 7 | Глава 8 | Глава 9 | Глава 10
| Глава 11 | Глава 12 | Глава 13 | Глава 14 | Глава 15 | Глава 16 | Глава 17 | Глава 18 | Глава 19
| Приложение А | Приложение Б | Приложение В | Приложение Г |