Path COMO

Esa Turtiainen etu@dna.fi, Traducido por Juan José López Mellado laveneno@hotmail.com

v0.4, 15 Nov 1997 (trad: 5 Ago 1998)

1. Introducción

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.

2. Copyright

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.

3. Generalidades

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.

4. Init

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.

5. Ingreso al sistema

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

6. Shells

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 !

6.1 bash

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.

6.2 tcsh

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.

7. Cambiando el ID del usuario

7.1 su

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.

7.2 sudo

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.

8. Servidores de red

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.

8.1 inetd

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).

8.2 rsh

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.

8.3 rlogin

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.

8.4 telnet

Telnet es similar a rlogin. Usa el programa login y la línea de comandos para invocarlo de manera similar.

8.5 ssh

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.

9. XFree86

9.1 XDM

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.

9.2 xterm -ls

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).

9.3 El menú y los botones de los X Window Manager.

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.

10. Comandos retardados por cron y at

10.1 cron

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.

10.2 at

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.

11. Algunos ejemplos

11.1 magicfilter

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.

11.2 Imprimiendo desde aplicaciones X

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.

12. Aspectos de seguridad

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.

13. ¿Cómo depurar los problemas?

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.

14. Algunas estrategias para que todos los usuarios tengan elmismo path

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.

15. Agradecimientos

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.