etu@dna.fi
, Traducido por Juan José López
Mellado laveneno@hotmail.com
Este documento describe los trucos y problemas más comunes con las variables de entorno de Unix / Linux, especialmente con la variable PATH. PATH es una lista de directorios donde son buscados los comandos. Los detalles dados se centran sobre la distribución Debian Linux 1.3.
Nótese que este documento es una versión beta. Por favor envíe cualquier comentario o corrección al autor.
Esta documentación es libre; puede ser distribuida y/o modificada bajo los términos de la GNU General Public License tal y como ha sido publicado por la Free Software Foundation; tanto la versión 2 de la Licencia, o (a su elección) cualquier versión posterior.
Esta documentación de distribuye con la esperanza de que sea útil, pero SIN NINGUNA GARANTÍA; también sin la garantía implícita de su MERCANTILIZACIÓN o ADECUACIÓN PARA UN USO PARTICULAR. Vea la GNU General Public License para más detalles.
Usted debe haber recibido una copia de la GNU General Public License acompañando a esa documentación; si no es así, escriba a la Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Todos los procesos de Unix contienen un ``entorno''. Este consiste en una lista de variables que contienen un nombre y un valor, ambos son cadenas de caracteres que pueden contener varios caracteres. Todos los procesos Unix tienen a uno padre que creó a este proceso como su hijo. Los procesos hijos heredan el entorno de su padre. Pueden hacer algunas modificaciones sobre el entorno antes de pasarlo a su vez a sus procesos hijos.
Una variable de entorno muy importante es PATH, una lista de directorios separados por dos puntos (`:'). Al buscar un comando se busca en todos estos directorios. Si intenta invocar el comando `pepe', se busca en todos los directorios del PATH (en orden) buscando un fichero ejecutable llamado `pepe' (uno con el bit x activado). Si se encuentra este fichero, se ejecuta.
En este howto, uso el término `comando' para referirme al programa ejecutable que será llamado con nombres cortos, usando el mecanismo del path.
En Linux, hasta las llamadas al sistema de bajo nivel encargadas de arrancar procesos (la familia exec) buscan a través de los directorios de la variable PATH: puede usar el mecanismo de path en cualquier lugar cuando intente ejecutar un comando. Si la llamada a sistema exec recibe un nombre de fichero que no comience por `/', evalúa la variable de entorno PATH. Incluso si no existiera esta variable, por lo menos se busca en los directorios /bin y /usr/bin.
En sh se usa el comando export
para declarar en el entorno, en
csh se usa el comando setenv
. Por ejemplo:
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:.
Los programas en C pueden usar la llamada de librería setenv()
para cambiar el entorno. Perl guarda el entorno en un array asociado
con el nombre %ENV, puede modificar PATH con
$ENV{PATH}="/bin"
.
El comando env
es la manera básica de preguntar por el entorno
actual. Y puede ser usado también para modificarlo.
Más información sobre el mecanismo básico del entorno puede
encontrarse en las páginas de manual `environ
', `setenv
' y
`execl
', en el fichero info `env
' y en la documentación de
los shells.
Cuando Linux arranca, el primer proceso normal que se ejecuta es el proceso init. Es un proceso especial porque no tiene padre. Asimismo es el ancestro de todos los otros procesos. El entorno de init será el entorno de todos los demás procesos si no lo modifican explícitamente. Muchos procesos lo modifican.
Init arranca un grupo de procesos. El fichero /etc/inittab
indica los procesos que el sistema debe arrancar. Estos procesos
trabajan con el entorno que directamente heredaron de init - típicamente
son procesos como `getty
', el programa que escribe `login:' en la
consola. Si arranca conexiones PPP en este punto, debe recordar que
está ejecutando en el entorno de init. La inicialización del sistema
es a menudo un fichero de comandos que es ejecutado en este punto.
En Debian 1.3 este fichero es /etc/init.d/rc
y este llama, a
su vez, al resto de los ficheros de comandos de inicio.
El sistema contiene varios servidores ejecutando (daemons) que puede que usen el entorno por defecto. Muchos servidores son arrancados desde los ficheros de inicialización y por tanto tienen el entorno de init.
Cuando un usuario ingresa en el sistema, el entorno es afectado por las configuraciones que han sido compiladas en los programas, por los ficheros de comandos de inicio del sistema y por los ficheros de comandos de inicio del usuario. Esta es bastante complicado y la situación actual no es completamente satisfactoria. Es totalmente diferente si el usuario ingresa a través de la consola de texto, a través de XDM o a través de la red.
Init es el proceso padre de todos los restantes procesos del sistema. Los otros procesos heredan el entorno de este proceso y el path es el path de init en el caso excepcional de que no se modifique.
El `path de init' está fijado en el código fuente del programa init y es el siguiente:
/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin
Nótese que el path de init no puede contener /usr/local/bin
.
Todos los programas que son arrancados desde /etc/inittab
trabajan en el entorno init, especialmente los ficheros de comandos
de inicio del sistema en /etc/init.d
(Debian 1.3).
Todo lo que es arrancado desde los ficheros de comandos de inicio del sistema tienen como entorno el de defecto. Por ejemplo, syslogd, kerneld, pppd (cuando es arrancado al inicio), gpm y algunos más importantes como lpd e inetd tienen todos el entorno de init y no lo cambian.
Un grupo de programas son arrancados desde los ficheros de comandos de inicio del sistema pero la variable PATH es explícitamente modificada en dichos ficheros de comandos. Por ejemplo: atd, sendmail, apache y squid.
Hay otros programas que son arrancados desde los ficheros de comandos de inicio pero que cambian su path completamente. Un ejemplo es cron.
En consolas de texto hay un programa getty esperando al ingreso de
un usuario (login). Escribe el mensaje `login:
' y otros.
Está trabajando en el entorno de init. Cuando getty tiene a un usuario
para ingresarlo en el sistema, invoca al programa `login
'. Este
programa actualiza el entorno del usuario e invoca al shell.
El programa login
pone en el path los valores definidos en
/usr/include/paths.h
. Este `path de ingreso' es diferente para
el usuario root y los otros usuarios.
para usuarios normales (_PATH_DEFPATH):
/usr/local/bin:/usr/bin:/bin:.
para root (_PATH_DEFPATH_ROOT):
/sbin:/bin:/usr/sbin:/usr/bin
El path de los usuarios normales no contiene ningún directorio sbin
.
No obstante, contiene el directorio actual, `.', el cual se considera
peligroso para el usuario root. Tampoco está disponible el directorio
/usr/local/bin
para el usuario root.
El path de ingreso es normalmente sobreescrito por la inicialización
del shell. De todos modos, es posible usar otros programas en
/etc/passwd
como shells de usuario. Por ejemplo, yo uso la
siguiente línea para arrancar PPP cuando ingreso con un nombre de
usuario especial. En este caso, pppd tiene exactamente el path
de ingreso.
etu-ppp:viYabVlxPwzDl:1000:1000:Esa Turtiainen, PPP:/:/usr/sbin/pppd
Muy a menudo los procesos de usuario son procesos hijos del shell que
se menciona en /etc/passwd
para ese usuario. Los ficheros de
inicialización de los shells normalmente modifican el path.
En el ingreso, el nombre del shell está precedido por un `-', por ejemplo bash es llamado como `-bash'. Esto indica al shell que es un shell de ingreso. En este caso, el shell ejecuta los ficheros de inicialización de `ingreso'. De cualquier otra manera solo se realizan algunas inicializaciones muy ligeras. Adicionalmente, el shell comprueba si es interactivo - si los comandos vienen a partir de un fichero o de un tty interactivo. Esto modifica la inicialización del shell de manera que un shell no interactivo y normal (no de ingreso) es inicializado de forma muy ligera - ¡bash no ejecuta ningún fichero de inicialización en este caso !
Cuando es un shell de ingreso normal, bash lee el fichero
/etc/profile
, donde el entorno del sistema y el path son
actualizados para todos los usuarios de bash. De todas maneras, no
es leído cuando el sistema interpreta el shell como no interactivo.
El caso más importante se produce con rsh, donde un comando remoto
es ejecutado en una máquina vecina. El fichero /etc/profile
no es leído y el path es heredado del daemon rsh.
bash recibe los argumentos -login
y -i
de la línea de comandos
que pueden ser usados para establecer el shell como un shell de ingreso
o un shell interactivo, respectivamente.
El usuario puede sobreescribir los valores activados por
/etc/profile
creando un fichero ~/.bash_profile
,
~/.bash_login
o ~/.profile
. Nótese que
solo el primero de estos es ejecutado difiriendo de la lógica de
inicialización de csh. ~/.bash_login
no es ejecutado
especialmente en shells de ingreso y si existe .bash_profile
, no
se ejecuta nunca.
Si bash es usado con el nombre sh en vez de con el nombre bash, emula
la inicialización original del Bourne Shell: lee solo los ficheros
/etc/profile
y ~/.profile
y solo para shells
de ingreso.
Como un shell de ingreso ejecuta los siguientes ficheros en este orden:
tcsh puede ser compilado para ejecutar otros ficheros de comandos antes de estos. ¡Esté atento!
Los shells no interactivos ejecutan solo los ficheros *cshrc
.
Los ficheros *login
pueden ser usados para modificar el path
solo una vez durante el ingreso.
El comando su
activa un nuevo identificador de usuario para
trabajar con el. Si no se indica el identificador de usuario, se usa
root
.
Normalmente `su
' invoca a un subshell con un identificador de
usuario diferente. Con el argumento `-' (sinónimos más recientes
son `-l' o `--login') `su
' invoca al shell como un shell de ingreso.
De todas maneras, no usa el programa login
para hacer el ingreso
sino que usa otro path compilado internamente para la `simulación'
de login (el que usa term
). Es el siguiente:
para usuarios normales
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:.
para el usuario root
/sbin:/bin:/usr/sbin:/usr/bin:/usr/bin/X11:/usr/local/sbin:/usr/local/bin
su
hace otros sutiles cambios en el entorno.
Hay un grupo de comandos que hacen uso de los comandos del superusuario
de forma más segura. Permiten mejor ingreso, restricciones basadas en
usuario y uso de passwords individuales. El más usado es sudo
.
$ sudo env
ejecuta el comando env
como superusuario (si está configurado para
permitirlo).
El comando sudo
tiene otra vez una manera diferente de manejar el
path. Modifica el path de búsqueda de tal manera que el directorio
actual es siempre el último. De todas maneras, no modifica la variable
de entorno PATH. `sudo env' y `env' dan el mismo valor para la variable
PATH. Sudo añade algunas variables adicionales como SUDO_USER.
Muchos servidores de red no pueden invocar subprocesos de ningún tipo. Por razones de seguridad, su path debe ser mínimo.
Una excepción importante son todos los servicios que permiten el ingreso
al sistema desde la red. Esta sección describe cual es el entorno en
esos casos. Si el comando es ejecutado en la máquina remota con rsh
tiene un path diferente del que tendría si se ejecutara con ssh
.
De forma similar, ingresar con rlogin
, telnet
o ssh
es
diferente.
Muchos servidores de red no tienen sus propios procesos esperando todo
el tiempo las peticiones de los clientes. Este trabajo se delega a un
super servidor de Internet llamado inetd
. Inetd escucha en todos
los puertos de red definidos y ejecuta el servidor apropiado cuando hay
una nueva petición. Estas características están definidas en
/etc/inetd.conf
.
inetd es arrancado desde los ficheros de comandos de inicio del sistema. Hereda el path del proceso init. No lo modifica y todos los servidores que inetd arranca tienen el path de init. Un ejemplo de estos servidores es imapd, el servidor del protocolo de oficina de correos IMAP.
Otros ejemplos de procesos inetd son telnetd
, rlogind
,
talkd
, ftp
, popd
, muchos de los servidores de http
y otros.
A veces es complicado el uso de inetd usando un programa tcpd por separado para arrancar el verdadero servidor. Es un programa que hace chequeos de seguridad adicionales antes de ejecutar la aplicación real. No afecta al path (sin verificar).
El daemon rsh
modifica el path a partir de _PATH_DEFPATH (/usr/include/paths.h
) el cual es el mismo path que el programa
de ingreso usa para los usuarios normales. Root obtendría el mismo
path que un usuario normal.
Actualmente rshd
ejecuta el comando que obtiene de la línea de
comandos:
shell -c command-line
y el shell no es un shell de ingreso. Es deseable que todos los shells
mencionados en /etc/passwd
soporten la opción `-c' dada por
línea de comandos.
Rlogin
invoca a login
para realizar el procedimiento de ingreso
real. Si ingresa mediante rlogin, obtendrá el mismo path que el que
tendría de haberlo hecho con login. Las otras maneras de ingresar en
una máquina Linux no usan login. Nótese las diferencias con rsh.
El comando de ingreso que actualmente se usa es
login -p -h nombre-maquina nombre-usuario
-p preserva el entorno excepto las variables HOME, PATH, SHELL, TERM, MAIL y LOGNAME. -h indica el nombre para ingresar en la máquina remota.
Telnet
es similar a rlogin
. Usa el programa login y la línea
de comandos para invocarlo de manera similar.
ssh
tiene un path definido a su manera. Tiene un path fijo al cual
añade el directorio donde ssh se encuentra. Normalmente esto significa
que /usr/bin
aparece dos veces en el path:
/usr/local/bin:/usr/bin:/bin:.:/usr/bin
El path no contiene /usr/X11/bin
y el shell invocado desde ssh
no es un shell de ingreso. Por eso
ssh remotehost xterm
nunca funciona y nada en /etc/profile
o /etc/csh.cshrc
puede cambiarlo. Siempre debe usar el path explícito
/usr/bin/X11/xterm
.
ssh busca variables de entorno de la forma VAR=VALOR
en el fichero
/etc/environment
. Desafortunadamente esto causa algunos
problemas con XFree86.
XDM es el sistema más habitual de ingresar en un terminal gráfico. Se parece a login pero internamente funciona totalmente diferente.
En el directorio /etc/X11/xdm
hay ficheros de configuración
que son ejecutados durante las diferentes fases del ingreso.
Xstartup
(y Xstartup_0
especialmente para la pantalla 0)
contienen comandos que deben ser ejecutados después de que el
usuario haya ingresado (los comandos son ejecutados como usuario root).
El path que es definido para los usuarios está en
/etc/X11/xdm/xdm-config
. Hay líneas:
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
Que serán el path por defecto para los usuarios normales y para root
respectivamente. Es muy importante que /usr/bin/X11
se encuentre
disponible para los usuarios de X. Si un usuario de X ingresa en otra
máquina para ejecutar una aplicación cliente de X, debe tener
/usr/bin/X11
en su path aunque no parezca provenir
directamente de un terminal X.
Después de ejecutar Xstartup, XDM ejecuta /etc/X11/Xsession
que es ejecutado como el usuario final. La configuración local se
supone que debe realizarse en /etc/environment
el cual
es incorporado desde Xsession si está disponible (Xsession es
ejecutado con /bin/sh
y por tanto /etc/environment
debe ser un fichero de comandos de sh). Esto entra en contradicción
con ssh el cual supone que /etc/environment
es un fichero
que solo contiene líneas de la forma VAR=VALOR
.
Por defecto el path de todos los comandos invocados desde el menú de
los gestores de ventanas de X es el path heredado desde XDM. Para
usar algo diferente debe explicitarse. Para arrancar el emulador de
terminal con un path que sea el ``normal'' debe usarse una opción
especial. En xterm
la opción que debe usarse para obtener un
shell de ingreso con un path igual al especificado en los ficheros
de comandos de inicio es `-ls' (login shell).
Los Window manager (gestores de ventanas) heredan el entorno de XDM. Todos los programas arrancados por el gestor de ventanas heredan el entorno de este.
El entorno del shell de usuario no afecta a los programas que son
arrancados desde un menú o botón del gestor de ventanas. Por ejemplo,
si un programa es ejecutado desde un `xterm -ls
' , tiene el entorno
por defecto de un shell de ingreso, pero si es ejecutado desde el menú,
solo tiene el entorno del gestor de ventanas.
cron
y at
Cron es un comando que ejecuta comandos periódicamente tal y como se
especifica en /etc/crontab
y los crontabs definidos por el
usuario. En Debian 1.3 hay un mecanismo estándar para ejecutar comandos
en /etc/cron.daily
, /etc/cron.weekly
y
/etc/cron.monthly
.
Cron es arrancado desde los ficheros de comandos de inicio pero cambia su PATH a uno un tanto extraño:
/usr/bin:/binn:/sbin:/bin:/usr/sbin:/usr/bin
ES PROBABLEMENTE UN FALLO DE CRON. ¡Este es el path de init en donde
se ha escrito /usr/bin:/bin
encima sin el 0 final! Este fallo
no existe en todos los sistemas.
En crontab
pueden haber definiciones de PATH. En Debian 1.3 hay
la siguiente línea por defecto al principio de /etc/crontab
:
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
Por este motivo, el PATH del programa crond no es usado nunca en los
programas de usuario. Todos los ficheros de comandos en los directorios
/etc/cron.*
obtienen su path por defecto. Este path es usado
aunque el programa no sea ejecutado como root.
at
es un comando que puede ser usado para ejecutar un programa una
sola vez a una determinada hora.
atd
es ejecutado con el path de init. De todas maneras, los programas
de usuario son ejecutados en el entorno del usuario usando un comando
sh
. Por lo tanto el shell sobreescribe sus valores. Vea el capítulo
sobre bash
.
magicfilter
es una utilidad muy común para manipular ficheros
para imprimir. Analiza el tipo de fichero que hay que imprimir e
invoca un fichero de comandos que lo filtra para poderlo imprimir.
Estos ficheros de comandos son invocados por lpd
que es
arrancado desde /etc/init.d/lpd
que es arrancado por
init. ¡Por tanto no contiene el directorio /usr/X11/bin
!
Es posible que deseara añadir el soporte para ficheros PDF en
magicfilter. Debe recordar el insertar el path completo al nombre
de fichero porque de cualquier otra manera magicfilter no lo
encontraría. Muchos de los programas usados por magicfilter no
necesitan un path completo, porque están en /bin
o
/usr/bin
.
La variable de entorno PRINTER es usada para indicar qué impresora está usando. De todas maneras, debe tener en cuenta que en algunos casos las aplicaciones X no encuentran esta variable.
Debe recordar que si la sesión X fue inicializada mediante XDM, el
gestor de ventanas no ha evaluado nunca sus ficheros de inicio.
Todas las aplicaciones X que haya arrancado desde xterm
tienen
su variable PRINTER. Pero si la misma aplicación es ejecutada
desde un menú o un botón del gestor de ventanas, no contiene la
variable PRINTER.
En algunos casos puede ser heredada desde una capa más baja: por ejemplo una aplicación de ayuda de Netscape puede tener o no su definición de PRINTER.
El path es muchas veces un gran problema de seguridad. Una de las formas más típicas de `hackear' un sistema es usando algún fallo en el path. Es fácil hacer actuar a un caballo de Troya si el hacker consigue que el root u otro usuario ejecute su versión de los comandos.
Un fallo muy común en el pasado (¿?) era mantener el directorio `.' en el path del usuario root. Los hackers hacían un programa `ls' en su directorio por defecto. Si el root hace
# cd ~hacker
# ls
estará ejecutando el comando ls del hacker.
Indirectamente, esto mismo se aplica a todos los programas que son
ejecutados como root. Ninguno de los daemons importantes deben nunca
ejecutar nada que otro usuario haya podido escribir. En algunos
sistemas, se permite tener programas en /etc/local/bin
con
una seguridad menos estricta - nótese que no existe en el path del
usuario root. De todas maneras, si se conoce que algún daemon ejecuta
`pepe' usando el path `/usr/local/bin:...
', sería posible
hacer que el daemon ejecutara `/usr/local/bin/pepe
' en vez
de `/bin/pepe
'. Entonces posiblemente alguien que pudiera
escribir en `/usr/local/bin/pepe
' tendría la posibilidad de
entrar en el sistema.
Es muy importante considerar en qué orden se encuentran los directorios
en el path. Si /usr/local/bin
está antes de /bin
, es
un riesgo de seguridad - si está después, no es posible sobreescribir
el comando `/bin/pepe
' con la nueva versión en
`/usr/local/bin/pepe
'.
En Linux debe recordarse que la evaluación del path se hace al nivel de
las llamadas al sistema operativo. En cualquier punto donde el path
de un fichero ejecutable sea dado puede darse un nombre corto que es
buscado como mínimo en /bin
y /usr/bin
- posiblemente
también en muchos otros sitios.
El comando básico para leer el entorno es /usr/bin/env
.
Es posible usar el directorio /proc
para encontrar el path de
un programa. Primero debe conocer el número de proceso - use el comando
ps
para obtenerlo. Por ejemplo, si xterm
es el proceso número
1088, puede ver su entorno con el comando
# more /proc/1088/environ
Esto no funciona con procesos daemon como xdm. Para acceder al entorno de los procesos de sistema o procesos de otros usuarios, es necesario acceder como root.
Para depurar a Netscape, puede crear un fichero de comandos
/tmp/test
:
$ cat > /tmp/test
#!/bin/sh
/usr/bin/env > /tmp/env
^d
$ chmod +x /tmp/test
Entonces modifique alguna de las aplicaciones de ayuda, por ejemplo
RealAudio, audio/x-pn-realaudio
para que llame al programa
/tmp/test
. Cuando intente ver algún enlace de RealAudio
(por ejemplo de http://www.realaudio.com/showcase
),
Netscape llama a nuestro programa que guarda el entorno en
/tmp/env
.
Es posible poner las configuraciones más importantes en los ficheros
globales de inicio de los shells para shells de ingreso:
/etc/csh.login
para tsch y /etc/profile
para bash.
Las excepciones que no obtienen el correcto path de estos ficheros
son rsh
, ssh
, elementos de un menú de algún gestor de
ventanas X que no haya sido cargado explícitamente desde un shell
de ingreso, comandos invocados desde inittab
, trabajos de
cron
, daemons como filtros mágicos cargados desde lprd
,
scripts CGI de WWW, y otros.
Si el path se define en /etc/csh.cshrc
, el path es correcto
aunque rsh o ssh ejecuten un comando en una máquina remota en cuya
cuenta de usuario se esté usando tcsh/csh. Pero no es posible definir
el path si la cuenta usa bash/sh.
Es posible combinar la definición del path en un solo fichero, por
ejemplo en /etc/environment-common
. En él escribimos:
${EXPORT}PATH${EQ}/bin:/usr/bin:/sbin:/usr/sbin:/usr/bin/X11:/usr/local/bin:/usr/games:.
Este fichero puede ser usado desde /etc/csh.login
(para
tcsh y csh).
set EQ=" " set EXPORT="setenv " source /etc/environment-common
Y desde /etc/profile
(para bash, no funciona para sh)
EQ='=' EXPORT="export " . /etc/environment-common
Y desde /etc/environment
(para XDM)
EQ="=" EXPORT="export " . /etc/environment-common
Esta estrategia funciona en la mayoría de casos, pero ssh se
quejará de las líneas de /etc/environment
(y las variables
de entorno EQ y EXPORT definidas). Y todavía, los comandos ejecutados
desde rsh con bash no tendrán este path.
Una de las razones para comenzar a escribir este documento fue la gran frustración de Ari Mujunen. Juha Takala me dio algunos comentarios muy valiosos.