El PATH del cron. Avisos de mail. Sesiones interactivas y de login de Bash.
En El Efecto Barrapunto comentaba que el siguiente script para rotar el access.log del apache no me funcionaba en el crontab y sí desde la línea de comandos:
#/bin/bash cd /var/log/apache2 BAKFILE=access.log.`date +%g%m%d%H%M` mv access.log $BAKFILE apache2ctl graceful sleep 600 gzip $BAKFILE
Aparte de que el logrotate está mucho más indicado para este propósito, el misterio se ha desvelado gracias a un amable lector de barrapunto. Resulta que el apache2ctl está en /usr/sbin, no en /usr/bin ni en /bin, que son los únicos directorios que vienen en el PATH del cron por defecto ya que ni el /etc/profile ni el .bashrc ni similares se ejecutan en ningún momento antes del comando.
Esto es porque el bash que ejecuta el cron es “no interactivo” y “no login” y según leemos en la parte de INVOCATION de la página de manual del bash:
When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the file name.
Es también interesante saber, leyendo esa misma sección, que si la shell es interactiva y de login, el bash ejecuta, en este orden (si no se ejecuta con “–noprofile”):
- /etc/profile
- ~/.bash_profile
- ~/.bash_login
- ~/.profile
Y si la shell es interactiva pero no es de login, el bash ejecuta (si no se ejecuta con “–norc” o “–rcfile file”)
- /etc/bash.bashrc
- ~/.bashrc
aunque en cualquier caso, típicamente el .profile es algo así:
# ~/.profile: executed by Bourne-compatible login shells. if [ -f ~/.bashrc ]; then . ~/.bashrc fi
Pero volviendo al asunto inicial, además de que el script no funcionara bien por el PATH, resulta que yo no me estaba enterando del fallo a pesar de que el cron se supone que te envía un mail con la salida del comando.
Y me los estaba mandando, en efecto, pero no al usuario root, sino a otro usuario, ya que cuando en Debian se configura el paquete exim4, entre otras cosas te pregunta a qué usuario se le debe de entregar el correo de root:
Algo que se refleja en el /etc/aliases en una línea como “root: usuario”. Además, si borráramos esa línea, igualmente el correo de root no se repartiría a root sino que se quedaría en /var/mail/mail de acuerdo con las reglas del exim:
hostname:/etc/exim4/conf.d/router # cat 900_exim4-config_local_user ### router/900_exim4-config_local_user ################################# # This router matches local user mailboxes. If the router fails, the error # message is "Unknown user". local_user: debug_print = "R: local_user for $local_part@$domain" driver = accept domains = +local_domains check_local_user local_parts = ! root transport = LOCAL_DELIVERY cannot_route_message = Unknown user hostname:/etc/exim4/conf.d/router # cat mmm_mail4root ### router/mmm_mail4root ################################# # deliver mail addressed to root to /var/mail/mail as user mail:mail # if it was not redirected in /etc/aliases or by other means # Exim cannot deliver as root since 4.24 (FIXED_NEVER_USERS) mail4root: debug_print = "R: mail4root for $local_part@$domain" driver = redirect domains = +local_domains data = /var/mail/mail file_transport = address_file local_parts = root user = mail group = mail
Vemos que el 900_exim4-config_local_user dice que no hay local delivery para root (local_parts = ! root) y el mmm_mail4root especifica que el correo de root (local_parts = root) se mande a /var/mail/mail.
Pues bien, el correo con la salida del cron se había mandado al buzón del usuario que aparecía en el /etc/alias:
rotar_access.sh: line 5: apache2ctl: command not found
pero yo entraba con ese usuario y no había visto el típico You have mail.
Tras algunas pruebas, he podido comprobar que si te logeas por telnet o en la consola sí te sale dicho mensaje al entrar, ¡pero no cuando te logeas por ssh! ¿Alguien sabe por qué es esto?
Además, para que los avisos de correos nuevos te vayan saliendo durante el uso normal del bash, las variables de entorno MAIL, MAILPATH y MAILCHECK (documentadas en el man de bash) deberían estar bien configuradas. En mi caso, me ha resultado muy útil saber que si pongo:
export MAILPATH='/var/mail/usuario?usuario tiene correo nuevo'
en el ~/.bashrc de root, cuando me llega correo nuevo para root que va a parar al buzón de usuario, me saldrá el mensajito “usuario tiene correo nuevo” en el terminal y sólo haciendo un “mail -u usuario” podré ver esos nuevos correos, que entre otras cosas, contienen mensajes útiles del cron.