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

ЖАНРЫ

Искусство программирования для Unix
Шрифт:

7.2.2.5. Контрпример: почему программа fetchmail не выполнена в виде конвейера

В понятиях Unix fetchmail является неудобной большой программой, изобилующей различными параметрами. Рассматривая способ транспортировки почты, можно предположить, что данную программу можно было бы разложить на компоненты конвейера. Предположим, что она разбита на ряд программ: несколько программ доставки для получения почты с РОРЗ- и IMAP-узлов, и локальный SMTP-инжектор. Конвейер мог бы передавать почтовый формат Unix. Существующую сложную конфигурацию fetchmail

можно было бы заменить сценарием оболочки, содержащим строки команд. В такой конвейер можно также добавить фильтры для блокировки спама.

#!/bin/sh

imap jrandom®imap.ccil.org | spamblocker | smtp jrandom imap jrandom@imap.netaxs.com | smtp jrandom # pop ed@pop.terns.com | smtp j random

Такая конструкция была бы весьма изящной и соответствовала бы духу Unix, но не смогла бы работать. Причина рассматривалась выше — конвейеры являются однонаправленными.

Одной из функций программы доставки (imap или pop) было бы принятие решения о том, отправлять ли запрос на удаление каждого принимаемого сообщения. В существующей организации fetchmail отправка такого запроса POP- или IMAP-серверу может быть задержана до тех пор, пока программа не получит подтверждения о том, что локальный SMTP-слушатель взял на себя ответственность за данное сообщение. Версия программы, организованная в виде конвейера из небольших компонентов, потеряла бы данное свойство.

Рассмотрим, например, последствия аварийного завершения smip-инжектора вследствие того, что SMTP-получатель сообщил о переполнении диска. Если программа доставки уже удалила почту, сообщения будут утеряны. Это означает, что программа доставки не может удалять почту до тех пор, пока не получит соответствующее уведомление от smtp-инжектора. Причем с данной проблемой связан ряд вопросов. Каким образом программы обменивались бы данными? Какое в точности сообщение было бы возвращено инжектором? Общая сложность такой системы и ее подверженность неочевидным ошибкам были бы выше, чем сложность монолитной программы.

Конвейеры являются превосходными инструментами, но они не универсальны.

7.2.3. Упаковщики

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

Как и в случае вызова с созданием подоболочки, связанного протокола не существует, поскольку программы не обмениваются данными во время выполнения вызываемой программы. Однако обычно упаковщик существует для указания аргументов, изменяющих поведение этой программы.

7.3.2.1. Учебный пример: сценарии резервного копирования

Специализированные упаковщики представляют собой классический пример использования Unix shell и других языков сценариев. Одним из распространенных и характерных видов специализированных упаковщиков является сценарий резервного копирования. Он может быть однострочным, таким же простым, как приведенный ниже.

tar -czvf /dev/stO

Приведенная выше команда является упаковщиком для утилиты архивирования tar(1), который предоставляет один фиксированный аргумент (накопитель на магнитных лентах /dev/stO) и передает tar

все остальные аргументы, указанные пользователем ("$@")50.

7.2.4. Оболочки безопасности и цепи Бернштайна

Один из распространенных способов использования сценариев упаковщиков — это создание оболочек безопасности (security wrappers). Сценарий безопасности может вызывать программу-диспетчер (gatekeeper) для проверки мандата (credential), а затем в зависимости от значения, возвращенного программой-диспетчером, запустить другую программу.

Образование цепей Бернштайна (Bernstein chaining) представляет собой специализированную методику применения оболочек безопасности, впервые разработанную Даниелем Бернштайном (Daniel J. Bernstein), который задействовал ее в многих своих пакетах. (Подобная модель наблюдается в таких командах, как nohup(1) иsu(1), но условность выполнения отсутствует.) Концептуально цепи Бернштайна подобны конвейерам, но, в отличие от последних, в цепях каждый успешный этап заменяет предыдущий, а не выполняется одновременно с ним.

Обычным применением данной методики является заключение привилегированных приложений в некоторой программе-диспетчере, которая затем может передать параметры менее привилегированной программе. В данной методике несколько программ объединяются с помощью вызовов exec или, возможно, комбинации вызовов fork и exec. Все программы указываются в одной командной строке. Каждая программа осуществляет некоторую функцию и (в случае успешного завершения) запускает ехес(2) для остальной части командной строки.

Пакет Бернштайна rblsmtpd является принципиальным примером. Он служит для поиска узла в антиспамной DNS-зоне системы предотвращения некорректного использования почты (Mail Abuse Prevention System). Свои функции данный пакет выполняет путем отправки DNS-запроса на IP-адрес, переданный ему как значение переменной среды TCPREMOTEIP. Если запрос оказался успешным, то rblsmtpd запускает собственный SMTP-сервер, который отклоняет почту. В противном случае предполагается, что оставшиеся аргументы командной строки вводят в действие агент доставки почты, который поддерживает протокол SMTP, и передаются для запуска ехес(2).

Еще один пример можно встретить в пакете qma.il Бернштайна. Пакет содержит программу, которая называется condredirect. Первым параметром является адрес электронной почты, остальные параметры — программа-диспетчер и аргументы, condredirect разветвляется и запускает программу-диспетчер со своими аргументами. В случае успешного завершения программой-диспетчером своей работы, condredirect перенаправляет почтовое сообщение, ожидающее в stdin, на указанный адрес электронной почты. В этом случае, в противоположность rblsmtpd, решение принимается дочерним процессом. Этот случай несколько больше походит на классический вызов с созданием подоболочки.

Более сложным примером является РОРЗ-сервер qmail. Он состоит из трех программ: qmail-popup, checkpassword и qmail-pop3d. Программа Checkpassword взята из отдельного пакета, остроумно названного checkpassword, и, естественно, предназначена для проверки паролей. В протоколе РОРЗ предусмотрен этап аутентификации и этап работы с почтовым ящиком. Перейдя к этапу работы с почтовым ящиком, пользователь не имеет возможности вернуться к этапу аутентификации. Это идеальное применение для цепей Бернштайна.

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