Lo hice y lo entendí

El blog de Vicente Navarro
26 feb

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”):

  1. /etc/profile
  2. ~/.bash_profile
  3. ~/.bash_login
  4. ~/.profile

Y si la shell es interactiva pero no es de login, el bash ejecuta (si no se ejecuta con “–norc” o “–rcfile file”)

  1. /etc/bash.bashrc
  2. ~/.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:

exim4 reconfiguration

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.

Entradas relacionadas

Tema LHYLE09, creado por Vicente Navarro