Perl для системного администрирования

Работа с дисковыми квотами


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

Существует другой, более активный подход: квоты на файловые системы. Квоты, или ограничения операционной системы, позволяют ограничить объем дискового пространства, отведенный определенному пользователю. Квоты существуют в Windows 2000 и во всех современных разновидностях Unix. В NT4 для этого необходимы продукты сторонних разработчиков, а в MacOS для пользователей существует понятие S.O.L. (Simply or Sore Out of Luck - просто не повезло).

И хотя это активный подход, поддерживать его гораздо сложнее, чем «чистящие» сценарии, поскольку он применяется ко всем файлам, а не только к лишним, например, к core-файлам. Большинство системных администраторов считают лучшей стратегией использовать комбинацию автоматических «чистящих» сценариев и дисковых квот. Первое помогает ограничить использование второго.

В этом разделе мы поговорим о работе с дисковыми квотами в Unix средствами Perl. Перед тем как перейти к конкретному разговору, нужно понять, как квоты устанавливаются и как их можно ввести вручную. Чтобы сделать возможным применение квот в файловой системе, системный администратор Unix обычно добавляет запись в таблицу смонтированных файловых систем (например файл /etc/fstab или /etc/ufstab) и перезагружает систему либо вручную вызывает команду, разрешающую использование квот (обычно quotaon).

Вот пример файла /etc/vfstab из Solaris:

{(device device mount FS fsck nou'it

#to mount to fsck point 'ype \.<Uijot op

Параметр rq в последнем столбце включает квоты для файловой системы. Хранятся они для каждого пользователя отдельно. Для просмотра информации о квотах пользователя на всех смонтированных файловых системах, на которых квоты применяются, надо вызвать команду quota:




$ quota - v sabrams чтобы получить данные, подобные этим:

Disk quotas for sabrams (u.d 670)

Files'/stem u:apc:

/hoTie/useri 228731 2bOOGO 253000 0 0 :}



В следующих нескольких примерах нас будут интересовать только первые три колонки этого вывода. Первое число - это объем занятого в настоящий момент дискового пространства пользователем sabrams на файловой системе, смонтированной как /home/users. Второе - это размер «мягкой квоты» пользователя. Мягкая квота - это объем дискового пространства, после превышения которого операционная система в течение некоторого времени выдает предупреждения, но не ограничивает выделение дискового пространства. Последнее число - это «жесткая квота», т. е. абсолютный верхний предел для объема пространства, занятого данным пользователем. Если программа попытается использовать еще некоторое дисковое пространство после превышения пользователем квоты, операционная система отвергнет этот запрос и вернет сообщение об ошибке, подобное disk quota excoedud.

При желании изменить размеры квот вручную необходимо использовать команду

edquota, которая загружает небольшой временный файл с информацией о текущих размерах квот в редактор, определяемый переменной окружения EDITOR командного интерпретатора. Вот пример такого файла с информацией об ограничениях для четырех файловых систем, на которых применяются квоты. Скорее всего, домашний каталог этого пользователя находится в каталоге /exprt/server2, т. к. только в этой файловой системе для него отведены квоты:

fs /exprt/serverl blocks (soft = 0, hard = 0) inodes (soft = 0, hard = 0)

fs /exprt/server2 blocks (soft = 250000, hard = 253000) inodes (soft = 0 hard = 0)

fs /exprt/serverS blocks (soft = 0. hard = 0) modes (soft = 0, hard = 0)

fs /exprt/server4 blocks (soft = 0, hard - 0) modes (soft = 0, hard = 0)

Использование edquota вручную может быть удобным способом редактирования ограничений одного пользователя, но это совершенно невозможно в случае десятков, сотен и тысяч учетных записей пользователей. Один из недостатков Unix - нехватка утилит командной строки для редактирования информации о квотах. В большинстве версий Unix есть функции библиотеки С для выполнения этой задачи, но нет утилит командной строки для написания сценариев. И, следуя девизу Perl, что «Существует более одного способа сделать это» («There's More Than One Way To Do It», TMTOWTDI, произносится как «тим-то-ади», «tim-toady»), мы рассмотрим два различных способа установки квот из Perl.





Редактирование квот при помощи edquota



Первый способ требует некоторой хитрости с нашей стороны. Совсем недавно мы упоминали процесс ручной установки дисковых квот: edquota вызывает редактор, в котором пользователь редактирует небольшой текстовый файл, после чего эти изменения используются для обновления информации о квотах. Не существует никаких указаний на то, какие данные необходимо вводить, чтобы внести изменения. В действительности, не существует даже ограничений на то, какой редактор будет применяться. Все, что нужно команде edquota, это программа, которую можно запустить и которая необходимым образом изменит маленький текстовый файл. Подойдет любой допустимый путь (заданный переменной окружения ;TI IOR) к такой программе. Почему бы не указать программе edquota сценарий на Perl? Давайте и рассмотрим такой сценарий в нашем следующем примере.

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

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

Второй копии программы необходимо сообщить, что именно она должна изменить по требованию исходной программы. Как она получает эту информацию из первой копии, вызвавшей edquota, менее очевидно, чем этого бы хотелось. В странице руководства по edquota сказано: «Вызывается редактор vi(l), если только в переменной EDITOR не указано иное». Идея передать аргументы командной строки через EDITOR или другую переменную окружения довольно опасна хотя бы потому, что мы не знаем, как на это отреагирует утилита edquota. Поэтому нам придется полагаться на один из методов межпроцессного взаимодействия, доступных в Perl. Например, два процесса могут:

  • Передавать друг другу временный файл


  • Создать именованный канал и общаться по нему


  • Передавать AppleEvents (в MacOS)




  • Использовать объект синхронизации (inutex) или оговоренные ключи реестра (в NT/2000)


  • Общаться через сокеты


  • Использовать разделяемую память


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

  • Направление соединения (одно- или двунаправленное?)


  • Частоту соединения (будет передано одно сообщение или несколько кусочков?)


  • Размер данных (будет это 10-мегабайтный файл или 20 символов?)


  • Формат данных (будет это двоичный файл или просто текст фиксированной ширины, разделенный определенным символом?)


  • Наконец, учитывайте то, насколько сложным вы хотите сделать свой сценарий.

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

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

    $edquota = "/usr/etc/edquota"; и путь к edquota

    Sautoedq = "/usr/adm/autoedquota"; ц полный путь к этому сценарию

    ft это первый или второй запуск?

    ft если присутствует более одного аргумента - это первый запуск

    if ($»ARGV > 0) {

    &ParseArgs;

    &CallEdquota; }

    и в противном случае это второй запуск, и мы должны выполнить редактирование



    else {

    &EdOuota(); }

    Рассмотрим код, вызываемый при первом запуске и используемый для анализа аргументов и вызова edquota через канал:

    sub ParseArgsf

    use Getopt: :Std;

    # для обработки параметров

    # Устанавливаем переменную $opt_u равной идентификатору и пользователя,

    $opt_f - равной имени файловой системы,

    $opt_s - в значение для мягкого ограничения и

    $opt_h -К в значение для жесткого ограничения getopt("u:f:s:h:");

    двоеточие говорит о том, что у этого

    # ключа есть аргумент die "ИСПОЛЬЗОВАНИЕ:

    $0 -u uid -f <fsystem> -s <softq> -h <nardq>\n"

    if (<$opt_u || !$opt_f || !$opt_s || !$opt_n); }

    sub CallEdquotaf

    $ENV{"EDITOR"} = Sautoedq;

    записываем в

    # переменную окружения EDITOR путь к нашему сценарию

    operKEPROCFSS. "|$edquota $opt_u") or die

    "Невозможно запустить edquota :$! \r,":

    посылаем измененные строки во вторую копию сценария

    print EPROCESS "$opt_f|$opt^s|$opt_.h\n";

    close(EPROCESS); }

    Вот вторая часть выполняемого действия:

    sub EdOuota {

    Stfile = $ARGV[0];

    получаем имя временного файла edquota

    open(TEMPFILE, Stfile) or die "Невозможно открыть временный файл

    # открываем файл-черновик, можно было бы и использовать и

    new_tmpfile() open(NEWTEMP, ">$tfile.$$") or die "

    Невозможно открыть временный файл-черновик Stfile.$$:$!\";

    # получаем строку ввода из первого вызова и отсекаем символ \

    chomp($change = <STDIN>);

    my($fs,$soft,Shard) = split(/\|/,$change);

    разбираем ответ

    считываем из временного файла строку. Если она содержит

    информацию о файловой системе, которую мы хотим

    изменить, изменяем эти значения. Записываем строку

    (вероятно, измененную) в черновик, while (<TEMPFILE>){

    перезаписываем временный файл измененным черновиком,

    так что изменения передаются edquota rename("Stfile.$$",Stfile)

    or die "Невозможно переименовать

    $tfile.$$ в $t*ile:$!\n":

    }

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





    Редактирование квот при помощи модуля Quota



    Когда-то очень давно предыдущий метод (или, если быть честным, предыдущий «хак») был единственным способом автоматизировать изменения квот, если, конечно, вас не радовала перспектива редактирования системных вызовов из библиотеки С, чтобы встроить их в интерпретатор Perl. Теперь, когда механизм расширений Perl существенно упростил встраивание библиотечных вызовов в Perl, создание модуля Quota для Perl стало только делом времени. Благодаря Тому Зорнеру (Tom Zoerner) и другим процесс установки квот средствами Perl теперь намного проще, если этот модуль поддерживает вашу версию Unix. Если нет, предыдущий метод все равно будет работать нормально.

    Вот небольшой пример, в котором принимаются те же аргументы, что и в предыдущем:

    use Getopt::Std;

    use Quota:; '

    getopt("u:f:s:h:");

    die "USAGE: $0 -u uid -f <filesystem> -s <softquota> -h <hard quota>\n"

    if (!$opt_u || !$opt_f || ISopt^s || !$opt_h);

    $dev = Quota::getcarg($opt_f) or die "Невозможно преобразовать путь

    $0ptc;f:$!\n";

    ($curblock,$soft,Shard,Sbtimeout,Scurinode,$isoft,Sihard,$itimeout)=

    Quota::query($dev,$uid) or die "Невозможно запросить квоту для

    $uid:$!\n";

    Quota::setqlim($dev,$opt_u,$opt_s,$opt_h,$isoft,$ihard,1) or

    die " Невозможно установить квоту:$!\n";

    После анализа аргументов остаются три простых шага: во-первых, мы используем Quota: :getcarg() для получения идентификатора устройства, который передается другим подпрограммам. Затем мы передаем этот идентификатор и идентификатор пользователя функции qjc-ta: :query(), чтобы получить текущие параметры квот. Нам нужны эти настройки, чтобы не нарушить ограничения, которые мы не будем изменять (например, число файлов). Наконец, мы устанавливаем квоту. Вот и все, всего лишь три строчки кода на Perl.

    Помните, что девиз Perl TMTOWTDI означает «существует более одного способа сделать это», но это вовсе не значит «несколько одинаково хороших способов».


    Содержание раздела