Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

1. Введение

Этот документ описывает основные тонкости и проблемы, при работе с переменными окружения Unix/Linux, в особенности с переменной PATH (путь). PATH это список директорий, где производится поиск исполняемых программ. Детали относятся к Debian Linux 1.3.

Внимание! Этот документ - бета релиз. Пожалуйста присылайте комментарии и исправления.


2. О Russian Linux Documentation Project

Данный документ был переведен в рамках проекта Russian Linux Documentation Project. Цель данного проекта - перевод документации, посвященной различным аспектам настройки и использования ОС Линукс на русский язык. Более подробно узнать о проекте вы можете на нашем веб сайте http://rldp.linux.ru.net/.

В переводе данного HOWTO участвовали:

Автор английской версии Esa Turtiainen etu@dna.fi


3. Условия распространения

Этот документ представляет собой свободную документацию; вы можете распространять и/или модифицировать его на условиях GNU General Public License, опубликованной Free Software Foundation; как версии 2, так и (на ваше усмотрение) любой более поздней версии.

Данная документация распространяется в надежде, что она будет полезна, но без КАКИХ ЛИБО ГАРАНТИЙ; даже без подразумеваемой гарантии КОММЕРЧЕСКОЙ ЦЕННОСТИ или гарантии ПРИГОДНОСТИ В КОНКРЕТНЫХ СЛУЧАЯХ. Детали смотри в Gnu General Public License.

Вы должны были получить копию GNU General Public License вместе с этой документацией; если нет, пишите Free Software Foundation,Inc., 675 Mass Ave, Cambridge,MA 02139,USA.


4. Общие принципы

Все процессы в Unix имеют "окружение". Это список переменных, который содержит имена и значения, представленные в виде строк, которые могут содержать большинство символов. У всех Unix процессов есть родительский процесс - процесс который создал данный в качестве дочернего. Дочерние процессы наследуют окружение у родительских. Они могут модифицировать окружение, прежде чем передать своим дочерним процессам.

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

В данном howto, я использую термин 'команда', чтобы обозначить исполняемую программу, которая запускается на исполнение с помощью короткого имени, используя механизм path.

В Линукс, даже низкоуровневые вызовы операционной системы для запуска процессов (семейство вызовов exec) просматривают директории указанные в переменной PATH: вы можете использовать механизм path везде, где вы пытаетесь исполнить команду. Даже если переменная PATH не установлена в окружении, по крайней мере директории /bin и /usr/bin, просматриваются в поисках подходящих команд.

В sh вы можете использовать команду export, чтобы установить окружение, в csh вы можете использовать команду setenv. Например:

sh:
PATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:.
csh:
setenv PATH /usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:.

Программы на C могут использовать библиотечный вызов setenv() для изменения окружения. Perl предоставляет доступ к окружению, через ассоциативный массив %ENV, вы можете установить PATH как $ENV{PATH}="/bin";

Команда env это простейший путь запросить текущие переменные окружения. Её можно также использовать для их модификации.

Более подробную информацию о механизме работы окружения, можно найти в страницах руководства 'environ', 'execl', 'setenv', и info файле 'env', а также в документации на различные командные интерпретаторы.

Когда Линукс загружается, первый запускаемый процесс - это init. Это специальный процесс, т.к. он не имеет родительского процесса. В тоже время он является прародителем всех процессов. Окружение Init является окружением всех процессов, если они его не изменят. Большинство изменяют.

Init запускает группу процессов. Файл /etc/inittab описывает запускаемые процессы. Эти процессы работают с окружением, которое напрямую унаследовано от init - обычно это процессы вроде 'getty', программы, которая пишет 'login:' на консоль. Если вы устанавливаете PPP соединения на этом этапе, вы должны помнить, что работаете в окружении init. Отсюда обычно запускается скрипт системной инициализации. В Debian 1.3 скрипт инициализации это /etc/init.d/rc, который в свою очередь запускает другие скрипты инициализации.

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

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


5. Init

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

Содержимое 'init path' прописано в исходном коде программы init:

/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin

Заметьте, что путь init, не включает /usr/local/bin.

Все программы, запущенные из /etc/inittab, работают в окружении init, особенно системные скрипты инициализации в /etc/init.d/ (Debian 1.3).

Все, что запущено из скриптов инициализации системы, по умолчанию имеет окружение init. Например: syslog,kerneld,pppd (если он запускается при загрузке), gpm и, что более важно, lpd и inetd, наследуют окружение init и не изменяют его.

Группа программ запускается при помощи скриптов запуска, но переменная PATH явно устанавливается в скрипте запуска. Например: atd,sendmail,apache и squid.

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


6. Вход в систему

В текстовой консоли, программа getty ждет входа пользователя в систему. Она выводит на консоль 'login:' и другие сообщения. Она работает с окружением init. Когда getty пропускает пользователя в систему, она запускает программу 'login', которая устанавливает пользовательское окружение и запускает shell.

Программа login устанавливает путь, как описано в /usr/include/paths.h. Причем его значение различно для root и обыкновенных пользователей.

для обычных пользователей (_PATH_DEFPATH):
/usr/local/bin:/usr/bin:/bin:.
для root (_PATH_DEFPATH_ROOT):
/sbin:/bin:/usr/sbin:/usr/bin

Переменная path обычных пользователей не содержит директорий sbin. В тоже время она содержит текущую директорию, '.', что считается опасным для пользователя root. Также path для root не содержит /usr/local/bin.

Значение path установленное login, часто переписывается при инициализации shell. В тоже время возможно указывать другие программы в /etc/passwd в качестве командных интерпретаторов пользователя. Например, я использовал следующую строку, чтобы запускать PPP, когда я вхожу под определенным именем пользователя. В данном случае, pppd наследует путь login.

etu-ppp:viYabVlxPwzDl:1000:1000:Esa Turtiainen, PPP:/:/usr/sbin/pppd

7. Командные интерпретаторы

Часто пользовательские процессы являются дочерними процессами командного интерпретатора, указанного в /etc/passwd для данного пользователя. Файлы инициализации командных интерпретаторов обычно модифицируют этот путь.

В login, имя командного интерпретатора предваряется '-', например bash вызывается как '-bash'. Это сигнализирует интерпретатору, что он является 'login' интерпретатором. В этом случае, интерпретатор вызывает файлы начальной инициализации. Иначе выполняется 'облегченная' версия инициализации. Дополнительно, интерпретатор проверяет 'интерактивность' сессии - считываются-ли команды из файла или с поступают интерактивного терминала. Это влияет на процесс инициализации, так, что не интерактивные сессии инициализируются минимально. Bash не вызывает в этом случае никаких файлов инициализации!


7.1. bash

Вызванный как обычный 'login' интерпретатор, bash исполняет общесистемный файл инициализации /etc/profile, где можно установить системное окружение и path для пользователей bash. Когда интерпретатор вызывается в не-интерактивном режиме, этот файл не исполняется. Наиболее важный случай - это rsh, когда удаленная команда исполняется на другой машине. В таком случае /etc/profile не запускается, и path наследуется у демона rsh.

Bash имеет аргументы командной строки -login и -i, которые могут быть использованы для установки интерпретатора в качестве 'login' интерпретатора или интерактивного интерпретатора.

Пользователь может изменить значения указанные в /etc/profile, создав файл ˜/.bash_profile, ˜/.bash_login или ˜/.profile . Заметьте, что только первый из этих файлов исполняется в зависимости от логики инициализации csh. ˜/.bash_login не исполняется для 'login' интерпретаторов и если существует .bash_profile, он не исполняется вообще!

Если bash используется с именем sh, вместо bash, он эмулирует инициализацию оригинального Bourne shell интерпретатора: исполняет только файлы /etc/profile и ˜/.profile и только для 'login' интерпретаторов.


7.2. tcsh

В качестве login интерпретатора tcsh исполняет следующие файлы в указанном порядке:

  • /etc/csh.cshrc

  • /etc/csh.login

  • ˜/.tcshrc

  • ˜/.cshrc (если не найден .tcshrc)

  • ˜/.history

  • ˜/.login

  • ˜/.cshdirs

tcsh может быть скомпилирован так, чтобы исполнять login скрипты перед cshrc скриптами. Остерегайтесь !

Не интерактивные интерпретаторы исполняют только *cshrc скрипты*. login скрипты могут быть использованы для установки path, только 1 раз, при входе в систему.


8. Изменение идентификатора пользователя

8.1. su

Команда su устанавливает новый идентификатор пользователя. Если он не указан, используется root.

Обычно su вызывает интерпретатор с другим идентификатором пользователя. Если указан аргумент '-' (более новый синоним для -l или --login), su вызывает интерпретатор как 'login' интерпретатор. Хотя su не использует программу login для этого, она использует использует другое 'встроенное' значение path, для 'симуляции' login. (термин используемый в исходном коде). Этот путь таков:

для обычных пользователей
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:. 
для пользователя root
/sbin:/bin:/usr/sbin:/usr/bin:/usr/bin/X11:/usr/local/sbin:/usr/local/bin

su также делает множество достаточно тонких изменений в окружение.


8.2. sudo

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

$ sudo env

исполняет команду от имени супер-пользователя (если разрешено конфигурацией)

Команда sudo имеет свой подход к работе с path. Она модифицирует путь поиска так, чтобы текущая директория всегда была последней. При этом переменная окружения PATH не модифицируется. 'sudo env' и 'env' выдадут одинаковое значение для переменной PATH. Sudo также добавляет пару переменных окружения, таких как SUDO_USER.


9. Сетевые сервера

Большинство сетевых серверов не должны вызывать каких либо под-процессов. Из соображений безопасности их путь должен быть минимален.

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


9.1. inetd

Большинство сетевых серверов не имеют собственного процесса, который постоянно ожидает запросов. Эта работа перекладывается на супер-сервер inetd. Inetd слушает все определенные сетевые порты и запускает соответствующий сервер, когда приходит запрос. Его поведение определяется в /etc/inetd.conf.

inetd запускается из скриптов инициализации системы. Он наследует только путь процесса init. Он не модифицирует его и все сервера запущенные из inetd имеют путь init. Пример такого сервера imapd, сервер почтового протокола IMAP.

Другие примеры процессов inetd это: telnetd,rlogind,talkd,ftp,popd, многие http сервера и т.д.

Часто использование inetd усложняется путем использования отдельной tcpd программы, чтобы запустить настоящий сервер. Это программа, которая делает дополнительную проверку безопасности, перед запуском настоящего приложения. Она не влияет на path (не проверено).


9.2. rsh

Демон rsh устанавливает path из _PATH_DEFPATH (/usr/include/paths.h) который соотвествует пути, используемому программой login для обычных пользователей. Root получит такой же путь как и обычный пользователь.

Фактически, rshd исполняет полученную им команду при помощи следующей командной строки:

shell -c командная строка

где интерпретатор не 'login' интерпретатор. Желательно, чтобы все оболочки, указанные в /etc/passwd поддерживали опцию -c, для задания командной строки.


9.3. rlogin

Rlogin вызывает login, чтобы обеспечить настоящую процедуру входа в систему. Если вы вошли в систему при помощи rlogin, вы получите путь login. Большинство других способов попасть в Линукс систему, не используют login. Заметьте разницу в сравнении с rsh.

Конкретно используется следующий синтаксис вызова login:

login -p -h имя_хоста имя_пользователя

-p сохраняет окружение исключая переменные HOME,PATH,SHELL,TERM,MAIL и LOGNAME. -h указывает удаленный хост для входа.


9.4. telnet

Telnet похож на rlogin. Он использует программу login и командную строку, чтобы вызывать её в похожей манере.


9.5. ssh

ssh использует свои собственные настройки для path . Он имеет фиксированный путь, куда добавляет директорию, в которой находится ssh. Часто это означает, что /usr/bin встречается в path дважды.

/usr/local/bin:/usr/bin:/bin:.:/usr/bin

Path не содержит /usr/X11/bin, а интерпретатор не вызывается как 'login' интерпретатор. Поэтому:

ssh remotehost xterm

Никогда не срабатывает и ничто в /etc/profile или /etc/csh.cshrc не сможет изменить этого. Вы всегда должны явно указывать путь /usr/bin/X11/xterm.

ssh ищет переменные окружения вида ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ в файле /etc/enviroment. К сожалению это вызывает проблемы некоторые проблемы с XFree86.


10. XFree86

10.1. XDM

XDM это наиболее часто используемый способ войти на графический терминал. Он немного похож на login, но внутренне совершенно с ним различен.

В директории /etc/X11/xdm содержатся конфигурационные файлы, которые исполняются на различных стадиях входа в систему. Xstartup (и Xstartup_0 специально для экрана 0) содержат команды, которые будут запущены после входа пользователя в систему (команды исполняются от имени root).

Путь устанавливаемый для пользователей находится в /etc/X11/xdm/xdm-config. Он содержит строки:

DisplayManager*userPath: /usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games 
DisplayManager*systemPath: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11

Это будет путь по умолчанию для обычных пользователей и root соотвественно. Очень важно, что /usr/bin/X11 доступен для пользователей X. Если пользователь X заходит на другую машину, чтобы запустить клиентское приложение X, он должен иметь /usr/bin/X11 в его пути, даже если он не заходит прямо с X терминала.

После исполнения Xstartup, XDM запускает /etc/X11/Xsession от имени пользователя. Локальная конфигурация производится из /etc/enviroment, который включается в Xsession если он существует. (Xsession исполняется при помощи /bin/sh, поэтому /etc/environment должен быть sh скриптом). Эта организация конфликтует с ssh, который предполагает, что /etc/environment, это файл содержащий строки вида ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ.


10.2. xterm -ls

По умолчанию путь для всех команд вызванных из меню оконного менеджера, это путь унаследованный от XDM. Чтобы использовать другой путь, его необходимо указать явно. Чтобы запустить эмулятор терминала с "нормальным" значением path, необходимо использовать специальные опции. Для xterm нужно использовать опцию -ls (login shell), чтобы вызвать 'login' интерпретатор с путем, указанным в файлах инициализации интерпретатора.


10.3. Меню и кнопки оконных менеджеров

Оконный менеджер наследует окружение от XDM. Все программы запущенные оконным менеджером наследуют его окружение.

Окружение пользовательского интерпретатора не влияет на программы которые запускаются при помощи меню и кнопок оконного менеджера. Например, если программа запущена из 'xterm -ls', она имеет окружение 'login' интерпретатора, но если она запущена из меню, она имеет окружение оконного менеджера.


11. Отложенные команды cron и at

11.1. cron

Cron это команда которая периодически исполняет команды, как указано в /etc/crontab и определенными пользователем аналогах. В Debian 1.3 есть стандартный механизм исполнения команд в /etc/cron.daily, /etc/cron.weekly и /etc/cron.monthly.

Cron запускается из инициализационных скриптов, но изменяет свой PATH на довольно странный:

/usr/bin:/binn:/sbin:/bin:/usr/sbin:/usr/bin

ПОХОЖЕ ЧТО ЭТО БАГ В CRON. Это путь унаследованный от init, где /usr/bin:/bin приписаны в начале без завершающего 0! Этот баг существует не во всех системах.

В crontab скрипте возможно указывать PATH. В Debian 1.3 существует следующая стандартная строка в начале /etc/crontab:

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

Из за этого, PATH crond никогда не используется в пользовательских программах. Все скрипты в директориях /etc/cron.* получают этот путь по умолчанию. Этот путь используется даже если программа исполняется не от имени root.


11.2. at

at это команда, которая может быть использована для однократного запуска программы, в указанное время.

atd исполняется с путем init. В тоже время программы пользователя всегда исполняются в окружении пользователя используя команду sh. Интерпретатор модифицирует путь в обычной манере. См. главу о bash.


12. Некоторые примеры

12.1. magicfilter

magicfilter это обычный инструмент манипуляции файлами для принтера. Он анализирует тип печатаемого файла и вызывает соответствующий скрипт, чтобы сделать необходимые преобразования перед печатью. Эти скрипты вызываются из lpd, который запускается из /etc/init.d/lpd, который в свою очередь запускается init. Поэтому путь соотвествует пути init. Он не содержит /usr/bin/X11!

Вы можете захотеть добавить печать PDF файлов в magicfilter. Это возможно сделать при помощи /usr/bin/X11/xpdf. Вы должны помнить, что необходимо указать полный путь к файлу, т.к. иначе magicfilter его не найдет. Большинство программ используемых в magicfilter не требуют указания полного пути, т.к. находятся в /bin или /usr/bin.


12.2. Печать из X приложений

Вы можете установить переменную окружения PRINTER, чтобы показать какой принтер вы используете. В тоже время вы можете заметить что в некоторых случаях это значение будет теряться в X приложениях.

Вы должны помнить, что если X сессия запускается из XDM, оконный менеджер никогда не исполняет ваши инициализационные скрипты для интерпретатора. Все X приложения, которые вы запустили из xterm, будут иметь вашу переменную окружения PRINTER. Хотя те же приложения, запущенные из меню или с помощью кнопок, не будут содержать переменную PRINTER в своём окружении.

В некоторых случаях это может быть унаследовано на более низкий уровень: например Netscape helper может иметь или не иметь этой переменной в окружении.


13. Соображения безопасности

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

В прошлом очень частой ошибкой было установить '.' в пути пользователя root. Злобный крекер создавал программу 'ls' в его домашней директории. Если root делал:

# cd ~hacker
# ls

то этим самым он исполнят крекерскую версию ls.

Неявно, эта брешь безопасности действует и для всех программ, которые исполняются от имени root. Любой важный демон никогда не должен исполнять чего либо, что может модифицироваться другим пользователем. На некоторых системах /usr/local/bin может содержать программы, не подвергающиеся жесткому контролю -- эта директория просто удалена из пути root. В тоже время, если известно, что некий демон исполняет команду 'foo' и использует путь '/usr/local/bin:...', возможно обмануть его заставив исполнить /'usr/local/bin/foo' вместо '/bin/foo'. Практически любой кто имеет доступ на запись к '/usr/local/bin' сможет взломать систему.

Очень важно обдумать в каком порядке располагаются директории в path. Если /usr/local/bin находиться перед /bin, это брешь в безопасности, если после, то перекрыть команду /bin/foo при помощи модифицированной версии в /usr/local/bin/foo станет невозможно.

В Линукс вы должны помнить, что обработка path происходит на уровне системного вызова. Везде где задан путь поиска исполнимого файла, вы можете использовать короткое имя, поиск которого будет произведен по крайней мере в /bin и /usr/bin - и, вероятно, во многих других местах.


14. Как выявлять проблемы?

Простейшая команда для просмотра окружения это /usr/bin/env.

Возможно использовать директорию /proc для поиска пути любой программы. Во первых вы должны знать номер процесса - используйте команду ps чтобы получить его. Например, если xterm это процесс номер 1088, вы можете просмотреть его окружение при помощи команды.

# more /proc/1088/environ

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

Для отладки Netscape, вы можете создать скрипт /tmp/test:

$ cat > /tmp/test
#!/bin/sh
/usr/bin/env > /tmp/env
^d
$ chmod +x /tmp/test

Затем настройте некоторое вспомогательное приложение, например RealAudio, audio/x-pn-realaudio на вызов программы "/tmp/test". Когда вы попробуете просмотреть некую RealAudio ссылку (что-нибудь с http://www.realaudion.com/showcase), Netscape вызовет нашу программу, которая сохранит окружение в /tmp/env.


15. Некоторые стратегии получения одинакового пути для всех пользователей

Наиболее важные настройки можно установить в глобальных файлах инициализации интерпретатора. Для 'login' интерпретаторов: /etc/csh.login для tcsh и /etc/profile для bash.

Исключения, которые не получат правильный путь, указанный в этих файлах: команды rsh, ssh, пункты меню в оконных менеджерах для X которые не запускают явно 'login' интерпретатор, команды вызванные из inittab, работы cron, процессы запущенные демонами, например magic filters запускаемые lprd, WWW CGI скрипты и так далее.

Если путь установлен в /etc/csh.cshrc, путь будет корректным даже если программа исполняется на другой машине с аккаунтом, использующим tcsh/csh. В тоже время невозможно установить путь, если аккаунт использует bash/sh.

Возможно скомбинировать установки пути в 1 файле, например в файле /etc/enviroment-common:

${EXPORT}PATH${EQ}/bin:/usr/bin:/sbin:/usr/sbin:/usr/bin/X11:/usr/local/bin:/usr/games:.

Это можно вызвать из /etc/csh.login (для tcsh и csh)

set EQ=" " set EXPORT="setenv " source /etc/environment-common

И из /etc/profile (для bash, не работает для обычного sh)

EQ='=' EXPORT="export " . /etc/environment-common

И из /etc/enviroment (для XDM)

EQ="=" EXPORT="export " . /etc/environment-common

Данная стратегия работает в большинстве случаев, но ssh будет жаловаться на строки в /etc/enviroment (и определенные переменные окружения EQ и EXPORT). rsh команды исполненные при помощи bash также не получат этого пути.


16. Благодарности

Одна из причин по которой я начал писать этот документ, это проблемы возникшие у Ari Mujunen. Juha Takala дал мне некоторые ценные комментарии.