home page::doc::syslog
Mimante.net

Gosh! Gulp! Log!

Uno dei software meno conosciuti e più sottovalutati dai nuovi utenti Linux è certamente syslog, il preziosissimo sistema di log che ogni distribuzione decentemente completa fornisce. Grazie a questo silenzioso aiutante un utente esperto è in grado di diagnosticare problemi di ogni genere del kernel1 e dei principali daemon2 e sottosistemi (come mail, news e web server).

In questo articolo cominceremo analizzando a grandi linee il sistema di log, concentrandoci prima di tutto sulle parti più visibili al normale utente, in particolare sulla configurazione e personalizzazione; in seguito potremo addentrarci maggiormente sul funzionamento di questo sistema piuttosto complesso, per concludere con accenni alla programmazione.

I file di log

Spostatevi nella directory /var/log ed eseguite il comando ls; quelli che state vedendo sono i file di log del vostro sistema: qui sono conservate tutte le informazioni sul normale funzionamento della macchina e soprattutto le registrazioni di errori e problemi; leggendo questi file potrete ottenere preziosi dettagli su come risolvere i problemi di funzionamento del vostro computer.
Ogni file contiene i log di specifici aspetti del sistema; il file principale che determina la configurazione del processo di logging è /etc/syslog.conf.
La sintassi dei file di log è uniforme: ogni messaggio viene scritto su una singola riga: inizia sempre con l'indicazione del momento in cui viene effettuata la registrazione, il nome del computer su cui gira il programma che ha generato il log, di solito (anche se non è obbligatorio) il nome del programma stesso ed infine, preceduto da un due punti, il messaggio vero e proprio. Ad esempio, nel file /var/log/mail.info trovo:
Mar 26 12:18:04 snoopy fetchmail[12106]: starting fetchmail 4.3.9 daemon
che significa questo: il 26 marzo alle ore 12, 18 minuti e 4 secondi il daemon di log ha ricevuto una richiesta dal processo fetchmail, che in quel momento stava girando con PID3 12106 sulla macchina snoopy (che è il nome locale del mio computer); il messaggio era "starting fetchmail 4.3.9 daemon" e serve solo per segnalare che qualcuno lo ha eseguito.
Ovviamente questo era il tipico messaggio puramente informativo; altri possono essere di ben maggior aiuto nella risoluzione dei problemi, analizziamo ad esempio una riga di /var/log/news/news.err che contiene i log degli errori ricevuti dal sistema che gestisce il news server locale:
Dec 27 12:39:39 snoopy fetchnews[1717]: error writing groupinfo file (disk full?)
indica che il 27 dicembre il programma fetchnews ha incontrato un errore piuttosto grave non essendo riuscito a scrivere un file. Non essendo riuscito a continuare ha smesso di operare ed ha segnalato la cosa nei log; grazie a questo avvertimento sono riuscito immediatamente a capire cosa era andato storto ed a risolvere la situazione.
Se si pensa che anche i programmi del sottosistema di rete (come tcpd(8)4, xinetd(1), ipchains(8)) si appoggia su syslog per registrare il proprio normale funzionamento e soprattutto per segnalare eventi sospetti, appare immediatamente chiaro quanto sia importante imparare ad utilizzare al meglio i log.

Analizziamo ora a grandi linee il funzionamento del sistema: i programmi che hanno necessità di lasciare una traccia nei log si appoggiano sul sistema di logging che nelle moderne installazioni è formato da due daemon: syslogd e klogd che si occupano rispettivamente di gestire i messaggi provenienti dai normali programmi e dal kernel; normalmente klogd si appoggia su syslogd, il cui file di configurazione /etc/syslog.conf è quindi la parte più interessante da analizzare.

/etc/syslog.conf

Questo file contiene una serie di righe formate da coppie di campi selettore e azione, dove selettore definisce cosa loggare, e azione dove scrivere i log; i due campi sono separati da spazi o tabulazioni.
Ogni selettore è definito da una coppia facility e priorità separati da un punto: facility stabilisce il genere di dati da considerare per i log (ad esempio le informazioni provenienti dal processo che si occupa di gestire le stampanti, lpd(8)), mentre priorità pone un filtro alla gravità dei messaggi da loggare. Andiamo ad analizzare un esempio banale:
mail.err    /var/log/mail.err
significa che di tutti i messaggi provenienti dai programmi che gestiscono il sistema di posta (ad esempio sendmail, postfix, qmail, smail o simili) dobbiamo loggare nel file /var/log/mail.err solamente quelli la cui priorità corrisponde o supera il livello err.

Cosa è possibile loggare

Le facility predefinite si possono trovare nei sorgenti delle libc, nel file /usr/include/sys/syslog.h; vediamole:
  • auth: messaggi relativi alla sicurezza del sistema, come ad esempio registrazioni di login e logout (è usato da programmi come su(1), sudo(8), login(1)).
  • authpriv: come auth, ma il messaggio potrebbe contenere dati sensibili, quindi è bene dirigerlo su un file separato che ci si preoccuperà di non rendere leggibile a tutti.
  • cron: il sottosistema di temporizzazione degli eventi: comprende ad esempio daemon come cron(8), anacron(8), atd(8).
  • daemon: generico per i daemon di sistema.
  • kern: messaggi del kernel.
  • lpr: il sottosistema che gestisce le stampanti.
  • mail: tutto ciò che ha a che fare con la consegna o ricezione di email.
  • news: gestione delle news Usenet.
  • syslog: messaggi generati dallo stesso syslog.
  • user: messaggi generici provenienti da programmi in user space (in contrapposizione ai log generati dal kernel).
  • uucp: il sottosistema UUCP.
  • ftp: introdotto con le libc2.0, è riservato ai server ftp.
  • da local0 a local7: riservati per utilizzi locali.
Oltre a questi sono validi security che però è obsoleto e andrebbe sostituito con auth, e mark che è ad esclusivo uso interno (utile per dirigere in un unico file tutti i marcatori, dei quali si parlerà in seguito).

Priorità

Insieme al tipo di dato da loggare, l'applicazione specifica anche la gravità e l'urgenza del messaggio. Sempre in /usr/include/sys/syslog.h si trovano le definizioni dei vari livelli di priorità. Vediamoli in ordine dal meno importante al più urgente:
  • debug: non mostrati normalmente ma solo quando si vuole ottenere informazioni dettagliate da un programma.
  • info: informazioni poco significative circa il funzionamento del programma.
  • notice: segnala una situazione significativa, ma sempre all'interno del normale funzionamento di un programma.
  • warning: messaggio generato in seguito a comportamenti più o meno anomali, che potrebbero essere legati a problemi di funzionamento.
  • err: si è verificata un errore.
  • crit: indica una situazione critica, che compromette seriamente il normale funzionamento del sottosistema coinvolto.
  • alert: condizione che impone un immediato intervento da parte dell'operatore.
  • emerg: il sistema risulta inutilizzabile.
Oltre a questi esistono anche warn, error e panic che però sono obsolete e corrispondono rispettivamente a warning, err ed emerg.

Sintassi avanzata di /etc/syslog.conf

È possibile creare righe di configurazione anche piuttosto complesse combinando in vari modi più facility e più livelli di priorità. Prima di tutto è possibile utilizzare il carattere * come jolly per tutte le facility o priorità, e la parola chiave none per indicare nessuna priorità. Ad esempio:
lpr.*    /var/log/lpr.log
raccoglie i messaggi del sottosistema di stampa di ogni priorità e li scrive nel file /var/log/lpr.log (nella pratica è del tutto equivalente a lpr.debug).

È poi possibile specificare più facility con la medesima priorità separandole con virgole:

user,daemon.crit    /var/log/misc.crit
salva nel file indicato i messaggi critici appartenenti alle facility user e daemon. Continuiamo con gli esempi:
*.emerg    /var/log/all.emerg
memorizza tutti i messaggi relativi ad emergenze.

È anche possibile scegliere diversi tipi di dati da inviare allo stesso file; basta separare le diverse entry con punti e virgola:

uucp.*;news.info    /var/log/uucp_and_news.log
salva tutti i messaggi provenienti dal sottosistema UUCP e i messaggi che siano di informazione (o più gravi) dal news server.
*.*;auth,authpriv.none    /var/log/quasi_tutto.log
salva pressoché ogni tipo di messaggio da ogni sistema, tranne tutti i messaggi (grazie alla keyword none) relativi alla sicurezza.

Come detto quando si specifica una priorità significa che tutti i messaggi del dato livello e tutti i messaggi più gravi vengono salvati nel file indicato. È anche possibile specificare che si desidera esclusivamente una data priorità anteponendole un uguale:

mail.=info    /var/log/mail.info
mail.=warning    /var/log/mail.warn
salva in /var/log/mail.warn solo i messaggi di warning del sottosistema che si occupa della gestione delle email, mentre in /var/log/mail.info si troveranno solo quelli informativi: se qui non si fosse aggiunto l'uguale avrebbe raccolto anche i messaggi di tipo notice, warning e degli altri livelli superiori.

Sempre per quanto riguarda le priorità è possibile invertire la selezione facendola precedere da un punto esclamativo:

mail.*;mail.!notice    /var/log/mail.log
che salva solo i messaggi di tipo debug e info, o anche:
mail.*;mail.!=debug    /var/log/mail.nodebug
che memorizza tutti i messaggi del dato sottosistema ad esclusione di quelli relativi al debug (questo esempio equivale quindi a mail.info).

In ogni punto lungo le righe di configurazione è possibile andare a capo terminando la riga con un backslash:

*.=info;*.=notice;*.=warning;\
        auth,authpriv.none;\
        cron,daemon.none;\
        mail,news.none    /var/log/messages
salva tutti i messaggi di tipo info, notice o warning tralasciando quelli dai sistemi di sicurezza, cron, daemon vari, mail e news.

I file di log

Finora abbiamo visto come riempirci il disco fisso di file di log, ma in realtà ci sono molte altre possibilità. Vediamo di seguito come personalizzare ulteriormente il file di configurazione agendo sul campo azione.

Normali file

Come sopra descritto negli esempi i nomi di file vanno obbligatoriamente scritti con il percorso completo (ovvero devono cominciare con uno /). Bisogna ricordare che ogni volta che il sistema di log va a scrivere una riga nei suoi file effettua una chiamata alla funzione di sistema fsync(2) che svuota il buffer riversandolo su disco fisso; nel caso che numerose righe debbano essere scritte in pochissimo tempo è possibile assistere ad una certa perdita di performance del sistema, completamente impegnato a tenere aggiornati quei file. Per evitare questi problemi è possibile disabilitare la continua sincronizzazione facendo precedere un meno (-) al nome del file: in questo modo il sistema risulterà meno impegnato; c'è ovviamente il rischio che in caso di crash alcune delle ultime righe del file non siano state ancora salvate: questo però non è di norma un grosso problema, se non per i log di eventi critici di parti fondamentali del sistema.
cron.*    -/var/log/cron.log
non sincronizza il file /var/log/cron.log ogni volta che una riga viene aggiunta; anche in caso di crash difficilmente i messaggi del sistema di temporizzazione saranno di nostro interesse.

Named pipe (o FIFO)

Anteponendo il carattere | al nome del file è possibile indicare che il file è in realtà una FIFO, o named pipe (per fare un esempio il file /dev/xconsole è una FIFO); potete creare named pipe con i comandi mkfifo(1) o mknod(1).

Terminali virtuali

Caratteristica molto utile è la facoltà di inviare i messaggi non ad un normale file, ma ad un terminale virtuale (come ad esempio i vari /dev/tty* e /dev/console):
kern.*    /var/log/kern.log
kern.*    /dev/tty9
Logga tutti i messaggi provenienti dal kernel nel file /var/log/kern.log ed inoltre li stampa sulla tty numero 9: potrete ora passare su quella console con la combinazione Alt+F9 (Ctrl+Alt+F9 se siete in ambiente X Window System) e leggere gli ultimi messaggi che il vostro kernel ha inviato.

Macchina remota

Possibilità che fa felici gli amministratori di sistema è quella di inviare i log ad un'altra macchina, scrivendone il nome preceduto da un @:
daemon.*    @ginopino
invia tutti i messaggi specificati alla macchina ginopino; da notare che su questa macchina deve necessariamente girare il daemon syslogd lanciato con l'opzione -r che lo pone in ascolto di messaggi dalla rete. Ovviamente nel file di log di questa macchina comparirà il nome del computer che ha generato il messaggio.
Se si dispone di una rete locale è da prendere in considerazione l'opportunità di loggare anche su una macchina remota almeno i log critici: questi infatti possono essere generati anche da problemi fisici dei dischi fissi, che potrebbero quindi rendere inaccessibili i file locali.

Utenti

Al posto del classico nome di file si può indicare una lista di utenti, separati da virgole, che riceveranno gli avvisi se sono loggati:
*.alert    root,davide
mostra in console i messaggi indicati agli utenti root e davide.

Chiunque sia attualmente loggato

Come visto analizzando le priorità, alcune di esse indicano la necessità di prendere immediati provvedimenti; in questo caso è utile fare in modo che chiunque sia attualmente loggato riceva il messaggio (tramite wall(1)):
*.emerg    *
tutti i messaggi di emergenza vengono ricevuti da tutti gli utenti, come specificato dal carattere * al posto del nome di file.

Esempi di /etc/syslog.conf

Vediamo ora un paio di esempi completi tratti dalle installazioni di default di Debian 2.2 (frozen) e RedHat 6.2; le righe che iniziano con un cancelletto (#) sono considerate commenti ed ignorate.
# Tratto dal pacchetto RedHat 6.2 sysklogd-1.3.31-16.i386.rpm

# Trascrive tutti i messaggi del kernel sulla console.
# Come si vede è commentata, per evitare che di default
# si ricevano troppi messaggi non graditi.
#kern.*                                                 /dev/console

# Logga nel file /var/log/messages tutti i messaggi (a parte quelli riferiti
# alle email ed alla sicurezza) di livello info o superiore.
*.info;mail.none;authpriv.none                          /var/log/messages

# Il file /var/log/secure ha i permessi settati in modo da restringerne
# l'accesso al solo amministratore di sistema.
authpriv.*                                              /var/log/secure

# Logga tutto ciò che ha a che fare con le email.
mail.*                                                  /var/log/maillog

# Chiunque sia loggato riceve i messaggi che segnalano emergenze.
*.emerg                                                 *

# Salva i messaggi superiori o uguali al livello crit dei sottosistemi
# UUCP e news in un file a parte.
uucp,news.crit                                          /var/log/spooler

# Salva i messaggi mostrati durante il boot.
local7.*                                                /var/log/boot.log

# Tratto dal pacchetto Debian 2.2 (frozen) sysklogd_1.3-33.deb

# Log divisi per facility.
auth,authpriv.*                 /var/log/auth.log
*.*;auth,authpriv.none          -/var/log/syslog
#cron.*                         /var/log/cron.log
daemon.*                        -/var/log/daemon.log
kern.*                          -/var/log/kern.log
lpr.*                           -/var/log/lpr.log
mail.*                          /var/log/mail.log
user.*                          -/var/log/user.log
uucp.*                          -/var/log/uucp.log

# Log separati per i livelli più significativi
# per quanto riguarda il mail server.
mail.info                       -/var/log/mail.info
mail.warn                       -/var/log/mail.warn
mail.err                        /var/log/mail.err

# Idem per il news server.
news.crit                       /var/log/news/news.crit
news.err                        /var/log/news/news.err
news.notice                     -/var/log/news/news.notice

# I messaggi di debug.
*.=debug;\
        auth,authpriv.none;\
        news.none;mail.none     -/var/log/debug

# I messaggi informativi.
*.=info;*.=notice;*.=warn;\
        auth,authpriv.none;\
        cron,daemon.none;\
        mail,news.none          -/var/log/messages

# Segnala a tutti le emergenze.
*.emerg                         *

# Alcuni messaggi che potrebbe tornare utile mostrare su una
# console separata.
#daemon,mail.*;\
#       news.=crit;news.=err;news.=notice;\
#       *.=debug;*.=info;\
#       *.=notice;*.=warn       /dev/tty8

# Idem sulla xconsole.  Lanciare il programma xconsole(1x)
# per vedere i log.
daemon.*;mail.*;\
        news.crit;news.err;news.notice;\
        *.=debug;*.=info;\
        *.=notice;*.=warn       |/dev/xconsole

Come vedete le politiche seguite sono piuttosto diverse: sicuramente la Debian (che tra parentesi usa alcune keyword obsolete!) punta a fornire un sistema che presenti i log in maniera più organica dividendo logicamente i messaggi e creando anche alcuni file "calderoni" di informazioni di varia provenienza, mentre RedHat crea log solo di dati realmente essenziali nel tentativo di mantenere basso lo spazio occupato su disco.

Visualizzare i log

Finora ci siamo limitati a configurare il sistema di logging in modo che salvi i suoi messaggi in alcuni file; ovviamente la maggior parte di questi file non sono di nostro costante interesse ed andremo ad analizzarli con un semplice editor o viewer di testo in caso di necessità (vi, emacs, cat, more o less vanno benissimo); più raramente, ad esempio mentre stiamo cercando di riprodurre una situazione di errore per comprendere un problema, abbiamo l'esigenza di monitorare perennemente un file di log: in questo caso possiamo ricorrere alla comoda utility tail(1); per esempio possiamo aprire una nuova console o XTerm e digitare il comando:
tail -f /var/log/mail.err
grazie al quale avremo costantemente sott'occhio le ultime righe del file. Con analoghi sistemi è possibile monitorare costantemente i file che ci forniscono informazioni sulla connessione di rete (utile mentre siamo collegati ad un canale IRC frequentato da gente non particolarmente amichevole). Ad esempio si può approntare uno script del genere (chiamiamolo /usr/local/sbin/slog):
#!/bin/sh
tail -f /var/log/misclog.log | grep -v 'from snoopy.mio [127.0.0.1]'
che, come si vede, evita di mostrare le righe che si riferiscono alle connessione effettuate in locale (snoopy.mio è il nome completo della mia macchina). Questo comando può essere eseguito dentro una nuova finestra di xterm(1), magari creando un altro script (salvatelo come /usr/local/sbin/thelogger):
#!/bin/sh
xterm -geometry 100x4+0+0 -ls -sl 200 -fg red -T "The LoggeR" \
      -name logger -e /usr/local/sbin/slog

Oltre a questi semplici mezzi sono disponibili in rete anche programmi più o meno complessi studiati appositamente per monitorare ed analizzare i vari log, spesso limitandosi ad un solo tipo di dati (ad esempio quelli forniti dal web server); vedere la sezione Link per trovarne alcuni elenchi.

Altri log

Pur avendo parlato tanto, ci siamo per ora limitati a trattare i log gestiti tramite i daemon syslogd e klogd; questi sono tra i più importanti ed interessanti ma non sono certamente i soli: nella directory /var/log troviamo infatti alcuni file il cui contenuto viene aggiornato autonomamente da determinati programmi senza passare dai suddetti daemon. Vediamone alcuni particolarmente significativi:
  • wtmp: un database che riporta i login e i logout di tutti gli utenti del sistema; contiene inoltre i dati relativi ai reboot. Questo file viene aggiornato da login(1), init(8), halt(8) e da certi getty(1) ed è utilizzato da programmi come last(1).
  • utmp: informazioni su chi sta attualmente utilizzando il sistema (vedere ad esempio il comando who(1)).
  • btmp: elenca i login falliti, è accessibile tramite lastb(1).
  • faillog: elenco dei login falliti, vedere il comando faillog(8).
  • lastlog: informazioni su quando i vari utenti hanno effettuato l'ultimo login; usare il comando lastlog(8) (è usato anche per mostrare la data e ora dell'ultimo accesso ad ogni nuovo login).
Molti dei file qui descritti dipendono direttamente da login(1): vedere la pagina di manuale relativa e il file di configurazione /etc/login.defs.

Normalmente si troveranno in /var/log altri file ed altre sottodirectory: di solito queste sono gestite autonomamente da un singolo programma, la cui documentazione è quindi l'unico riferimento per formato ed uso dei file di log; esempi classici sono la sottodirectory apache (o httpd) che mantiene nota degli accessi al proprio web server e il file xdm-errors (o con un nome simile) gestito dal programma xdm(1x).

Annegare nei log

Con tutti questi file nella directory /var/log che non fanno altro che accrescersi continuamente è facile immaginare di ritrovarsi in breve tempo con una directory che occupa tantissimo spazio su disco, peraltro con dati che difficilmente ci interessano, specie quelli più vecchi di un certo tot di tempo (che può essere un mese per messaggi particolarmente significativi, ma anche pochi giorni per i log di banali informazioni sul corretto funzionamento del sistema). Per ovviare a questo problema esistono una serie di tool normalmente inseriti in ogni distribuzione che si occupano di prendere periodicamente i file di log, copiarne il contenuto in un file che verrà poi compresso per risparmiare ulteriore spazio ed infine azzerarne la lunghezza; dopo un certo periodo di tempo gli stessi file compressi contenenti i vecchi log vengono cancellati definitivamente.
In Debian potete fare riferimento alla pagina di manuale del comando savelog(8) mentre RedHat si appoggia a logrotate(8); entrambi vengono normalmente eseguiti periodicamente tramite cron(8) e sono estremamente personalizzabili. Vedere la sezione Link, per questi ed altri programmi simili.
Per rendere l'idea, una directory /var/log gestita da savelog(8) e con un /etc/syslog.conf settato in maniera anche piuttosto prolissa difficilmente a regime dovrebbe superare i pochissimi megabyte di dimensione, almeno su macchine casalinghe.

Analisi approfondita di syslog

Quello che abbiamo visto finora rappresenta quanto necessario conoscere per utilizzare in modo corretto il sistema di log; cerchiamo ora di analizzare più da vicino il funzionamento di syslog, in modo da farci un quadro teorico più preciso.

Il daemon principale di tutto il sistema è syslogd(8), il cui comportamento è stabilito dal file di configurazione /etc/syslog.conf; si occupa di raccogliere i messaggi da loggare e di scriverli nei rispettivi file (o di trasferirli ad un host remoto o visualizzarli sulla console, a seconda di come configurato). Può essere avviato sia dai normali file di inizializzazione (rc.*, per intenderci) che da init(8) tramite l'apposita opzione -n, anche se il primo metodo è certamente il più diffuso e preferibile.
Una volta lanciato di default apre in lettura la unix domain socket /dev/log dove i programmi interessati potranno inviare, tramite opportune funzioni di libreria, i propri messaggi da far loggare; se avviato con l'opzione -r (e con permessi di superutente) si metterà anche in ascolto sulla porta UDP 514 in modo che altre macchine possano inviargli i propri log.

Se syslogd è in grado di ricevere i messaggi provenienti dai normali programmi, va ricordato che esiste un'altra fonte importantissima di log: il kernel. Per questo esiste il daemon klogd(8) che può essere lanciato nel medesimo modo di syslogd (per la precisione è bene lanciare klogd dopo di syslogd, e disattivarli in ordine inverso).
Il kernel utilizza la funzione printk() per inviare i propri messaggi: se nessuno li intercetta di default essi vengono stampati sulla console. Il daemon klogd si pone in "ascolto" del file /proc/kmsg (il filesystem /proc è un filesystem virtuale: i suoi file non occupano spazio su disco), se questo file non è presente o klogd viene avviato con l'opzione -s viene utilizzata l'equivalente chiamata di sistema sys_syslog().
Il formato di default dei messaggi provenienti dal kernel è:

<[0-7]>Testo del messaggio.
dove il numero tra 0 e 7 racchiuso dalla coppia di minore/maggiore indica la priorità del messaggio; queste priorità sono definite nel file include/linux/kernel.h (tra i sorgenti del kernel) e sono del tutto analoghe a quelle già viste per syslogd, con <7> ad indicare i messaggi di debug e <0> per riportare che il sistema è in uno stato inutilizzabile.
A questo punto quando klogd intercetta un messaggio proveniente dal kernel di default lo passa a syslogd con kern come facility e il corrispondente livello di priorità; in alternativa klogd può essere lanciato con l'opzione -f file grazie alla quale i messaggi verranno loggati nel file indicato.
Oltre a ciò i messaggi il cui livello sia inferiore a quello prestabilito (il default è il livello 6, quindi tutti i messaggi dei livelli da 0 a 5) vengono anche mostrati in console; questo comportamento può essere modificato con l'opzione -c numero che consente di cambiare questa impostazione; ad esempio può essere una buona idea usare -c 4, specie su macchine con parecchi utenti in modo da limitare i messaggi visualizzati in console a quelli di emergenza, di allerta, critici e di errore.

Come c'era da aspettarsi il kernel può trovarsi nella condizione di dover comunicare informazioni parecchio importanti, quando si trova in situazioni critiche. In caso di serio errore interno viene generato un Oops che mostra lo stato dei registri, il contenuto della stack del kernel e il codice che si stava eseguendo quando è intervenuto il problema. Sfortunatamente i dati mostrati sono di norma prettamente numerici e quindi praticamente inutili: anche se li passaste ad uno sviluppatore questi non saprebbe cosa farsene visto che questi valori variano a seconda di come il kernel è stato compilato; per ovviare a questo problema in fase di compilazione del kernel viene creato un file di nome System.map che mappa funzioni e variabili del kernel facendo corrispondere ai loro indirizzi numerici un nome significativo. Il daemon klogd cerca questo file nell'ordine in /boot/System.map, /System.map e /usr/src/linux/System.map (oppure si può indicare quale file usare da riga di comando con l'opzione -k file); se non lo trova dovrete manualmente utilizzare l'utility ksymoops che trovate nella directory scripts/ksymoops tra i sorgenti del kernel per tradurre i valori numerici; di norma troverete l'oops tra i file di log, ma non è però così raro trovarsi nella situazione in cui il general protection fault sia talmente grave da uccidere klogd o impedire la scrittura su disco fisso: in questo caso è necessario trascrivere su carta quanto mostrato sullo schermo.
A complicare ulteriormente le cose va ricordato che ormai moltissime parti del kernel possono essere compilate come moduli da caricare dinamicamente in memoria quando necessario; in questa situazione non è possibile stabilire una corrispondenza statica tra indirizzi numerici e rappresentazione simbolica. Il kernel supporta delle chiamate di sistema utili per determinare quali moduli siano attualmente caricati in memoria; usando queste chiamate klogd è in grado quanto meno di indicare quale modulo abbia generato l'errore (se questo è stato ben progettato esportando le symbol information saranno disponibili maggiori dettagli); queste informazioni vanno però raccolte ed aggiornate ogni volta che un modulo viene caricato o scaricato: klogd può venir avvertito che è richiesto un aggiornamento alle sue tabelle semplicemente chiamandolo con l'opzione -i (usando -I si aggiornano sia le informazioni riguardanti i moduli sia quelle relative del file System.map). Altra opzione utile in questi casi è -p, da usare al lancio di klogd per fare in modo che le informazioni sui moduli vengano automaticamente aggiornate in caso si riceva un oops (ovviamente questa opzione va usata con cognizione di causa: un kernel che ha sollevato un general protection fault è in una condizione piuttosto instabile: per ricaricare le informazioni klogd ha bisogno di effettuare alcune chiamate di sistema che potrebbero compromettere l'intera operazione e quel che resta del sistema).
In generale è preferibile fare in modo che queste informazioni siano aggiornate al caricamento e scaricamento dei moduli; per rendere la cosa automatica (senza dover richiamare ogni volta klogd con l'opzione -i) è possibile applicare ai programmi insmod(1), rmmod(1) e modprobe(1) del pacchetto modules (o modutils) una patch5 che si trova tra i sorgenti di syslog; sfortunatamente questa patch è scritta per una versione non proprio recente delle modutils, comunque leggendola non dovrebbe essere difficile capire cosa e come modificare.

Tanto syslogd quanto klogd rispondono ad una serie di segnali (che l'utente può inviare tramite il comando kill(1)); nella documentazione si trova l'elenco completo, qui ricordiamo solo i principali: tramite SIGHUP è possibile far rileggere il file di configurazione a syslogd, grazie a SIGTSTP e SIGCONT è possibile rispettivamente sospendere temporaneamente e far riprendere l'esecuzione di klogd; sempre per quanto riguarda klogd si può forzarlo ad aggiornare le informazioni relative ai moduli con il segnale SIGUSR1.
Entrambi i daemon utilizzano dei file nella directory /var/run per indicare i propri PID: i file sono syslogd.pid e klogd.pid.

Sicurezza

Difficilmente si penserà che un oggetto così utile e tutto sommato poco interagente con il mondo esterno possa in qualche modo rappresentare un problema per la sicurezza, eppure questo rischio esiste e si può manifestare sotto vari aspetti.
Prima di tutto nei log di sistema finiscono informazioni molto importanti proprio sulla sicurezza stessa del sistema; è quindi bene porre massima attenzione nello scegliere permessi d'accesso e proprietario dei file in cui i messaggi verranno scritti; in secondo luogo se il sistema dovesse cessare di funzionare nessun log verrebbe più scritto e potremmo perdere informazioni molto preziose: per accorgerci di un simile evento syslogd di default scrive nei file di log ad intervalli regolari (di norma 20 minuti, personalizzabili tramite l'opzione -m minuti) una riga il cui unico messaggio è -- MARK --, in questo modo l'assenza di questo marcatore nei file di log indica che il daemon non ha funzionato correttamente per un certo periodo.
Anche un corretto funzionamento può provocare dei seri rischi: nei log inviati tramite facility auth e authpriv finiscono le informazioni su login, logout e altre procedure di autenticazione degli utenti: molto facilmente possono comparire informazioni sensibili, ed è quindi decisamente consigliabile assicurarsi che i permessi di questi file di log non ne consentano la lettura a chiunque; analoghi problemi vengono posti da pppd(8) e chat(8) (usati per il collegamento dial-up al provider) che potrebbero loggare la procedura di connessione che mostrerebbe anche username e password dell'utente.
Può succedere, per errore o per deliberato attacco da parte di un utente locale o via rete, che la stessa "bravura" di syslog di registrare tutti i messaggi possa diventare un problema: se uno stesso messaggio venisse inviato migliaia di volte si rischia certo di rallentare leggermente le prestazioni del sistema ma anche di esaurire lo spazio su disco, eventualità che non è mai particolarmente piacevole. Non ci sono molti modi per proteggersi efficacemente da simili attacchi: per quanto riguarda i messaggi di eventi registrati dalla rete molti programmi hanno l'ottima abitudine di salvare solo il primo di una raffica ravvicinata di numerosi eventi identici, e in seguito di indicare semplicemente quante volte il fatto si è ripetuto (ad esempio con un messaggio come "last message repeated 10 times"). Altra buona idea è il fare in modo che la directory /var o /var/log sia su una partizione separata dal resto del sistema; come se non bastasse tramite quota è possibile restringere il massimo uso di spazio su disco per un dato utente oppure si può semplicemente riservare al superutente un certo spazio sulla partizione (vedere le pagine di manuale di mke2fs(8) e tune2fs(8)); entrambe queste soluzioni prevedono però che il daemon syslogd venga eseguito come utente non privilegiato (ovvero non da root), il che preclude l'utilizzo dell'opzione -r.
Proprio a questo proposito meglio ricordarsi che se si lancia syslogd con l'opzione -r è bene limitare l'accesso alla porta 514 UDP solo alle macchine effettivamente autorizzate ad inviarci messaggi (per la gestione di firewall vedere le pagine di manuali di ipchains(8) per i kernel 2.2, ipfwadm(8) per i 2.0 e iptables(8) per i 2.3 e 2.4).
Infine c'è anche la possibilità che in determinate circostanze alcuni messaggi di log vengano effettivamente persi, ma si tratta di casi piuttosto particolari e dipendenti da numerosi fattori (vedere a questo proposito il lungo thread generato da questo avviso apparso su bugtraq qualche tempo fa).

Programmazione

Vediamo brevemente qualche esempio per rendere i nostri programmi più prolissi.
Programmando in C dobbiamo semplicemente includere la libreria syslog.h delle libc ed usare poi alcune semplici funzioni. Esempio:
#include <syslog.h>

void main() {
    syslog(LOG_ERR, "pofferbacco");
}
questo banalissimo programma chiama semplicemente la funzione syslog(3) scrivendo il messaggio "pofferbacco" con priorità LOG_ERR (ovvero è classificato come errore) tramite la facility di default che è LOG_USER (ovvero i generici messaggi di programmi che lavorano in spazio utente). Se guardo nel file /var/log/user.log trovo:
Apr  5 14:41:37 snoopy test: pofferbacco
Complichiamo un poco le cose:
#include <syslog.h>

void main() {
    char* ERR_MEX = "errore di livello critico";

    openlog("Programma di test", LOG_CONS, LOG_DAEMON);
    syslog(LOG_CRIT, "pofferbacco: %s.", ERR_MEX);
    closelog();
}
la chiamata ad openlog() ci consente di settare alcune opzioni: la prima è il nome del programma come apparirà nei log (ovviamente non invito nessuno ad utilizzare nomi tanto lunghi: l'ideale resta il puro e semplice nome dell'eseguibile); la seconda indica una o più (in caso vengano messe in OR) opzioni che modificano il comportamento del sistema: in questo caso ho indicato che se non si riuscisse a contattare il daemon syslogd si dovrà scrivere il messaggio in console; la terza indica la facility da utilizzare, qui facciamo finta di essere daemon di sistema. Anche la chiamata a syslog() è leggermente cambiata e mostra come sia possibile utilizzare variabili nella stringa da loggare. In questo caso nel file /var/log/daemon.log trovo:
Apr  5 14:43:21 snoopy Programma di test: pofferbacco: errore di livello critico.
Per usi più complessi e per riferimenti alle facility, priorità e opzioni disponibili vi rimando alla pagina di manuale di syslog(3).

Naturalmente sarebbe limitante poter accedere al sistema di log solo tramite il C; per questo molti altri linguaggi mettono a disposizione opportune funzioni di libreria atte allo scopo.
Programmando in shell (sh, bash, tcsh, csh, ash, zsh, etc.) si può usare il comando logger(1) (di solito incluso nel pacchetto bsdutils o simile) grazie al quale possiamo specificare anche la facility e la priorità desiderate (opzione -p facility.priorità). Per esempio, se fossimo paranoici, potremmo aggiungere al file /usr/X11R6/bin/startx una riga del tipo:

logger -p user.info "L'utente `id -un` ha lanciato X."
Che mostrerà in /var/log/user.log:
Apr  5 15:01:52 snoopy davide: L'utente davide ha lanciato X.

Anche in Perl si può accedere a syslog tramite Sys::Syslog (vedere syslog(3pm)) che fornisce tutte le funzioni del caso.
Analoga situazione per chi vuole programmare in Tcl, che può riferirsi alla pagina di manuale di syslog(n).
In Python si possono utilizzare le funzioni e le costanti definite nel modulo syslog (disponibile ovviamente solo per sistemi Unix, vedere la Python Library Reference); ad esempio:

#!/usr/bin/python

import syslog

syslog.openlog("test in python", syslog.LOG_PID, syslog.LOG_DAEMON)
syslog.syslog(syslog.LOG_WARNING, "Succedono cose strane...")
syslog.closelog()
genera nel file /var/log/daemon.log la riga:
Apr  5 15:09:15 snoopy test in python[1746]: Succedono cose strane...

Chi volesse cimentarsi nella programmazione del kernel può far riferimento alla pagina di manuale di syslog(2) per avere maggiori informazioni su come funzionano le cose "all'interno".

Compatibilità e altre soluzioni

Tutto ciò che abbiamo visto, specie per quanto riguarda le parti più visibili al normale utente come la sintassi del file di configurazione, vale solo ed esclusivamente per versioni abbastanza recenti di syslog; le versioni completamente conformi allo standard BSD hanno una struttura più semplice ed il file di configurazione impone un certo numero di limitazioni (per citarne una sola: i campi facility.priorità e azione devono necessariamente essere separati da tabulazioni e non possono contenere spazi) e pressoché tutte le opzioni "estese" mancano completamente (non si può far precedere il segno meno ai nomi dei file, non si possono specificare ! e = nella definizione della priorità e così via); in caso vi trovaste ad utilizzare una di queste versioni sono certo che una rapida lettura della documentazione vi toglierebbe ogni dubbio.

Esistono naturalmente progetti che puntano a sostituire syslog, cito qui solo syslog-ng che potete trovare a http://www.balabit.com/products/syslog_ng/, che pone maggior attenzione alla sicurezza ed alla flessibilità (ad esempio nei file di configurazione è possibile filtrare i messaggi di log tramite regular expression); come ovvio non consiglio a nessuno, a meno che non sia realmente esperto ed abbia validi motivi, di sperimentare alternative a syslog che è standard, ben collaudato e supportato.

Conclusioni

Abbiamo visto come usare una delle parti più nascoste e nonostante questo più importanti di un sistema Linux (e Unix in generale); spero di essere stato abbastanza efficace nel mostrarne le grandi potenzialità e vi invito come sempre a contattarmi senza timore all'indirizzo alberanid@libero.it.

Link

Potete trovare tutti i programmi relativi ai log di sistema che ho citato nell'articolo partendo dagli archivi di LinuxBerg e FreshMeat.

Note

1 - Il kernel è il nucleo del sistema operativo.
2 - Un daemon è un programma che gira continuamente in background per fornire un determinato servizio.
3 - PID è l'acronimo di Process ID, ovvero un numero che identifica univocamente tutti i processi che stanno attualmente girando su un dato computer. Potete vederli con i comandi ps(1), top(1) o pidof(8).
4 - In tutto l'articolo indicherò i nomi di comandi con la sintassi nome(n) dove "nome" è (in genere) il nome dell'eseguibile e "n" una stringa (normalmente un numero) che indica a quale sezione delle pagine di manuali si deve far riferimento. Ad esempio, la man page di syslog(3) la potete leggere eseguendo il comando: man 3 syslog.
5 - Le patch sono da applicare ai sorgenti del dato programma per aggiornarlo ad una nuova versione. Riferirsi alla documentazione di patch(1) e diff(1) rispettivamente per applicarle e per crearle.