Книга: Сценарии командной оболочки. Linux, OS X и Unix. 2-е издание
Назад: Глава 4. Тонкая настройка Unix
Дальше: Глава 6. Системное администрирование: обслуживание системы

Глава 5. Системное администрирование: управление пользователями

Никакая сложная операционная система, будь то Windows, OS X или Unix, не может функционировать бесконечно долго без вмешательства человека. Если вы работаете в многопользовательской системе Linux, значит, кто-то выполняет задачи системного администрирования. Вы можете игнорировать пресловутого «человека за ширмой», управляющего всем и вся, или сами быть великим и могучим волшебником из страны Оз — тем, кто двигает рычаги и нажимает кнопки, чтобы обеспечить нормальную работу системы. Если вы единственный пользователь системы, вам придется регулярно решать задачи системного администрирования самостоятельно.

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

Что интересно, многие сценарии, предназначенные для системного администрирования, включают не более 20–30 строк. С помощью команд Linux можно выявить, какие команды являются сценариями, а добавив конвейер — узнать, сколько строк содержит каждый из них. Ниже перечисляется 15 самых коротких сценариев в /usr/bin/:

$ file /usr/bin/* | grep "shell script" | cut -d: -f1 | xargs wc -l \

| sort -n | head -15

     3 zcmp

     3 zegrep

     3 zfgrep

     4 mkfontdir

     5 pydoc

     7 sgmlwhich

     8 batch

     8 ps2pdf12

     8 ps2pdf13

     8 ps2pdf14

     8 timed-read

     9 timed-run

    10 c89

    10 c99

    10 neqn

Ни один из 15 самых коротких сценариев в каталоге /usr/bin/ не содержит больше 10 строк. И десятистрочный сценарий форматирования формул neqn наглядно демонстрирует, как короткий сценарий командной оболочки может упрощать жизнь пользователям:

#!/bin/bash

# Присутствие этого сценария не должно расцениваться как наличие поддержки

#   GNU eqn и groff -Tascii|-Tlatin1|-Tutf8|-Tcp1047.

: ${GROFF_BIN_PATH=/usr/bin}

PATH=$GROFF_BIN_PATH:$PATH

export PATH

exec eqn -Tascii ${1+"[email protected]"}

# eof

Сценарии, которые будут представлены в этой главе, такие же короткие и полезные, как neqn, и помогают решить множество административных задач, включая резервное копирование системы, добавление и удаление учетных записей и пользовательских данных, управление учетными записями. Также вы получите простой и удобный интерфейс к команде date, изменяющий текущие дату и время, и инструмент для проверки файлов crontab.

№ 35. Анализ использования дискового пространства

Даже с появлением очень емких жестких дисков и постоянным уменьшением цен на них системным администраторам постоянно приходится следить за использованием дискового пространства, чтобы общедоступные диски не переполнились.

Наиболее типичным приемом мониторинга является исследование каталога /usr или /home с использованием команды du, чтобы определить объем всех подкаталогов, с последующим выводом списка 5 или 10 пользователей, занявших больше всего дискового пространства. Однако этот подход не позволяет контролировать потребление дискового пространства в других местах на жестких дисках. Если у отдельных пользователей есть дополнительное архивное пространство на втором диске или у вас завелись хитрецы, которые хранят огромные видеофайлы в каталоге с именем, начинающимся с точки и находящемся в каталоге /tmp или в неиспользуемом каталоге в области ftp, такие факты расходования дискового пространства не будут обнаружены. Кроме того, если домашние каталоги пользователей разбросаны по нескольким дискам, поиск каждого каталога /home может оказаться неоптимальным.

Лучшее решение — получить имена всех учетных записей непосредственно из файла /etc/passwd и затем отыскать в файловой системе все файлы, принадлежащие каждой учетной записи, как показано в листинге 5.1.

Код

Листинг 5.1. Сценарий fquota

#!/bin/bash

# fquota -- инструмент анализа расходования дискового пространства для Unix;

#   предполагается, что все учетные записи рядовых пользователей

#   имеют числовые идентификаторы UID >= 100

MAXDISKUSAGE=20000 # В мегабайтах

for name in $(cut -d: -f1,3 /etc/passwd | awk -F: '$2 > 99 {print $1}')

do

  /bin/echo -n "User $name exceeds disk quota. Disk usage is: "

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

  #   он лучше соответствовал структуре каталогов на вашем диске.

  #   Наиболее вероятно, что вам придется заменить имя /Users на /home.

  find / /usr /var /Users -xdev -user $name -type f -ls | \

    awk '{ sum += $7 } END { print sum / (1024*1024) " Mbytes" }'

  done | awk "\$9 > $MAXDISKUSAGE { print \$0 }"

exit 0

Как это работает

В соответствии с соглашениями, идентификаторы пользователей (User ID, UID) от 1 до 99 отводятся для системных демонов и административных задач, а идентификаторы со значениями 100 и выше можно выбирать для учетных записей обычных пользователей. Поскольку администраторы Linux обычно весьма организованные люди, этот сценарий пропускает все учетные записи со значениями UID меньше 100.

Аргумент -xdev в вызове команды find гарантирует, что find не будет выполнять поиск во всех файловых системах. Иными словами, этот аргумент предотвращает обход командой системных областей, каталогов, доступных только для чтения, извлекаемых устройств, каталога /proc действующих процессов (в Linux) и других подобных областей. Вот почему в список явно включены такие каталоги, как /usr, /var и /home. Эти каталоги часто размещаются в отдельных файловых системах для упрощения их резервного копирования и организации. Добавление их в список, когда они действительно находятся в корневой файловой системе, не означает, что они будут просмотрены дважды.

На первый взгляд кажется, что сценарий выведет сообщение exceeds disk quota (превысил дисковую квоту) для любой учетной записи, но это не так: команда awk, следующая за концом цикла , позволит вывести такое сообщение только для учетных записей, файлы которых занимают больше чем MAXDISKUSAGE.

Запуск сценария

Сценарий не имеет аргументов и должен запускаться с привилегиями root, чтобы гарантировать доступность всех каталогов и файловых систем. Запускать сценарии с такими привилегиями предпочтительнее с помощью команды sudo (выполните команду man sudo в окне терминала, чтобы получить дополнительную информацию). Почему именно с помощью sudo? Потому что такой прием позволяет выполнить с привилегиями root только одну команду, после чего привилегии командной оболочки будут вновь понижены до уровня обычного пользователя. Каждый раз, когда вам потребуется выполнить административную команду, используйте для этого sudo. Использование команды su - root, напротив, позволит выполнить все последующие команды с привилегиями root, пока подоболочка не будет закрыта явно, а отвлекшись на что-то срочное, легко забыть, что вы получили привилегии root, и есть риск по ошибке сделать что-то, что приведет к разрушительным последствиям.

ПРИМЕЧАНИЕ

Измените список каталогов в команде find , чтобы он точно соответствовал структуре каталогов на вашем диске.

Результаты

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

Листинг 5.2. Тестирование сценария fquota

$ sudo fquota

User taylor exceeds disk quota. Disk usage is: 21799.4 Mbytes

Как видите, пользователь taylor вышел из-под контроля! Объем его файлов составил 21 Гбайт, что намного больше квоты в 20 Гбайт, выделяемой каждому пользователю.

Усовершенствование сценария

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

№ 36. Уведомление о превышении квоты дискового пространства

Большинство системных администраторов стремятся найти самый простой способ решения проблемы, а самый простой способ организовать управление дисковыми квотами — добавить в сценарий fquota (сценарий № 35) рассылку предупреждений по электронной почте пользователям, занявшим слишком большой объем дискового пространства, как показано в листинге 5.3.

Код

Листинг 5.3. Сценарий diskhogs

  #!/bin/bash

  # diskhogs -- инструмент анализа расходования дискового пространства для Unix;

  #   предполагается, что все учетные записи рядовых пользователей

  #   имеют числовые идентификаторы UID >= 100.

  #   Рассылает электронные письма с предупреждением всем нарушителям

  #   и выводит на экран общий отчет.

  MAXDISKUSAGE=500

  violators="/tmp/diskhogs0.$$"

  trap "$(which rm) -f $violators" 0

  for name in $(cut -d: -f1,3 /etc/passwd | awk -F: '$2 > 99 { print $1 }')

  do

    /bin/echo -n "$name "

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

    #   он лучше соответствовал структуре каталогов на вашем диске.

    #   Наиболее вероятно, что вам придется заменить имя /Users на /home.

    find / /usr /var /Users -xdev -user $name -type f -ls | \

      awk '{ sum += $7 } END { print sum / (1024*1024) }'

  done | awk "\$2 > $MAXDISKUSAGE { print \$0 }" > $violators

  if [ ! -s $violators ] ; then

    echo "No users exceed the disk quota of ${MAXDISKUSAGE}MB"

    cat $violators

    exit 0

  fi

  while read account usage ; do

  cat << EOF | fmt | mail -s "Warning: $account Exceeds Quota" $account

    Your disk usage is ${usage}MB, but you have been allocated only

    ${MAXDISKUSAGE}MB. This means that you need to delete some of your

    files, compress your files (see 'gzip' or 'bzip2' for powerful and

    easy-to-use compression programs), or talk with us about increasing

    your disk allocation.

    Thanks for your cooperation in this matter.

    Your friendly neighborhood sysadmin

  EOF

    echo "Account $account has $usage MB of disk space. User notified."

  done < $violators

  exit 0

Как это работает

При создании этого сценария за основу был взят сценарий № 35. Изменения отмечены номерами , , , и . Обратите внимание на дополнительную команду fmt в конвейере, передающем текст программе отправки электронной почты .

Этот трюк помогает улучшить вид автоматически сгенерированного электронного письма, когда в тексте имеются поля неизвестной длины, такие как $account. Логика работы цикла for несколько отличается от логики работы цикла for в сценарии № 35: так как вывод этого цикла предназначен исключительно для использования во второй части сценария, в каждой итерации он просто выводит имя учетной записи и объем занятого дискового пространства, а не сообщение об ошибке exceeds disk quota (превысил дисковую квоту).

Запуск сценария

Сценарий не имеет аргументов и должен запускаться с привилегиями root, чтобы гарантировать точность результатов. Для большей безопасности желательно запускать сценарий командой sudo, как показано в листинге 5.4.

Результаты

Листинг 5.4. Тестирование сценария diskhogs

$ sudo diskhogs

Account ashley has 539.7MB of disk space. User notified.

Account taylor has 91799.4MB of disk space. User notified.

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

Листинг 5.5. Электронное письмо, отправленное пользователю ashley после превышения дисковой квоты

Subject: Warning: ashley Exceeds Quota

Your disk usage is 539.7MB, but you have been allocated only 500MB. This means

that you need to delete some of your files, compress your files (see 'gzip' or

'bzip2' for powerful and easy-to-use compression programs), or talk with us

about increasing your disk allocation.

Thanks for your cooperation in this matter.

Your friendly neighborhood sysadmin

Усовершенствование сценария

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

№ 37. Увеличение удобочитаемости вывода команды df

Вывод утилиты df порой выглядит очень непонятным, но мы можем увеличить его удобочитаемость. Сценарий в листинге 5.6 преобразует счетчики байтов в выводе df в более понятные единицы измерения.

Код

Листинг 5.6. Сценарий newdf, обертка для df, помогающая получить более удобочитаемый вывод

  #!/bin/bash

  # newdf -- более дружественная версия df

  awkscript="/tmp/newdf.$$"

  trap "rm -f $awkscript" EXIT

  cat << 'EOF' > $awkscript

  function showunit(size)

  { mb = size / 1024; prettymb=(int(mb * 100)) / 100;

    gb = mb / 1024; prettygb=(int(gb * 100)) / 100;

    if ( substr(size,1,1) !~ "[0-9]" ||

         substr(size,2,1) !~ "[0-9]" ) { return size }

    else if ( mb < 1) { return size "K" }

    else if ( gb < 1) { return prettymb "M" }

    else { return prettygb "G" }

  }

  BEGIN {

    printf "%-37s %10s %7s %7s %8s %-s\n",

          "Filesystem", "Size", "Used", "Avail", "Capacity", "Mounted"

  }

  !/Filesystem/ {

    size=showunit($2);

  used=showunit($3);

  avail=showunit($4);

  printf "%-37s %10s %7s %7s %8s %-s\n",

        $1, size, used, avail, $5, $6

  }

  EOF

df -k | awk -f $awkscript

  exit 0

Как это работает

Основная работа выполняется awk-сценарием, и не составило бы большого труда написать весь сценарий на awk, а не на языке командной оболочки, применив в нем функцию system() для вызова команды df. (Вообще, этот пример — идеальный кандидат, чтобы переписать его на языке Perl, но наша книга совсем не о том.)

В этом сценарии используется старый трюк, в строках и , пришедший из языка BASIC.

Быстро ограничить количество знаков после десятичной точки при работе с числами произвольной точности можно, умножив число на степень 10, преобразовав произведение в целое число (отбросив дробную часть) и разделив результат на ту же степень 10: prettymb=(int(mb * 100)) / 100;. Этот код, например, превратит значение 7,085344324 в более привлекательное 7,08.

ПРИМЕЧАНИЕ

Некоторые версии df поддерживают флаг -h, позволяющий получить похожий вывод. Однако этот сценарий, как и многие другие в данной книге, обеспечивает более дружественный и понятный вывод в любой системе, Unix или Linux, независимо от используемой версии df.

Запуск сценария

Сценарий не имеет аргументов и может запускаться с любыми привилегиями, в том числе с привилегиями root. Чтобы исключить строки с информацией об устройствах, которые вам не интересны, используйте команду grep -v после вызова df.

Результаты

Обычная команда df выводит результаты в виде, трудном для понимания, как показано в листинге 5.7.

Листинг 5.7. В выводе по умолчанию команды df сложно разобраться

$ df

Filesystem                        512-blocks Used      Available Capacity Mounted on

/dev/disk0s2                      935761728  628835600 306414128 68%      /

devfs                             375        375       0         100%     /dev

map -hosts                        0          0         0         100%     /net

map auto_home                     0          0         0         100%     /home

localhost:/mNhtYYw9t5GR1SlUmkgN1E 935761728  935761728 0         100%     /Volumes/                                                                           Mobile-                                                                           Backups

Новый сценарий использует awk для увеличения удобочитаемости и преобразует 512-байтные блоки в более понятный формат, как можно видеть в листинге 5.8.

Листинг 5.8. Простой и понятный вывод сценария newdf

$ newdf

Filesystem                        Size   Used    Avail   Capacity Mounted

/dev/disk0s2                      446.2G 299.86G 146.09G 68%      /

devfs                             187K   187K    0       100%     /dev

map -hosts                        0      0       0       100%

map auto_home                     0      0       0       100%

localhost:/mNhtYYw9t5GR1SlUmkgN1E 446.2G 446.2G  0       100%     /Volumes/                                                                    Mobile-                                                                    Backups

Усовершенствование сценария

В этом сценарии много недостатков, и один из самых значительных — наличие версий df, включающих информацию об использовании индексных узлов (inode) и даже внутреннюю информацию о процессоре, хотя она не представляет никакого интереса (как две записи map в примере выше). Сценарий был бы намного полезнее, если бы мы удалили вывод подобной ненужной информации, поэтому в первую очередь стоить применить флаг -P в вызове df, ближе к концу сценария , чтобы удалить из вывода информацию об использовании индексных узлов. (Ее можно было бы вывести в отдельном столбце, но тогда вывод станет еще шире и форматировать его станет труднее.) Чтобы удалить записи map, достаточно воспользоваться командой grep. Просто добавьте в конец команды |grep -v "^map" , и вы навсегда избавитесь от них.

№ 38. Определение доступного пространства на диске

Коль скоро сценарий № 37 способен упростить вывод команды df, чтобы его было легче читать и понимать, тогда на более простой вопрос об объеме доступного дискового пространства в системе тем более можно ответить с помощью сценария командной оболочки. Команда df действительно сообщает информацию для каждого диска, но для ее осмысления требуется приложить некоторые усилия:

$ df

Filesystem    1K-blocks Used     Available Use% Mounted on

/dev/hdb2     25695892  1871048  22519564  8%   /

/dev/hdb1     101089    6218     89652     7%   /boot

none          127744    0        127744    0%   /dev/shm

Более полезная версия df могла бы суммировать числа в колонке «Available» (Доступно) и выводить ее в удобочитаемом виде. Эта задача легко решается с помощью команды awk, как показано в листинге 5.9.

Код

Листинг 5.9. Сценарий diskspace, удобная обертка для df, сообщающая информацию в дружественном формате

  #!/bin/bash

  # diskspace -- суммирует доступное дисковое пространство и выводит сумму

  #   в логичном и удобочитаемом виде

  tempfile="/tmp/available.$$"

  trap "rm -f $tempfile" EXIT

  cat << 'EOF' > $tempfile

      { sum += $4 }

  END { mb = sum / 1024

        gb = mb / 1024

        printf "%.0f MB (%.2fGB) of available disk space\n", mb, gb

      }

  EOF

  df -k | awk -f $tempfile

  exit 0

Как это работает

Сценарий diskspace опирается на временный awk-сценарий, который сохраняется в каталоге /tmp. Этот awk-сценарий вычисляет общий объем доступного дискового пространства на основе переданных ему данных и затем выводит результат в удобочитаемом формате. Результаты вызова команды df по конвейеру передаются команде awk , которая в свою очередь выполняет операции, определяемые awk-сценарием. Когда работа сценария завершается, временный awk-сценарий удаляется из каталога /tmp благодаря обработчику сигнала выхода, установленному командой trap в начале сценария.

Запуск сценария

Этот сценарий, который может запустить любой пользователь, выводит короткую строку с информацией о суммарном объеме доступного дискового пространства.

Результаты

В той же системе, где был получен вывод команды df, показанный выше, этот сценарий выведет строку, представленную в листинге 5.10.

Листинг 5.10. Тестирование сценария diskspace

$ diskspace

96199 MB (93.94GB) of available disk space

Усовершенствование сценария

Если в вашей системе несколько многотерабайтных дисков, вы могли научить сценарий автоматически выводить значение в терабайтах. В случае исчерпания дискового пространства будет особенно неприятно увидеть, что доступно всего 0,03 Гб — но это отличный повод запустить сценарий № 36, чтобы подтолкнуть пользователей удалить ненужные файлы, разве не так?

Обратите внимание еще на одну проблему: имеет ли смысл учитывать доступное дисковое пространство на всех устройствах, включая разделы, которые точно не будут заполняться, такие как /boot, или достаточно сообщать информацию только о пользовательских разделах? В последнем случае этот сценарий можно было бы усовершенствовать, добавив вызов grep сразу после вызова df . Используйте grep с именами нужных устройств, чтобы включить в расчеты только определенные устройства, или grep -v с именами ненужных устройств, чтобы исключить из расчетов информацию о них.

№ 39. Реализация защищенной команды locate

Сценарий locate, представленный в сценарии № 19 (глава 2), очень полезен, но создает угрозу безопасности: если процесс сбора данных запустить с привилегиями root, он составит полный список файлов и каталогов во всей системе, независимо от их владельца, что даст возможность обычным пользователям увидеть имена файлов каталогов, к которым у них нет доступа. Процесс сбора информации можно запустить с привилегиями обобщенного пользователя (как это делается в OS X, где mklocatedb запускается с привилегиями пользователя nobody), но и это не самое правильное решение, потому что вам может понадобиться найти файл где-нибудь в дереве вашего домашнего каталога, независимо от наличия прав доступа к этим файлам и каталогам у пользователя nobody.

Одно из решений этой дилеммы состоит в том, чтобы расширить записи, хранящиеся в базе данных locate, дополнив их сведениями о владельце, группе и привилегиях доступа. Но сама база данных mklocatedb все равно останется незащищенной, если только не запускать сценарий locate с привилегией setuid или setgid, чего желательно всячески избегать в интересах безопасности всей системы.

Компромиссное решение — создавать файл .locatedb отдельно для каждого пользователя. Это не самый худший вариант, потому что личные базы данных нужны только пользователям, которые действительно пользуются командой locate. После вызова система создаст файл .locatedb в домашнем каталоге пользователя, а его своевременное обновление можно переложить на задание cron, выполняющееся по ночам. Когда пользователь запустит защищенный сценарий slocate в самый первый раз, он увидит сообщение, предупреждающее о том, что он может выполнять поиск только среди общедоступных файлов. Запустив сценарий на следующий день (в зависимости от того, на какое время запланирован запуск задания cron), пользователи будут получать свои, персонализированные результаты.

Код

Защищенная версия locate состоит из двух сценариев: конструктора базы данных mkslocatedb (представленного в листинге 5.11), и утилиты поиска slocate (представленной в листинге 5.12).

Листинг 5.11. Сценарий mkslocatedb

  #!/bin/bash

  # mkslocatedb -- создает центральную базу данных общедоступных файлов,

  #   выполняясь с привилегиями пользователя nobody, и одновременно обходит

  #   домашние каталоги всех пользователей в поисках  файла .slocatedb.

  #   Если файл найден, для пользователя создается дополнительная, личная

  #   версия базы данных поиска файлов.

  locatedb="/var/locate.db"

  slocatedb=".slocatedb"

  if [ "$(id -nu)" != "root" ] ; then

    echo "$0: Error: You must be root to run this command." >&2

    exit 1

  fi

  if [ "$(grep '^nobody:' /etc/passwd)" = "" ] ; then

    echo "$0: Error: you must have an account for user 'nobody'" >&2

    echo "to create the default slocate database." >&2

    exit 1

  fi

  cd /   # Предотвратить проблемы нехватки прав доступа после команды su

  # Сначала создать или обновить общедоступную базу данных.

  su -fm nobody -c "find / -print" > $locatedb 2>/dev/null

  echo "building default slocate database (user = nobody)"

  echo ... result is $(wc -l < $locatedb) lines long.

  # Теперь обойти учетные записи пользователей и посмотреть,

  #   у кого в домашнем каталоге имеется файл .slocatedb.

  for account in $(cut -d: -f1 /etc/passwd)

  do

    homedir="$(grep "^${account}:" /etc/passwd | cut -d: -f6)"

    if [ "$homedir" = "/" ] ; then

      continue   # Не создавать в корневом каталоге.

    elif [ -e $homedir/$slocatedb ] ; then

      echo "building slocate database for user $account"

      su -m $account -c "find / -print" > $homedir/$slocatedb \

        2>/dev/null

      chmod 600 $homedir/$slocatedb

      chown $account $homedir/$slocatedb

      echo ... result is $(wc -l < $homedir/$slocatedb) lines long.

    fi

  done

  exit 0

Сам сценарий slocate (в листинге 5.12) — это пользовательский интерфейс к базе данных slocate.

Листинг 5.12. Сценарий slocate, сопутствующий сценарий для mkslocatedb

#!/bin/bash

# slocate -- выполняет поиск собственной, защищенной базы данных locatedb

#   пользователя по указанному шаблону. Если база данных не найдена, это

#   означает, что она отсутствует, тогда выводится предупреждающее сообщение

#   и создается новая база данных. Если личная база данных .slocatedbis пустая,

#   вместо нее используется системная.

locatedb="/var/locate.db"

slocatedb="$HOME/.slocatedb"

if [ ! -e $slocatedb -o "$1" = "--explain" ] ; then

  cat << "EOF" >&2

Warning: Secure locate keeps a private database for each user, and your

database hasn't yet been created. Until it is (probably late tonight),

I'll just use the public locate database, which will show you all

publicly accessible matches rather than those explicitly available to

account ${USER:-$LOGNAME}.

EOF

  if [ "$1" = "--explain" ] ; then

    exit 0

  fi

  # Перед продолжением создать файл .slocatedb, чтобы задание cron заполнило

  #   его, когда в следующий раз сценарий mkslocatedb будет запущен.

  touch $slocatedb     # mkslocatedb заполнит этот файл при следующем запуске

  chmod 600 $slocatedb # Установить безопасные привилегии

elif [ -s $slocatedb ] ; then

  locatedb=$slocatedb

else

  echo "Warning: using public database. Use \"$0 --explain\" for details." >&2

fi

if [ -z "$1" ] ; then

  echo "Usage: $0 pattern" >&2

  exit 1

fi

exec grep -i "$1" $locatedb

Как это работает

Сценарий mkslocatedb основан на идее, что процесс, запущенный с привилегиями root, может временно приобретать привилегии разных пользователей, используя команду su -fm user . После этого он может выполнить команду find с привилегиями каждого пользователя для создания персонализированных баз данных с именами файлов. Однако, работая с командой su внутри сценария, необходимо соблюдать некоторые меры предосторожности, потому что по умолчанию su не только изменяет действующий идентификатор пользователя, но также импортирует окружение для выбранной учетной записи. Это может приводить к странным и запутывающим сообщениям об ошибках, если только не использовать в команде флаг -m, запрещающий импорт пользовательского окружения. Флаг -f — это дополнительная мера предосторожности, помогающая предотвратить загрузку файла .cshrc для учетных записей, использующих командную оболочку csh или tcsh.

Еще одна необычная конструкция в строке , 2>/dev/null, которая отправляет все сообщения об ошибках в пресловутый битоприемник: все, что посылается в /dev/null, исчезает без следа. Это самый простой способ избавиться от неизбежных сообщений о недостаточности привилегий, которые выводит команда find в каждом вызове.

Запуск сценария

mkslocatedb — сценарий, необычный не только тем, что должен запускаться с привилегиями root, но и тем, что использования команды sudo для его запуска будет недостаточно. Вы должны войти в систему как пользователь root или использовать более мощную команду su, чтобы приобрести привилегии root перед запуском сценария. Это объясняется тем, что su фактически превращает вас в суперпользователя root, тогда как sudo просто дает текущему пользователю привилегии root на время. Команда sudo устанавливает другие права доступа к файлам, чем команда su. Сценарий slocate, конечно, не предъявляет таких требований.

Результаты

В результате попытки создать базы данных для пользователей nobody (общедоступная база данных) и taylor в системе Linux на экран будут выведены строки, как показано в листинге 5.13.

Листинг 5.13. Запуск сценария mkslocatedb с привилегиями root

# mkslocatedb

building default slocate database (user = nobody)

... result is 99809 lines long.

building slocate database for user taylor

... result is 99808 lines long.

Теперь давайте сначала попробуем найти конкретный файл или группу файлов, соответствующих заданному шаблону, зарегистрировавшись в системе как пользователь tintin (в домашнем каталоге которого нет файла .slocatedb):

tintin $ slocate Taylor-Self-Assess.doc

Warning: using public database. Use "slocate --explain" for details.

$

Теперь введем ту же команду от имени пользователя taylor, которому принадлежит разыскиваемый файл:

taylor $ slocate Taylor-Self-Assess.doc

/Users/taylor/Documents/Merrick/Taylor-Self-Assess.doc

Усовершенствование сценария

Если ваша файловая система имеет огромный объем, такой подход может привести к потреблению значительного пространства на диске. Одно из решений проблемы — не включать в персональные базы данных .slocatedb записи, имеющиеся в центральной базе данных. Это потребует выполнения дополнительных операций (сортировать оба файла командой sort и затем отыскивать различия командой diff или просто пропускать каталоги /usr и /bin, когда выполняется поиск индивидуальных файлов пользователей), но поможет сэкономить место на диске. Другой способ экономии — добавлять в индивидуальные файлы .slocatedb только ссылки на файлы, к которым выполнялось обращение с момента последнего обновления. Этот прием будет работать лучше, если сценарий mkslocatedb запускать не каждый день, а раз в неделю; иначе все пользователи встретят понедельник с пустыми базами данных, потому что едва ли кто-то из них будет запускать команду slocate в выходные.

Наконец, еще один простой способ сэкономить место на диске — хранить файлы .slocatedb в сжатом виде и разжимать их «на лету», во время поиска командой slocate. Идею можно подсмотреть в реализации команды zgrep, в сценарии № 33 (глава 4).

№ 40. Добавление пользователей в систему

Если вы отвечаете за поддержку сетей в системах Unix или Linux, вас наверняка расстраивают мелкие несовместимости между разными операционными системами, имеющимися в вашем распоряжении. Некоторые самые простые задачи администрирования оказываются несовместимы с разными разновидностями Unix, и главная из них — управление учетными записями пользователей. Вместо одной команды, на 100% совместимой со всеми разновидностями Linux, каждый производитель норовит создать собственную программу с графическим интерфейсом для работы с настройками своей системы.

Казалось бы, простой протокол управления сетью (Simple Network Management Protocol, SNMP) должен помогать в нормализации подобных отклонений, тем не менее управление учетными записями пользователей остается таким же сложным делом, как лет десять тому назад, особенно в гетерогенных окружениях. Как результат, полезные наборы сценариев для системных администраторов включают версии adduser, suspenduser и deleteuser, которые можно настроить под конкретные потребности и затем легко перенести на все системы Unix. Далее мы рассмотрим сценарий adduser, а в следующих двух разделах — сценарии suspenduser и deleteuser.

ПРИМЕЧАНИЕ

Операционная система OS X со своей отдельной базой данных для хранения учетных записей пользователей является исключением из правил. Чтобы сохранить душевное здоровье, просто пользуйтесь версиями приведенных команд для Mac, не стараясь вникнуть в тонкости администрирования этой базы данных из командной строки.

В Linux учетная запись создается добавлением в файл /etc/passwd уникальной записи, включающей имя учетной записи длиной от одного до восьми символов, уникальный числовой идентификатор пользователя, числовой идентификатор группы, путь к домашнему каталогу и командную оболочку входа для этого пользователя. Современные системы хранят шифрованные пароли в /etc/shadow, так что для каждого нового пользователя в этом файле также должна быть создана запись. Наконец, учетная запись должна быть указана в файле /etc/group, в собственной группе (эта стратегия реализована в данном сценарии) или в составе существующей группы. Реализация всех перечисленных шагов приводится в листинге 5.14.

Код

Листинг 5.14. Сценарий adduser

  #!/bin/bash

  # adduser -- добавляет нового пользователя в систему, включая создание

  #   домашнего каталога, копирование конфигурационных данных по умолчанию

  #   и так далее.

  #   Для стандартных систем Unix/Linux, не для OS X.

  pwfile="/etc/passwd"

  shadowfile="/etc/shadow"

  gfile="/etc/group"

  hdir="/home"

  if [ "$(id -un)" != "root" ] ; then

    echo "Error: You must be root to run this command." >&2

    exit 1

  fi

  echo "Add new user account to $(hostname)"

  /bin/echo -n "login: " ; read login

  # Следующая строка ограничивает максимальный числовой идентификатор

  #   пользователя значением 5000, скорректируйте это значение,

  #   чтобы оно соответствовало верхней границе вашего диапазона

  #   числовых идентификаторов пользователей.

  uid="$(awk -F: '{ if (big < $3 && $3 < 5000) big=$3 } END { print big + 1 }'\

         $pwfile)"

  homedir=$hdir/$login

  # Для каждого пользователя создается собственная группа.

  gid=$uid

  /bin/echo -n "full name: " ; read fullname

  /bin/echo -n "shell: " ; read shell

  echo "Setting up account $login for $fullname..."

  echo ${login}:x:${uid}:${gid}:${fullname}:${homedir}:$shell >> $pwfile

  echo ${login}:*:11647:0:99999:7::: >> $shadowfile

  echo "${login}:x:${gid}:$login" >> $gfile

  mkdir $homedir

  cp -R /etc/skel/.[a-zA-Z]* $homedir

  chmod 755 $homedir

  chown -R ${login}:${login} $homedir

  # Установка начального пароля

  exec passwd $login

Как это работает

Самая замысловатая команда в этом сценарии находится в строке . Она перебирает записи в файле /etc/passwd, отыскивает наибольший числовой идентификатор, который меньше наибольшего допустимого значения для учетных записей пользователей (в этом сценарии используется число 5000, но вы должны скорректировать его для своей конфигурации), и затем прибавляет 1, чтобы получить числовой идентификатор для новой учетной записи. Это избавляет администратора от необходимости запоминать следующий доступный числовой идентификатор, а также гарантирует высокую степень согласованности информации об учетных записях в процессе развития и изменения коллектива пользователей.

Сценарий добавляет учетную запись с новым числовым идентификатором. Затем создает домашний каталог для нового пользователя и копирует в него содержимое каталога /etc/skel. В соответствии с соглашениями, каталог /etc/skel должен хранить шаблоны файлов .cshrc, .login, .bashrc и .profile. На сайтах, где имеется веб-сервер, поддерживающий службу ~account, в новый домашний каталог необходимо также скопировать каталог, такой как /etc/skel/public_html. Это особенно удобно, если в вашей организации предусматривается настройка рабочих станций с Linux для инженеров или разработчиков специальными конфигурациями bash.

Запуск сценария

Этот сценарий не имеет аргументов и должен запускаться с привилегиями root.

Результаты

В нашей системе уже есть учетная запись для tintin, поэтому мы решили создать отдельную учетную запись для snowy (как показано в листинге 5.15).

Листинг 5.15. Тестирование сценария adduser

$ sudo adduser

Add new user account to aurora

login: snowy

full name: Snowy the Dog

shell: /bin/bash

Setting up account snowy for Snowy the Dog...

Changing password for user snowy.

New password:

Retype new password:

passwd: all authentication tokens updated successfully.

Усовершенствование сценария

Одним из значительных преимуществ использования собственного сценария adduser является возможность расширять его и изменять логику отдельных операций, не заботясь об обновлении системы под эти изменения. В числе вероятных расширений автоматическая отправка приветственного электронного письма, в общих чертах обрисовывающего порядок работы и способы получения справочной информации, автоматическая печать на бумаге сводной информации об учетной записи для передачи пользователю, добавление псевдонима firstname_lastname или firstname.lastname в файл aliases сервера электронной почты и даже копирование комплекта файлов в домашний каталог учетной записи, чтобы пользователь мог немедленно включиться в коллективную работу над проектом.

№ 41. Приостановка действия учетной записи

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

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

Большая часть сценария в листинге 5.16 связана с определением присутствия пользователя в системе, его уведомлением о завершении сеанса и принудительным выводом из системы.

Код

Листинг 5.16. Сценарий suspenduser

#!/bin/bash

# suspenduser -- приостанавливает действие учетной записи до неопределенного

#   момента в будущем

homedir="/home" # Местонахождение домашних каталогов пользователей

secs=10         # Пауза в секундах перед выводом пользователя из системы

if [ -z $1 ] ; then

  echo "Usage: $0 account" >&2

  exit 1

elif [ "$(id -un)" != "root" ] ; then

  echo "Error. You must be 'root' to run this command." >&2

  exit 1

fi

echo "Please change the password for account $1 to something new."

passwd $1

# Теперь посмотрим, если пользователь зарегистрирован в системе.

#   выведем его принудительно.

if who|grep "$1" > /dev/null ; then

  for tty in $(who | grep $1 | awk '{print $2}'); do

    cat << "EOF" > /dev/$tty

******************************************************************************

URGENT NOTICE FROM THE ADMINISTRATOR:

This account is being suspended, and you are going to be logged out

in $secs seconds. Please immediately shut down any processes you

have running and log out.

If you have any questions, please contact your supervisor or

John Doe, Director of Information Technology.

******************************************************************************

EOF

  done

  echo "(Warned $1, now sleeping $secs seconds)"

  sleep $secs

  jobs=$(ps -u $1 | cut -d\ -f1)

kill -s HUP $jobs # Послать сигнал остановки процессам пользователя.

   sleep 1           # Дать одну секунду...

kill -s KILL $jobs > /dev/null 2>1 # и остановить те, что еще остались.

   echo "$1 was logged in. Just logged them out."

fi

# В заключение закрыть домашний каталог от любопытных глаз.

chmod 000 $homedir/$1

echo "Account $1 has been suspended."

exit 0

Как это работает

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

Обратите внимание, что сценарий посылает сигнал остановки SIGHUP (HUP) всем процессам, запущенным пользователем , ждет одну секунду и затем посылает более жесткий сигнал SIGKILL (KILL) . Сигнал SIGHUP завершает работу запущенного приложения, но не всегда, и оболочка входа не реагирует на него. Однако сигнал SIGKILL не может быть проигнорирован или заблокирован, поэтому он действует со стопроцентной гарантией. Однако такой способ остановки приложений нельзя назвать предпочтительным, потому что этот сигнал не дает приложению возможности удалить временные файлы, вытолкнуть буферы, чтобы гарантировать запись изменений на диск, и выполнить другие заключительные операции.

Разблокирование пользователя выполняется в два шага: открыть его домашний каталог (командой chmod 700) и установить известный пользователю пароль (командой passwd).

Запуск сценария

Этот сценарий должен запускаться с привилегиями root и принимает один аргумент: имя учетной записи, действие которой требуется приостановить.

Результаты

Выяснилось, что пользователь snowy нарушил правила пользования учетной записи. Давайте приостановим ее действие, как показано в листинге 5.17.

Листинг 5.17. Тестирование сценария suspenduser на пользователе snowy

$ sudo suspenduser snowy

Please change the password for account snowy to something new.

Changing password for user snowy.

New password:

Retype new password:

passwd: all authentication tokens updated successfully.

(Warned snowy, now sleeping 10 seconds)

snowy was logged in. Just logged them out.

Account snowy has been suspended.

Так как snowy в этот момент был зарегистрирован в системе, он получил сообщение, показанное в листинге 5.18, за несколько секунд до того, как его принудительно вывели из системы.

Листинг 5.18. Текст предупреждения, появившийся на терминале пользователя перед его отключением

******************************************************************************

URGENT NOTICE FROM THE ADMINISTRATOR:

This account is being suspended, and you are going to be logged out

in 10 seconds. Please immediately shut down any processes you

have running and log out.

If you have any questions, please contact your supervisor or

John Doe, Director of Information Technology.

******************************************************************************

№ 42. Удаление учетной записи

Удаление учетной записи немного сложнее в реализации, чем приостановка ее действия, потому что сценарий должен прочесать всю файловую систему в поисках файлов, принадлежащих удаляемой учетной записи, прежде чем информация о ней будет стерта из файлов /etc/passwd и /etc/shadow. Сценарий в листинге 5.19 гарантирует полное удаление из системы учетной записи и всех ее данных. Предполагается, что предыдущий сценарий suspenduser находится в одном из каталогов, перечисленных в текущем значении переменной PATH.

Код

Листинг 5.19. Сценарий deleteuser

  #!/bin/bash

  # deleteuser -- удаляет учетную запись без следа.

  #   Не предназначен для использования в OS X.

  homedir="/home"

  pwfile="/etc/passwd"

  shadow="/etc/shadow"

  newpwfile="/etc/passwd.new"

  newshadow="/etc/shadow.new"

  suspend="$(which suspenduser)"

  locker="/etc/passwd.lock"

  if [ -z $1 ] ; then

    echo "Usage: $0 account" >&2

    exit 1

  elif [ "$(whoami)" != "root" ] ; then

    echo "Error: you must be 'root' to run this command.">&2

    exit 1

  fi

  $suspend $1 # Заблокировать учетную запись на время выполнения работы.

  uid="$(grep -E "^${1}:" $pwfile | cut -d: -f3)"

  if [ -z $uid ] ; then

    echo "Error: no account $1 found in $pwfile" >&2

    exit 1

  fi

  # Удалить пользователя из файлов password и shadow.

  grep -vE "^${1}:" $pwfile > $newpwfile

  grep -vE "^${1}:" $shadow > $newshadow

  lockcmd="$(which lockfile)" # Найти приложение lockfile.

  if [ ! -z $lockcmd ] ; then # Использовать системную команду lockfile.

    eval $lockcmd -r 15 $locker

  else                        # Не вышло, используем свой механизм.

    while [ -e $locker ] ; do

      echo "waiting for the password file" ; sleep 1

    done

    touch $locker            # Создать блокировку на основе файла.

  fi

  mv $newpwfile $pwfile

  mv $newshadow $shadow

rm -f $locker              # Щелк! Снять блокировку.

  chmod 644 $pwfile

  chmod 400 $shadow

  # Теперь удалить домашний каталог и перечислить все, что осталось.

  rm -rf $homedir/$1

  echo "Files still left to remove (if any):"

  find / -uid $uid -print 2>/dev/null | sed 's/^/ /'

  echo ""

  echo "Account $1 (uid $uid) has been deleted, and their home directory "

  echo "($homedir/$1) has been removed."

  exit 0

Как это работает

Чтобы избежать любых изменений в учетной записи в то время, пока работает сценарий deleteuser, сразу после запуска он приостанавливает ее действие, вызывая suspenduser.

Перед изменением файла с паролями этот сценарий блокирует доступ к нему с помощью программы lockfile, если она доступна . Как вариант, для со­здания файла-блокировки в Linux можно также использовать утилиту flock. Если этой программы нет, сценарий использует относительно примитивный механизм блокировки, основанный на создании файла /etc/passwd.lock. Если файл-блокировка уже существует , сценарий ждет его удаления другой программой, после чего создает свой файл, выполняет необходимые операции и удаляет его по завершении .

Запуск сценария

Этот сценарий должен запускаться с привилегиями root (с помощью sudo) и в качестве аргумента ожидает получить имя учетной записи для удаления. В листинге 5.20 показан запуск сценария для удаления учетной записи пользователя snowy.

ВНИМАНИЕ

Действия, выполняемые сценарием, необратимы, и в ходе своей работы он удаляет много файлов, поэтому будьте осторожны во время экспериментов с ним!

Результаты

Листинг 5.20. Тестирование сценария deleteuser на учетной записи пользователя snowy

$ sudo deleteuser snowy

Please change the password for account snowy to something new.

Changing password for user snowy.

New password:

Retype new password:

passwd: all authentication tokens updated successfully.

Account snowy has been suspended.

Files still left to remove (if any):

   /var/log/dogbone.avi

Account snowy (uid 502) has been deleted, and their home directory

(/home/snowy) has been removed.

Пользователь snowy попытался спрятать AVI-файл (dogbone.avi) в каталоге /var/log. Но мы благополучно нашли его — кто знает, что там может быть?

Усовершенствование сценария

Сценарий deleteuser преднамеренно был создан неполным. Вы должны решить, что делать с файлами, принадлежащими удаляемой учетной записи: сжать их и поместить в архив, записать на ленту, скопировать в облачное хранилище, сохранить на DVD или даже послать их по почте прямо в ФБР (в последнем случае мы просто пошутили). Кроме всего прочего упоминание об учетной записи необходимо удалить из файла /etc/group. Если за пределами домашнего каталога имеются файлы, принадлежащие учетной записи, команда find найдет их, но администратор должен сам просмотреть их и решить, что с ними делать, удалить или оставить.

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

№ 43. Проверка пользовательского окружения

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

Сложное решение — сначала проверить переменную PATH, чтобы гарантировать присутствие в ней только допустимых каталогов, а затем проверить все настройки важнейших вспомогательных программ и убедиться, что полные пути указывают на существующие файлы или что эти файлы находятся в каталогах, перечисленных в PATH. Задачу решает сценарий в листинге 5.21.

Код

Листинг 5.21. Сценарий validator

  #!/bin/bash

  # validator -- проверяет допустимость каталогов в переменной PATH

  #   и затем проверяет допустимость всех остальных переменных окружения.

  #   Проверяются переменные SHELL, HOME, PATH, EDITOR, MAIL и PAGER.

  errors=0

  source library.sh # Содержит сценарий #1 с функцией in_path().

  validate()

  {

    varname=$1

    varvalue=$2

    if [ ! -z $varvalue ] ; then

      if [ "${varvalue%${varvalue#?}}" = "/" ] ; then

        if [ ! -x $varvalue ] ; then

          echo "** $varname set to $varvalue, but I cannot find executable."

          (( errors++ ))

        fi

      else

        if in_path $varvalue $PATH ; then

          echo "** $varname set to $varvalue, but I cannot find it in PATH."

          errors=$(( $errors + 1 ))

        fi

      fi

    fi

  }

  # НАЧАЛО ОСНОВНОГО СЦЕНАРИЯ

  # =================

  if [ ! -x ${SHELL:?"Cannot proceed without SHELL being defined."} ] ; then

    echo "** SHELL set to $SHELL, but I cannot find that executable."

    errors=$(( $errors + 1 ))

  fi

  if [ ! -d ${HOME:?"You need to have your HOME set to your home directory"} ]

  then

    echo "** HOME set to $HOME, but it's not a directory."

    errors=$(( $errors + 1 ))

  fi

  # Первая интересная проверка: все каталоги в PATH допустимы?

  oldIFS=$IFS; IFS=":" # IFS -- разделитель полей. Записать в него ':'.

  for directory in $PATH

  do

    if [ ! -d $directory ] ; then

      echo "** PATH contains invalid directory $directory."

      errors=$(( $errors + 1 ))

    fi

  done

  IFS=$oldIFS # Восстановить прежнее значение разделителя полей.

  # Следующие переменные должны содержать полные пути к файлам программ,

  #   но могут быть не определены или содержать только имена программ.

  #   Добавьте дополнительные переменные в комплект, если это

  #   необходимо для вашего сайта и ваших пользователей.

  validate "EDITOR" $EDITOR

  validate "MAILER" $MAILER

  validate "PAGER" $PAGER

  # И в заключение вывод разных сообщений, в зависимости от значения errors

  if [ $errors -gt 0 ] ; then

    echo "Errors encountered. Please notify sysadmin for help."

  else

    echo "Your environment checks out fine."

  fi

  exit 0

Как это работает

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

$ echo $PATH

/bin/:/sbin:/usr/bin:/sw/bin:/usr/X11R6/bin:/usr/local/mybin

Допустимость переменных окружения оценивает функция validate() , которая прежде всего проверяет, начинается ли значение каждой переменной с символа слеша (/). Если это условие выполняется, функция проверяет наличие указанного выполняемого файла. Если значение переменной не начинается с символа слеша (/), сценарий вызывает функцию in_path(), импортированную из библиотеки, написанную нами в сценарии № 1 (глава 1) , которая проверяет присутствие программы в одном из каталогов, перечисленных в переменной PATH.

Самый необычный аспект сценария — использование значений по умолчанию в некоторых условных выражениях и в операции извлечения подстроки из переменной. Использование значений по умолчанию в условных выражениях вы видите в блоке, начинающемся со строки . Синтаксис ${varname:?"errorMessage"} можно интерпретировать так: «Если переменная varname существует, вернуть ее значение; иначе завершить сценарий и вывести сообщение errorMessage».

Синтаксис извлечения подстроки из переменной ${varvalue%${varvalue#?}}, используемый в строке , — это функция извлечения подстроки, определяемая стандартом POSIX, которая возвращает только первый символ из значения переменной varvalue. Таким образом сценарий определяет, является ли значение переменной полным путем к файлу (начинается с символа слеша / и определяет полный путь к программе).

Если ваша версия Unix/Linux не поддерживает этот синтаксис, его можно заменить более прямолинейными проверками. Например, использовать вместо ${SHELL:?No Shell} следующие строки:

if [ -z "$SHELL" ] ; then

  echo "No Shell" >&2; exit 1

fi

А вместо {varvalue%${varvalue#?}} — следующую строку, дающую тот же результат:

$(echo $varvalue | cut -c1)

Запуск сценария

Этот сценарий пользователи могут запускать для проверки своего окружения. Он не принимает аргументов командной строки и запускается, как показано в лис­тинге 5.22.

Результаты

Листинг 5.22: Тестирование сценария validator

$ validator

** PATH contains invalid directory /usr/local/mybin.

** MAILER set to /usr/local/bin/elm, but I cannot find executable.

Errors encountered. Please notify sysadmin for help.

№ 44. Очистка гостевой учетной записи

Несмотря на то что по соображениям безопасности на многих сайтах запрещен вход с именем пользователя guest, кое-где такая гостевая учетная запись все еще используется (часто с легко угадываемым паролем), чтобы дать клиентам или сотрудникам из других отделов доступ к сети. Это бывает удобно, но есть одна большая проблема: когда одной учетной записью пользуется множество людей, существует опасность, что кто-то из них по неосторожности испортит ее настройки, затруднив работу тех, кто последует за ним. Такое может произойти, например, во время экспериментов с командами, при редактировании файлов .rc или добавлении подкаталогов, и так далее.

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

Код

Листинг 5.23. Сценарий fixguest

#!/bin/bash

# fixguest -- очищает гостевую учетную запись в процессе выхода.

# Не доверяйте переменным окружения: ссылайтесь на источники,

#   доступные только для чтения.

iam=$(id -un)

myhome="$(grep "^${iam}:" /etc/passwd | cut -d: -f6)"

# *** НЕ запускайте этот сценарий в обычной учетной записи!

if [ "$iam" != "guest" ] ; then

  echo "Error: you really don't want to run fixguest on this account." >&2

  exit 1

fi

if [ ! -d $myhome/..template ] ; then

  echo "$0: no template directory found for rebuilding." >&2

  exit 1

fi

# Удалить все файлы и каталоги в домашнем каталоге учетной записи.

cd $myhome

rm -rf * $(find . -name ".[a-zA-Z0-9]*" -print)

# Теперь должен остаться только каталог ..template.

cp -Rp ..template/* .

exit 0

Как это работает

Чтобы сценарий работал правильно, создайте комплект шаблонных файлов и каталогов и поместите их в подкаталог ..template, внутри домашнего каталога гостевой учетной записи. Измените права доступа к каталогу ..template, чтобы он был доступен только для чтения, и затем установите права и принадлежность файлов каталогов внутри ..template, чтобы они соответствовали пользователю guest.

Запуск сценария

Самый подходящий момент для запуска сценария fixguest — выход пользователя из системы. Для этого можно вставить запуск в файл .logout (прием работает почти во всех командных оболочках за редким исключением). Кроме того, вы убережете себя от многих жалоб пользователей, если сценарий login будет выводить, например, такое сообщение:

Внимание: Все файлы будут автоматически удалены из домашнего каталога

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

не сохраняйте здесь ничего важного. Если вам потребуется что-то сохранить,

отправьте это по электронной почте на свой почтовый ящик.

Вы предупреждены!

Однако отдельные знающие пользователи могут скорректировать содержимое файла .logout, поэтому имеет смысл организовать вызов сценария fixguest также из задания cron. Просто в начале сценария нужно убедиться, что в системе нет ни одного пользователя, зарегистрировавшегося с гостевой учетной записью!

Результаты

Сценарий ничего не выводит во время работы, он только восстанавливает состояние домашнего каталога в соответствии с содержимым каталога ..template.

Тема: Внимание: ashley превысил квоту

Вы используете 539.7 Мбайт дискового пространства, тогда как вам выделено 500 Мбайт.

Это означает, что вам следует удалить некоторые ваши файлы, сжать файлы (с помощью

простых и мощных программ сжатия ‘gzip’ или ‘bzip2’) или подать заявку на увеличение

дисковой квоты для вас.

Спасибо за сотрудничество в этом вопросе.

Ваш непосредственный системный администратор.

Вам непонятно, о чем это мы? Это персонажи из замечательной серии иллюстрированных комиксов «Adventures of Tintin» (Приключения Тинтина), созданной бельгийским художником Эрже (Hergé) и вышедшей в середине XX века. Подробности смотрите на сайте: http://www.tintin.com/ (https://ru.wikipedia.org/wiki/Приключения_ТинтинаПримеч. пер.).

Перевод:

СРОЧНОЕ СООБЩЕНИЕ ОТ АДМИНИСТРАТОРА:

Эта учетная запись блокируется, и вы будете выведены из системы

через 10 секунд. Пожалуйста, завершите все свои процессы и

выйдите из системы.

По всем вопросам обращайтесь к своему руководителю или

Джону Доу, начальнику отдела информационных технологий.

Назад: Глава 4. Тонкая настройка Unix
Дальше: Глава 6. Системное администрирование: обслуживание системы