Successivo: , Precedente: , Su: Funzioni di libreria   [Contenuti][Indice]


10.5 Leggere la lista degli utenti

Il vettore PROCINFO (vedi la sezione Variabili predefinite) dà accesso ai numeri ID reale ed effettivo dell’utente e del gruppo e, se disponibili, alla serie di gruppi ulteriori a cui l’utente appartiene. Comunque, poiché questi sono numeri, non forniscono informazioni molto utili per l’utente medio. Bisogna trovare un modo per reperire informazioni sull’utente associate con i numeri ID dell’utente e del gruppo. Questa sezione illustra una raccolta di funzioni per ottenere le informazioni dalla lista gli utenti. Vedi la sezione Leggere la lista dei gruppi per una raccolta di funzioni simili per ottenere informazioni dalla lista dei gruppi.

Lo standard POSIX non definisce il file dove sono mantenute le informazioni degli utenti. Invece, fornisce il file d’intestazione <pwd.h> e diverse subroutine del linguaggio C per ottenere informazioni sugli utenti. La funzione primaria è getpwent(), che sta per “get password entry”. La “password” proviene dal file originale della lista degli utenti, /etc/passwd, che contiene le informazioni sugli utenti assieme alle password criptate (da cui il nome).76

Sebbene un programma awk possa semplicemente leggere /etc/passwd direttamente, questo file può non contenere tutte le informazioni su tutti gli utenti del sistema.77 Per essere sicuri di poter produrre una versione leggibile e completa della banca dati degli utenti, è necessario scrivere un piccolo programma in C che chiama getpwent(). getpwent() viene definita in modo da restituire un puntatore a una struct passwd. Ogni volta che viene chiamata, restituisce l’elemento successivo della lista. Quando non ci sono più elementi, restituisce NULL, il puntatore nullo. Quando accade ciò, il programma C dovrebbe chiamare endpwent() per chiudere la lista.. Quel che segue è pwcat, un programma in C che “concatena” la lista delle password:

/*
 * pwcat.c
 *
 * Genera una versione stampabile della lista delle password.
 */
#include <stdio.h>
#include <pwd.h>

int
main(int argc, char **argv)
{
    struct passwd *p;

    while ((p = getpwent()) != NULL)
        printf("%s:%s:%ld:%ld:%s:%s:%s\n",
            p->pw_name, p->pw_passwd, (long) p->pw_uid,
            (long) p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);

    endpwent();
    return 0;
}

Se non si conosce il linguaggio C, non è il caso di preoccuparsi. L’output di pwcat è la lista degli utenti, nel formato tradizionale del file /etc/passwd con campi separati da due punti (:). I campi sono:

Login name

Il nome di login dell’utente.

Encrypted password

La password criptata dell’utente. Può non essere disponibile su alcuni sistemi.

User-ID

L’ID numerico dell’utente. (Su alcuni sistemi, è un numero di formato long [32bit] del linguaggio C, e non nel formato int [16bit]. Quindi, lo cambieremo in long per sicurezza.)

Group-ID

L’ID di gruppo numerico dell’utente. (Valgono le stesse considerazioni su long al posto di int.)

Full name

Il nome completo dell’utente, e talora altre informazioni associate all’utente.

Home directory

La directory di login (o “home”) (nota ai programmatori di shell come $HOME).

Login shell

Il programma che viene eseguito quando l’utente effettua l’accesso. Questo è comunemente una shell, come Bash.

Di seguito si riportano alcune righe di un possibile output di pwcat:

$ pwcat
-| root:x:0:1:Operator:/:/bin/sh
-| nobody:x:65534:65534::/:
-| daemon:x:1:1::/:
-| sys:x:2:2::/:/bin/csh
-| bin:x:3:3::/bin:
-| arnold:x:2076:10:Arnold Robbins:/home/arnold:/bin/sh
-| miriam:x:112:10:Miriam Robbins:/home/miriam:/bin/sh
-| andy:x:113:10:Andy Jacobs:/home/andy:/bin/sh
…

Dopo quest’introduzione, di seguito si riporta un gruppo di funzioni per ottenere informazioni sugli utenti. Ci sono diverse funzioni, che corrispondono alle omonime funzioni C:

# passwd.awk --- accedere alle informazioni del file delle password

BEGIN {
    # modificare per adattarlo al sistema in uso
    _pw_awklib = "/usr/local/libexec/awk/"
}

function _pw_init(    oldfs, oldrs, olddol0, pwcat, using_fw, using_fpat)
{
    if (_pw_inizializzato)
        return

    oldfs = FS
    oldrs = RS
    olddol0 = $0
    using_fw = (PROCINFO["FS"] == "FIELDWIDTHS")
    using_fpat = (PROCINFO["FS"] == "FPAT")
    FS = ":"
    RS = "\n"

    pwcat = _pw_awklib "pwcat"
    while ((pwcat | getline) > 0) {
        _pw_byname[$1] = $0
        _pw_byuid[$3] = $0
        _pw_bycount[++_pw_totale] = $0
    }
    close(pwcat)
    _pw_contatore = 0
    _pw_inizializzato = 1
    FS = oldfs
    if (using_fw)
        FIELDWIDTHS = FIELDWIDTHS
    else if (using_fpat)
        FPAT = FPAT
    RS = oldrs
    $0 = olddol0
}

La regola BEGIN imposta una variabile privata col nome della directory in cui si trova pwcat. Poiché è destinata a essere usata da una routine di libreria di awk, si è scelto di metterla in /usr/local/libexec/awk; comunque, in un altro sistema potrebbe essere messa in una directory differente.

La funzione _pw_init() mette tre copie delle informazioni sull’utente in tre vettori associativi. I vettori sono indicizzati per nome-utente (_pw_byname), per numero di ID-utente (_pw_byuid), e per ordine di occorrenza (_pw_bycount). La variabile _pw_inizializzato è usata per efficienza, poiché in questo modo _pw_init() viene chiamata solo una volta.

Poiché questa funzione usa getline per leggere informazioni da pwcat, dapprima salva i valori di FS, RS e $0. Annota nella variabile using_fw se la suddivisione in campi usando FIELDWIDTHS è attiva o no. Far questo è necessario, poiché queste funzioni potrebbero essere chiamate da qualsiai parte all’interno di un programma dell’utente, e l’utente può suddividere i record in campi a suo piacimento. Ciò rende possibile ripristinare il corretto meccanismo di suddivisione dei campi in un secondo momento. La verifica può restituire solo vero per gawk. Il risultato può essere falso se si usa FS o FPAT, o in qualche altra implementazione di awk.

Il codice che controlla se si sta usando FPAT, utilizzando using_fpat e PROCINFO["FS"], è simile.

La parte principale della funzione usa un ciclo per leggere le righe della lista, suddividere le righe in campi, e poi memorizzare la riga all’interno di ogni vettore a seconda delle necessità. Quando il ciclo è completato, _pw_init() fa pulizia chiudendo la pipe, impostando _pw_inizializzato a uno, e ripristinando FS (e FIELDWIDTHS o FPAT se necessario), RS e $0. L’uso di _pw_contatore verrà spiegato a breve.

La funzione getpwnam() ha un nome utente come argomento di tipo stringa. Se quell’utente è presente nella lista, restituisce la riga appropriata. Altrimenti, il riferimento a un elemento inesistente del vettore aggiunge al vettore stesso un elemento il cui valore è la stringa nulla:

function getpwnam(nome)
{
    _pw_init()
    return _pw_byname[nome]
}

In modo simile, la funzione getpwuid() ha per argomento il numero ID di un utente. Se un utente con quel numero si trova nella lista, restituisce la riga appropriata. Altrimenti restituisce la stringa nulla:

function getpwuid(uid)
{
    _pw_init()
    return _pw_byuid[uid]
}

La funzione getpwent() scorre semplicemnte la lista, un elemento alla volta. Usa _pw_contatore per tener traccia della posizione corrente nel vettore _pw_bycount:

function getpwent()
{
    _pw_init()
    if (_pw_contatore < _pw_totale)
        return _pw_bycount[++_pw_contatore]
    return ""
}

La funzione endpwent() reimposta _pw_contatore a zero, in modo che chiamate successive a getpwent() ricomincino da capo:

function endpwent()
{
    _pw_contatore = 0
}

In questa serie di funzioni, il fatto che ogni subroutine chiami _pw_init() per inizializzare il vettore della lista utenti risponde a una precisa scelta progettuale. Il lavoro necessario per eseguire un processo separato che generi la lista degli utenti, e l’I/O per esaminarla, si ha solo se il programma principale dell’utente chiama effettivamente una di queste funzioni. Se questo file di libreria viene caricato assieme a un programma dell’utente, ma non viene mai chiamata nessuna delle routine, non c’è nessun lavoro aggiuntivo richiesto in fase di esecuzione. (L’alternativa è quella di spostare il corpo di _pw_init() all’interno di una regola BEGIN, che esegua sempre pwcat. Questo semplifica il codice ma richiede di eseguire un processo extra il cui risultato potrebbe non essere mai utilizzato dal programma.)

A sua volta, chiamare ripetutamente _pw_init() non è troppo dispendioso, perché la variabile _pw_inizializzato permette di evitare di leggere i dati relativi agli utenti più di una volta. Se la preoccupazione è quella di minimizzare il tempo di esecuzione del programma awk, il controllo di _pw_inizializzato potrebbe essere spostato al di fuori di _pw_init() e duplicato in tutte le altre funzioni. In pratica, questo non è necessario, poiché la maggior parte dei programmi di awk è I/O-bound78, e una tale modifica complicherebbe inutilmente il codice.

Il programma id in Stampare informazioni sull’utente usa queste funzioni.


Note a piè di pagina

(76)

Questo è vero per le versioni più antiche di Unix. In quelle più recenti, la password di ogni utente è stata trasferita nel file /etc/shadow, un file non accessibile dall’utente normale. La struttura del file /etc/passwd è rimasta la stessa, ma al posto del campo password c’è una x.

(77)

Capita spesso che le informazioni sulla password siano memorizzate in una lista in rete.

(78)

I programmi si distinguono tradizionalemente in CPU-bound e I/O-bound. Quelli CPU-bound effettuano elaborazioni che non richiedono molta attività di I/O, come ad esempio la preparazione di una tavola di numeri primi. Quelli I/O bound leggono dei file, ma richiedono poca attività di elaborazione per ogni record letto.


Successivo: , Precedente: , Su: Funzioni di libreria   [Contenuti][Indice]