X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=session.tex;h=45ca8e7cab3b1d30809d2c810398d1ae0fcce0bb;hp=969c05db00568956836ea4bafe8cc1fd63fa949a;hb=aee4a2e76ad7b0ed41eb06c4c70b8db5df9a4451;hpb=f1b2cf6ae09fb598a0b44719644ffaa94c2864f3 diff --git a/session.tex b/session.tex index 969c05d..45ca8e7 100644 --- a/session.tex +++ b/session.tex @@ -1,39 +1,487 @@ -\chapter{Sessioni di lavoro} +%% session.tex +%% +%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to +%% copy, distribute and/or modify this document under the terms of the GNU Free +%% Documentation License, Version 1.1 or any later version published by the +%% Free Software Foundation; with the Invariant Sections being "Prefazione", +%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the +%% license is included in the section entitled "GNU Free Documentation +%% License". +%% +\chapter{Terminali e sessioni di lavoro} \label{cha:session} -Esamineremo in questo capitolo le modalità in cui sono organizzati i processi -all'interno del sistema e le varie relazioni che intercorrono fra di essi, e -le modalità che permettono, a partire dall'avvio del sistema, di organizzare -il lavoro degli utenti in sessioni di lavoro associate ai terminali di -controllo con cui essi si sono collegati al sistema. +I terminali per lungo tempo tempo sono stati l'unico modo per accedere al +sistema, per questo anche oggi che esistono molte altre interfacce, essi +continuano a coprire un ruolo particolare, restando strettamente legati al +funzionamento dell'interfaccia a linea di comando. -\section{La procedura di login} +Nella prima parte del capitolo esamineremo i concetti base del sistema delle +sessioni di lavoro, vale a dire il metodo con cui il kernel permette ad un +utente di gestire le capacità multitasking del sistema, permettendo di +eseguire più programmi in contemporanea. Nella seconda parte del capitolo +tratteremo poi il funzionamento dell'I/O su terminale, e delle varie +peculiarità che esso viene ad assumere a causa del suo stretto legame con il +suo uso come interfaccia di accesso al sistema da parte degli utenti. + + +\section{Il \textit{job control}} +\label{sec:sess_job_control} + +Viene comunemente chiamato \textit{job control} quell'insieme di funzionalità +il cui scopo è quello di permettere ad un utente di poter sfruttare le +capacità multitasking di un sistema Unix per eseguire in contemporanea più +processi, pur potendo accedere, di solito, ad un solo terminale,\footnote{con + X e con i terminali virtuali tutto questo non è più vero, dato che si può + accedere a molti terminali in contemporanea da una singola postazione di + lavoro, ma il sistema è nato prima dell'esistenza di tutto ciò.} avendo cioè +un solo punto in cui si può avere accesso all'input ed all'output degli +stessi. + + +\subsection{Una panoramica introduttiva} +\label{sec:sess_job_control_overview} + +Il \textit{job control} è una caratteristica opzionale, introdotta in BSD +negli anni '80, e successivamente standardizzata da POSIX.1; la sua +disponibilità nel sistema è verificabile attraverso il controllo della macro +\macro{\_POSIX\_JOB\_CONTROL}. In generale il \textit{job control} richiede il +supporto sia da parte della shell (quasi tutte ormai lo fanno), che da parte +del kernel; in particolare il kernel deve assicurare sia la presenza di un +driver per i terminali abilitato al \textit{job control} che quella dei +relativi segnali illustrati in \secref{sec:sig_job_control}. + +In un sistema che supporta il \textit{job control}, una volta completato il +login, l'utente avrà a disposizione una shell dalla quale eseguire i comandi e +potrà iniziare quella che viene chiamata una \textsl{sessione}, che riunisce +(vedi \secref{sec:sess_proc_group}) tutti i processi eseguiti all'interno +dello stesso login (esamineremo tutto il processo in dettaglio in +\secref{sec:sess_login}). + +Siccome la shell è collegata ad un solo terminale, che viene usualmente +chiamato \textsl{terminale di controllo}, (vedi \secref{sec:sess_ctrl_term}) +un solo comando alla volta (quello che viene detto in \textit{foreground}), +potrà scrivere e leggere dal terminale. La shell però può eseguire anche più +comandi in contemporanea, mandandoli in \textit{background} (aggiungendo una +\cmd{\&} alla fine del comando), nel qual caso essi saranno eseguiti senza +essere collegati al terminale. + +Si noti come si sia parlato di comandi e non di programmi o processi; fra le +funzionalità della shell infatti c'è anche quella di consentire di concatenare +più programmi in una sola riga di comando con le pipe, ed in tal caso verranno +eseguiti più programmi, inoltre, anche quando si invoca un singolo programma, +questo potrà sempre lanciare sottoprocessi per eseguire dei compiti specifici. + +Per questo l'esecuzione di un comando può originare più di un processo; quindi +nella gestione del job control non si può far riferimento ai singoli processi. +Per questo il kernel prevede la possibilità di raggruppare più processi in un +\textit{process group} (detto anche \textsl{raggruppamento di processi}, vedi +\secref{sec:sess_proc_group}) e la shell farà sì che tutti i processi che +originano da una riga di comando appartengano allo stesso raggruppamento, in +modo che le varie funzioni di controllo, ed i segnali inviati dal terminale, +possano fare riferimento ad esso. + +In generale allora all'interno di una sessione avremo un eventuale (può non +esserci) \textit{process group} in \textit{foreground}, che riunisce i +processi che possono accedere al terminale, e più \textit{process group} in +\textit{background}, che non possono accedervi. Il job control prevede che +quando un processo appartenente ad un raggruppamento in \textit{background} +cerca di accedere al terminale, venga inviato un segnale a tutti i processi +del raggruppamento, in modo da bloccarli (vedi \secref{sec:sess_ctrl_term}). + +Un comportamento analogo si ha anche per i segnali generati dai comandi di +tastiera inviati dal terminale che vengono inviati a tutti i processi del +raggruppamento in \textit{foreground}. In particolare \cmd{C-z} interrompe +l'esecuzione del comando, che può poi essere mandato in \textit{background} +con il comando \cmd{bg}.\footnote{si tenga presente che \cmd{bg} e \cmd{fg} + sono parole chiave che indicano comandi interni alla shell, e nel caso non + comportano l'esecuzione di un programma esterno.} Il comando \cmd{fg} +consente invece di mettere in \textit{foreground} un comando precedentemente +lanciato in \textit{background}. + +Di norma la shell si cura anche di notificare all'utente (di solito prima +della stampa a video del prompt) lo stato dei vari processi; essa infatti sarà +in grado, grazie all'uso di \func{waitpid}, di rilevare sia i processi che +sono terminati, sia i raggruppamenti che sono bloccati (in questo caso usando +l'opzione \const{WUNTRACED}, secondo quanto illustrato in +\secref{sec:proc_wait}). + + +\subsection{I \textit{process group} e le \textsl{sessioni}} +\label{sec:sess_proc_group} + +Come accennato in \secref{sec:sess_job_control_overview} nel job control i +processi vengono raggruppati in \textit{process group} e \textit{sessioni}; +per far questo vengono utilizzati due ulteriori identificatori (oltre quelli +visti in \secref{sec:proc_pid}) che il kernel associa a ciascun +processo:\footnote{in Linux questi identificatori sono mantenuti nei campi + \var{pgrp} e \var{session} della struttura \struct{task\_struct} definita in + \file{sched.h}.} l'identificatore del \textit{process group} e +l'identificatore della \textsl{sessione}, che vengono indicati rispettivamente +con le sigle \acr{pgid} e \acr{sid}, e sono mantenuti in variabili di tipo +\type{pid\_t}. I valori di questi identificatori possono essere visualizzati +dal comando \cmd{ps} usando l'opzione \cmd{-j}. + +Un \textit{process group} è pertanto definito da tutti i processi che hanno lo +stesso \acr{pgid}; è possibile leggere il valore di questo identificatore con +le funzioni \funcd{getpgid} e \funcd{getpgrp},\footnote{\func{getpgrp} è + definita nello standard POSIX.1, mentre \func{getpgid} è richiesta da SVr4.} +i cui prototipi sono: +\begin{functions} + \headdecl{unistd.h} + + \funcdecl{pid\_t getpgid(pid\_t pid)} + Legge il \acr{pgid} del processo \param{pid}. + + \funcdecl{pid\_t getpgrp(void)} + Legge il \acr{pgid} del processo corrente. + + \bodydesc{Le funzioni restituiscono il \acr{pgid} del processo, + \func{getpgrp} ha sempre successo, mentre \func{getpgid} restituisce -1 + ponendo \var{errno} a \errval{ESRCH} se il processo selezionato non + esiste.} +\end{functions} + +La funzione \func{getpgid} permette di specificare il \acr{pid} del processo +di cui si vuole sapere il \acr{pgid}; un valore nullo per \param{pid} +restituisce il \acr{pgid} del processo corrente; \func{getpgrp} è di norma +equivalente a \code{getpgid(0)}. + +In maniera analoga l'identificatore della sessione può essere letto dalla +funzione \funcd{getsid}, che però nelle \acr{glibc}\footnote{la system call è + stata introdotta in Linux a partire dalla versione 1.3.44, il supporto nelle + librerie del C è iniziato dalla versione 5.2.19. La funzione non è prevista + da POSIX.1, che parla solo di processi leader di sessione, e non di + identificatori di sessione.} è accessibile solo definendo +\macro{\_XOPEN\_SOURCE} e \macro{\_XOPEN\_SOURCE\_EXTENDED}; il suo prototipo +è: +\begin{prototype}{unistd.h}{pid\_t getsid(pid\_t pid)} + Legge l'identificatore di sessione del processo \param{pid}. + + \bodydesc{La funzione restituisce l'identificatore (un numero positivo) in + caso di successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà + i valori: + \begin{errlist} + \item[\errcode{ESRCH}] Il processo selezionato non esiste. + \item[\errcode{EPERM}] In alcune implementazioni viene restituito quando il + processo selezionato non fa parte della stessa sessione del processo + corrente. + \end{errlist} + } +\end{prototype} + +Entrambi gli identificatori vengono inizializzati alla creazione di ciascun +processo con lo stesso valore che hanno nel processo padre, per cui un +processo appena creato appartiene sempre allo stesso raggruppamento e alla +stessa sessione del padre. Vedremo poi come sia possibile creare più +\textit{process group} all'interno della stessa sessione, e spostare i +processi dall'uno all'altro, ma sempre all'interno di una stessa sessione. + +Ciascun raggruppamento di processi ha sempre un processo principale, il +cosiddetto \textit{process group leader}, che è identificato dall'avere un +\acr{pgid} uguale al suo \acr{pid}, in genere questo è il primo processo del +raggruppamento, che si incarica di lanciare tutti gli altri. Un nuovo +raggruppamento si crea con la funzione \funcd{setpgrp},\footnote{questa è la + definizione di POSIX.1, BSD definisce una funzione con lo stesso nome, che + però è identica a \func{setpgid}; nelle \acr{glibc} viene sempre usata + sempre questa definizione, a meno di non richiedere esplicitamente la + compatibilità all'indietro con BSD, definendo la macro + \macro{\_BSD\_SOURCE}.} il cui prototipo è: +\begin{prototype}{unistd.h}{int setpgrp(void)} + Modifica il \acr{pgid} al valore del \acr{pid} del processo corrente. + + \bodydesc{La funzione restituisce il valore del nuovo \textit{process + group}.} +\end{prototype} + +La funzione, assegnando al \acr{pgid} il valore del \acr{pid} processo +corrente, rende questo \textit{group leader} di un nuovo raggruppamento, tutti +i successivi processi da esso creati apparterranno (a meno di non cambiare di +nuovo il \acr{pgid}) al nuovo raggruppamento. È possibile invece spostare un +processo da un raggruppamento ad un altro con la funzione \funcd{setpgid}, il +cui prototipo è: +\begin{prototype}{unistd.h}{int setpgid(pid\_t pid, pid\_t pgid)} + Assegna al \acr{pgid} del processo \param{pid} il valore \param{pgid}. + + \bodydesc{La funzione ritorna il valore del nuovo \textit{process group}, e + -1 in caso di errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{ESRCH}] Il processo selezionato non esiste. + \item[\errcode{EPERM}] Il cambiamento non è consentito. + \item[\errcode{EACCES}] Il processo ha già eseguito una \func{exec}. + \item[\errcode{EINVAL}] Il valore di \param{pgid} è negativo. + \end{errlist} + } +\end{prototype} + +La funzione permette di cambiare il \acr{pgid} del processo \param{pid}, ma il +cambiamento può essere effettuato solo se \param{pgid} indica un +\textit{process group} che è nella stessa sessione del processo chiamante. +Inoltre la funzione può essere usata soltanto sul processo corrente o su uno +dei suoi figli, ed in quest'ultimo caso ha successo soltanto se questo non ha +ancora eseguito una \func{exec}.\footnote{questa caratteristica è implementata + dal kernel che mantiene allo scopo un altro campo, \var{did\_exec}, in + \struct{task\_struct}.} Specificando un valore nullo per \param{pid} si +indica il processo corrente, mentre specificando un valore nullo per +\param{pgid} si imposta il \textit{process group} al valore del \acr{pid} del +processo selezionato; pertanto \func{setpgrp} è equivalente a \code{setpgid(0, + 0)}. + +Di norma questa funzione viene usata dalla shell quando si usano delle +pipeline, per mettere nello stesso process group tutti i programmi lanciati su +ogni linea di comando; essa viene chiamata dopo una \func{fork} sia dal +processo padre, per impostare il valore nel figlio, che da quest'ultimo, per +sé stesso, in modo che il cambiamento di \textit{process group} sia immediato +per entrambi; una delle due chiamate sarà ridondante, ma non potendo +determinare quale dei due processi viene eseguito per primo, occorre eseguirle +comunque entrambe per evitare di esporsi ad una race +condition\index{race condition}. + +Si noti come nessuna delle funzioni esaminate finora permetta di spostare un +processo da una sessione ad un altra; infatti l'unico modo di far cambiare +sessione ad un processo è quello di crearne una nuova con l'uso di +\funcd{setsid}; il suo prototipo è: +\begin{prototype}{unistd.h}{pid\_t setsid(void)} + Crea una nuova sessione sul processo corrente impostandone \acr{sid} e + \acr{pgid}. + + \bodydesc{La funzione ritorna il valore del nuovo \acr{sid}, e -1 in caso di + errore, il solo errore possibile è \errval{EPERM}, che si ha quando il + \acr{pgid} e \acr{pid} del processo coincidono.} +\end{prototype} + +La funzione imposta il \acr{pgid} ed il \acr{sid} del processo corrente al +valore del suo \acr{pid}, creando così una nuova sessione ed un nuovo +\textit{process group} di cui esso diventa leader (come per i \textit{process + group} un processo si dice leader di sessione\footnote{in Linux la proprietà + è mantenuta in maniera indipendente con un apposito campo \var{leader} in + \struct{task\_struct}.} se il suo \acr{sid} è uguale al suo \acr{pid}) ed +unico componente. Inoltre la funzione distacca il processo da ogni terminale +di controllo (torneremo sull'argomento in \secref{sec:sess_ctrl_term}) cui +fosse in precedenza associato. + +La funzione ha successo soltanto se il processo non è già leader di un +\textit{process group}, per cui per usarla di norma si esegue una \func{fork} +e si esce, per poi chiamare \func{setsid} nel processo figlio, in modo che, +avendo questo lo stesso \acr{pgid} del padre ma un \acr{pid} diverso, non ci +siano possibilità di errore.\footnote{potrebbe sorgere il dubbio che, per il + riutilizzo dei valori dei \acr{pid} fatto nella creazione dei nuovi processi + (vedi \secref{sec:proc_pid}), il figlio venga ad assumere un valore + corrispondente ad un process group esistente; questo viene evitato dal + kernel che considera come disponibili per un nuovo \acr{pid} solo valori che + non corrispondono ad altri \acr{pid}, \acr{pgid} o \acr{sid} in uso nel + sistema.} Questa funzione viene usata di solito nel processo di login (per i +dettagli vedi \secref{sec:sess_login}) per raggruppare in una sessione tutti i +comandi eseguiti da un utente dalla sua shell. + + + +\subsection{Il terminale di controllo e il controllo di sessione} +\label{sec:sess_ctrl_term} + +Come accennato in \secref{sec:sess_job_control_overview}, nel sistema del +\textit{job control} i processi all'interno di una sessione fanno riferimento +ad un terminale di controllo (ad esempio quello su cui si è effettuato il +login), sul quale effettuano le operazioni di lettura e +scrittura,\footnote{nel caso di login grafico la cosa può essere più + complessa, e di norma l'I/O è effettuato tramite il server X, ma ad esempio + per i programmi, anche grafici, lanciati da un qualunque emulatore di + terminale, sarà quest'ultimo a fare da terminale (virtuale) di controllo.} e +dal quale ricevono gli eventuali segnali da tastiera. + +A tale scopo lo standard POSIX.1 prevede che ad ogni sessione possa essere +associato un terminale di controllo; in Linux questo viene realizzato +mantenendo fra gli attributi di ciascun processo anche qual'è il suo terminale +di controllo. \footnote{Lo standard POSIX.1 non specifica nulla riguardo + l'implementazione; in Linux anch'esso viene mantenuto nella solita struttura + \struct{task\_struct}, nel campo \var{tty}.} In generale ogni processo +eredita dal padre, insieme al \acr{pgid} e al \acr{sid} anche il terminale di +controllo (vedi \secref{sec:proc_fork}). In questo modo tutti processi +originati dallo stesso leader di sessione mantengono lo stesso terminale di +controllo. + +Alla creazione di una nuova sessione con \func{setsid} ogni associazione con +il precedente terminale di controllo viene cancellata, ed il processo che è +divenuto un nuovo leader di sessione dovrà riottenere\footnote{solo quando ciò + è necessario, cosa che, come vedremo in \secref{sec:sess_daemon}, non è + sempre vera.}, un terminale di controllo. In generale questo viene fatto +automaticamente dal sistema\footnote{a meno di non avere richiesto + esplicitamente che questo non diventi un terminale di controllo con il flag + \const{O\_NOCTTY} (vedi \secref{sec:file_open}). In questo Linux segue la + semantica di SVr4; BSD invece richiede che il terminale venga allocato + esplicitamente con una \func{ioctl} con il comando \const{TIOCSCTTY}.} +quando viene aperto il primo terminale (cioè uno dei vari file di dispositivo +\file{/dev/tty*}) che diventa automaticamente il terminale di controllo, +mentre il processo diventa il \textsl{processo di controllo} di quella +sessione. + +In genere (a meno di redirezioni) nelle sessioni di lavoro questo terminale è +associato ai file standard (di input, output ed error) dei processi nella +sessione, ma solo quelli che fanno parte del cosiddetto raggruppamento di +\textit{foreground}, possono leggere e scrivere in certo istante. Per +impostare il raggruppamento di \textit{foreground} di un terminale si usa la +funzione \funcd{tcsetpgrp}, il cui prototipo è: +\begin{functions} + \headdecl{unistd.h} + \headdecl{termios.h} + + \funcdecl{int tcsetpgrp(int fd, pid\_t pgrpid)} Imposta a \param{pgrpid} il + \textit{process group} di \textit{foreground} del terminale associato al + file descriptor \param{fd}. + + \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{ENOTTY}] Il file \param{fd} non corrisponde al terminale di + controllo del processo chiamante. + \item[\errcode{ENOSYS}] Il sistema non supporta il job control. + \item[\errcode{EPERM}] Il \textit{process group} specificato non è nella + stessa sessione del processo chiamante. + \end{errlist} + ed inoltre \errval{EBADF} ed \errval{EINVAL}. + } +\end{functions} +\noindent la funzione può essere eseguita con successo solo da +un processo nella stessa sessione e con lo stesso terminale di controllo. + +Come accennato in \secref{sec:sess_job_control_overview}, tutti i processi (e +relativi raggruppamenti) che non fanno parte del gruppo di \textit{foreground} +sono detti in \textit{background}; se uno si essi cerca di accedere al +terminale di controllo provocherà l'invio da parte del kernel di uno dei due +segnali \const{SIGTTIN} o \const{SIGTTOU} (a seconda che l'accesso sia stato +in lettura o scrittura) a tutto il suo \textit{process group}; dato che il +comportamento di default di questi segnali (si riveda quanto esposto in +\secref{sec:sig_job_control}) è di fermare il processo, di norma questo +comporta che tutti i membri del gruppo verranno fermati, ma non si avranno +condizioni di errore.\footnote{la shell in genere notifica comunque un + avvertimento, avvertendo la presenza di processi bloccati grazie all'uso di + \func{waitpid}.} Se però si bloccano o ignorano i due segnali citati, le +funzioni di lettura e scrittura falliranno con un errore di \errcode{EIO}. + +Un processo può controllare qual'è il gruppo di \textit{foreground} associato +ad un terminale con la funzione \funcd{tcgetpgrp}, il cui prototipo è: +\begin{functions} + \headdecl{unistd.h} \headdecl{termios.h} + + \funcdecl{pid\_t tcgetpgrp(int fd)} Legge il \textit{process group} di + \textit{foreground} del terminale associato al file descriptor \param{fd}. + \bodydesc{La funzione restituisce in caso di successo il \acr{pgid} del + gruppo di \textit{foreground}, e -1 in caso di errore, nel qual caso + \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{ENOTTY}] Non c'è un terminale di controllo o \param{fd} non + corrisponde al terminale di controllo del processo chiamante. + \end{errlist} + ed inoltre \errval{EBADF} ed \errval{ENOSYS}. + } +\end{functions} + +Si noti come entrambe le funzioni usino come argomento il valore di un file +descriptor, il risultato comunque non dipende dal file descriptor che si usa +ma solo dal terminale cui fa riferimento; il kernel inoltre permette a ciascun +processo di accedere direttamente al suo terminale di controllo attraverso il +file speciale \file{/dev/tty}, che per ogni processo è un sinonimo per il +proprio terminale di controllo. Questo consente anche a processi che possono +aver rediretto l'output di accedere al terminale di controllo, pur non +disponendo più del file descriptor originario; un caso tipico è il programma +\cmd{crypt} che accetta la redirezione sullo standard input di un file da +decifrare, ma deve poi leggere la password dal terminale. + +Un'altra caratteristica del terminale di controllo usata nel job control è che +utilizzando su di esso le combinazioni di tasti speciali (\cmd{C-z}, +\cmd{C-c}, \cmd{C-y} e \verb|C-\|) si farà sì che il kernel invii i +corrispondenti segnali (rispettivamente \const{SIGTSTP}, \const{SIGINT}, +\const{SIGQUIT} e \const{SIGTERM}, trattati in \secref{sec:sig_job_control}) a +tutti i processi del raggruppamento di \textit{foreground}; in questo modo la +shell può gestire il blocco e l'interruzione dei vari comandi. + +Per completare la trattazione delle caratteristiche del job control legate al +terminale di controllo, occorre prendere in considerazione i vari casi legati +alla terminazione anomala dei processi, che sono di norma gestite attraverso +il segnale \const{SIGHUP}. Il nome del segnale deriva da \textit{hungup}, +termine che viene usato per indicare la condizione in cui il terminale diventa +inutilizzabile, (letteralmente sarebbe \textsl{impiccagione}). + +Quando si verifica questa condizione, ad esempio se si interrompe la linea, o +va giù la rete o più semplicemente si chiude forzatamente la finestra di +terminale su cui si stava lavorando, il kernel provvederà ad inviare il +segnale di \const{SIGHUP} al processo di controllo. L'azione preimpostata in +questo caso è la terminazione del processo, il problema che si pone è cosa +accade agli altri processi nella sessione, che non han più un processo di +controllo che possa gestire l'accesso al terminale, che potrebbe essere +riutilizzato per qualche altra sessione. + +Lo standard POSIX.1 prevede che quando il processo di controllo termina, che +ciò avvenga o meno per un \textit{hungup} del terminale (ad esempio si +potrebbe terminare direttamente la shell con \cmd{kill}) venga inviato un +segnale di \const{SIGHUP} ai processi del raggruppamento di foreground. In +questo modo essi potranno essere avvisati che non esiste più un processo in +grado di gestire il terminale (di norma tutto ciò comporta la terminazione +anche di questi ultimi). + +Restano però gli eventuali processi in background, che non ricevono il +segnale; in effetti se il terminale non dovesse più servire essi potrebbero +proseguire fino al completamento della loro esecuzione; ma si pone il problema +di come gestire quelli che sono bloccati, o che si bloccano nell'accesso al +terminale, in assenza di un processo che sia in grado di effettuare il +controllo dello stesso. + +Questa è la situazione in cui si ha quello che viene chiamato un +\textit{orphaned process group}. Lo standard POSIX.1 lo definisce come un +\textit{process group} i cui processi hanno come padri esclusivamente o altri +processi nel raggruppamento, o processi fuori della sessione. Lo standard +prevede inoltre che se la terminazione di un processo fa sì che un +raggruppamento di processi diventi orfano e se i suoi membri sono bloccati, ad +essi vengano inviati in sequenza i segnali di \const{SIGHUP} e +\const{SIGCONT}. + +La definizione può sembrare complicata, e a prima vista non è chiaro cosa +tutto ciò abbia a che fare con il problema della terminazione del processo di +controllo. Consideriamo allora cosa avviene di norma nel \textit{job + control}: una sessione viene creata con \func{setsid} che crea anche un +nuovo process group: per definizione quest'ultimo è sempre \textsl{orfano}, +dato che il padre del leader di sessione è fuori dalla stessa e il nuovo +process group contiene solo il leader di sessione. Questo è un caso limite, e +non viene emesso nessun segnale perché quanto previsto dallo standard riguarda +solo i raggruppamenti che diventano orfani in seguito alla terminazione di un +processo.\footnote{l'emissione dei segnali infatti avviene solo nella fase di + uscita del processo, come una delle operazioni legate all'esecuzione di + \func{\_exit}, secondo quanto illustrato in \secref{sec:proc_termination}.} + +Il leader di sessione provvederà a creare nuovi raggruppamenti che a questo +punto non sono orfani in quanto esso resta padre per almeno uno dei processi +del gruppo (gli altri possono derivare dal primo). Alla terminazione del +leader di sessione però avremo che, come visto in +\secref{sec:proc_termination}, tutti i suoi figli vengono adottati da +\cmd{init}, che è fuori dalla sessione. Questo renderà orfani tutti i process +group creati direttamente dal leader di sessione (a meno di non aver spostato +con \func{setpgid} un processo da un gruppo ad un altro, cosa che di norma non +viene fatta) i quali riceveranno, nel caso siano bloccati, i due segnali; +\const{SIGCONT} ne farà proseguire l'esecuzione, ed essendo stato nel +frattempo inviato anche \const{SIGHUP}, se non c'è un gestore per +quest'ultimo, i processi bloccati verranno automaticamente terminati. + + + +\subsection{Dal login alla shell} \label{sec:sess_login} -L'organizzazione del sistema del \textit{Job Control}\footnote{viene - usualmente chiamata così la capacità del sistema di poter gestire più - processi (i \textit{job}) lanciati in contemporanea a partire da una shell, - di solito associata al terminale su cui si è appunto effettuato il login, in - modo da poter gestire la possibile presenza di più di un processo che - necessita di accedere a quest'ultimo.} è strettamente connessa alle modalità -con cui un utente accede al sistema, collegandosi ad esso con un terminale, -che sia questo realmente tale, come un VT100 collegato ad una seriale o -virtuale, come quelli associati a schermo e tastiera, o semplicemente -associato ad una connessione di rete. Per questo motivo in questa prima -sezione prenderemo in esame le modalità di come avviene tutto ciò e di come il -sistema (a partire da \cmd{init}) sia in grado di gestire il tutto. - - -\subsection{Il login su terminale} -\label{sec:sess_tty_log} - -La modalità più classica di accesso ad un sistema Unix è quella del login su -terminale, che pertanto esamineremo per prima. Abbiamo già brevemente -illustrato in \secref{sec:intro_kern_and_sys} le modalità con cui il sistema -si avvia, e di come, a partire da \cmd{init}, vengano lanciati tutti gli altri -programmi. Adesso prenderemo in esame in maniera dettagliata le modalità con -cui si arriva a fornire ad un utente la shell che gli permette di lanciare i -suoi comandi. +L'organizzazione del sistema del job control è strettamente connessa alle +modalità con cui un utente accede al sistema per dare comandi, collegandosi ad +esso con un terminale, che sia questo realmente tale, come un VT100 collegato +ad una seriale o virtuale, come quelli associati a schermo e tastiera o ad una +connessione di rete. Dato che i concetti base sono gli stessi, e dato che alla +fine le differenze sono\footnote{in generale nel caso di login via rete o di + terminali lanciati dall'interfaccia grafica cambia anche il processo da cui + ha origine l'esecuzione della shell.} nel dispositivo cui il kernel associa i +file standard (vedi \secref{sec:file_std_descr}) per l'I/O, tratteremo solo il +caso classico del terminale. + +Abbiamo già brevemente illustrato in \secref{sec:intro_kern_and_sys} le +modalità con cui il sistema si avvia, e di come, a partire da \cmd{init}, +vengano lanciati tutti gli altri processi. Adesso vedremo in maniera più +dettagliata le modalità con cui il sistema arriva a fornire ad un utente la +shell che gli permette di lanciare i suoi comandi su un terminale. Nella maggior parte delle distribuzioni di GNU/Linux\footnote{fa eccezione la distribuzione \textit{Slackware}, come alcune distribuzioni su dischetto, ed @@ -44,45 +492,53 @@ lanciati, ed in quali modalit anch'esso definito nello stesso file. Tralasciando la descrizione del sistema dei run level, (per il quale si -rimanda alla lettura della pagina di manuale di \cmd{init} e di -\file{inittab}) quello che comunque viene sempre fatto è di lanciare almeno -una istanza di un programma che permetta l'accesso ad un terminale. +rimanda alla lettura delle pagine di manuale di \cmd{init} e di +\file{inittab}) quello che comunque viene sempre fatto è di eseguire almeno +una istanza di un programma che permetta l'accesso ad un terminale. Uno schema +di massima della procedura è riportato in \figref{fig:sess_term_login}. + +\begin{figure}[htb] + \centering + \includegraphics[width=15cm]{img/tty_login} + \caption{Schema della procedura di login su un terminale.} + \label{fig:sess_term_login} +\end{figure} Un terminale, che esso sia un terminale effettivo, attaccato ad una seriale o ad un altro tipo di porta di comunicazione, o una delle console virtuali associate allo schermo, viene sempre visto attraverso attraverso un device driver che ne presenta un'interfaccia comune su un apposito file di -dispositivo. Storicamente i primi terminali erano appunto terminali di -telescriventi (\textit{teletype}), da cui deriva sia il nome dell'interfaccia, -\textit{tty}, che quello dei relativi file di dispositivo, che sono sempre -della forma \texttt{/dev/tty*}. - -Per controllare i terminali si usa di solito il programma \cmd{getty} (od una -delle sue varianti), che permette di mettersi in ascolto sugli stessi. Alla -radice della catena che porta ad una shell per i comandi perciò c'è sempre -\cmd{init} che esegue prima una \func{fork} e poi una \func{exec} per lanciare -una istanza di questo programma su un terminale, il tutto ripetuto per -ciascuno dei terminali che si hanno a disposizione (o per un certo numero di -essi, nel caso delle console virtuali), secondo quanto indicato -dall'amministratore in \file{/etc/inittab}. - -Il programma viene lanciato da \texttt{init} con i privilegi di -amministratore, e con un ambiente vuoto; \cmd{getty} si cura di aprire il -terminale in lettura sullo standard input ed in scrittura sullo standard -output e sullo standard error, di effettuare, qualora servano, ulteriori -settaggi,\footnote{ad esempio, come qualcuno si sarà accorto scrivendo un nome - di login in maiuscolo, può effettuare la conversione in minuscole - automaticamente, ponendosi in una modalità speciale che non distingue fra i - due tipi di caratteri (a beneficio di vecchi terminali che non supportano le - minuscole).} ed infine di stampare un messaggio di benvenuto e porsi in -attesa dell'immissione del nome di un utente. - -Una volta che si sia immesso un nome di login \cmd{getty} esegue il programma -\cmd{login} con una \func{exevle}, passando come argomento la suddetta stringa -ed un ambiente opportunamente costruito che contenga quanto necessario (ad -esempio di solito viene opportunamente inizializzata la variabile di ambiente -\texttt{TERM}) ad identificare il terminale su cui si sta operando, a -beneficio dei programmi che verranno lanciati in seguito. +dispositivo. + +Per controllare un terminale si usa di solito il programma \cmd{getty} (od una +delle sue varianti), che permette di mettersi in ascolto su uno di questi +dispositivi. Alla radice della catena che porta ad una shell per i comandi +perciò c'è sempre \cmd{init} che esegue prima una \func{fork} e poi una +\func{exec} per lanciare una istanza di questo programma su un terminale, il +tutto ripetuto per ciascuno dei terminali che si hanno a disposizione (o per +un certo numero di essi, nel caso delle console virtuali), secondo quanto +indicato dall'amministratore nel file di configurazione del programma, +\file{/etc/inittab}. + +Quando viene lanciato da \cmd{init} il programma parte con i privilegi di +amministratore e con un ambiente vuoto; \cmd{getty} si cura di chiamare +\func{setsid} per creare una nuova sessione ed un nuovo process group, e di +aprire il terminale (che così diventa il terminale di controllo della +sessione) in lettura sullo standard input ed in scrittura sullo standard +output e sullo standard error; inoltre effettuerà, qualora servano, ulteriori +settaggi.\footnote{ad esempio, come qualcuno si sarà accorto scrivendo un nome + di login in maiuscolo, può effettuare la conversione automatica dell'input + in minuscolo, ponendosi in una modalità speciale che non distingue fra i due + tipi di caratteri (a beneficio di alcuni vecchi terminali che non + supportavano le minuscole).} Alla fine il programma stamperà un messaggio di +benvenuto per poi porsi in attesa dell'immissione del nome di un utente. + +Una volta che si sia immesso il nome di login \cmd{getty} esegue direttamente +il programma \cmd{login} con una \func{exevle}, passando come argomento la +stringa con il nome, ed un ambiente opportunamente costruito che contenga +quanto necessario (ad esempio di solito viene opportunamente inizializzata la +variabile di ambiente \texttt{TERM}) ad identificare il terminale su cui si +sta operando, a beneficio dei programmi che verranno lanciati in seguito. A sua volta \cmd{login}, che mantiene i privilegi di amministratore, usa il nome dell'utente per effettuare una ricerca nel database degli @@ -96,85 +552,1471 @@ la password non corrisponde\footnote{il confronto non viene effettuato con un numero di volte dopo di che \cmd{login} esce ed \cmd{init} provvede a rilanciare un'altra istanza di \func{getty}. -Se invece la password corrisponde a questo punto \cmd{login} esegue -\func{chdir} per settare la \textit{home directory} dell'utente, cambia i -diritti di accesso al terminale (con \func{chown} e \func{chmod}) per -assegnarne la titolarità all'utente ed al suo gruppo principale, assegnandogli -al contempo i diritti di lettura e scrittura. Inoltre il programma provvederà -a costruire gli opportuni valori per le variabili di ambiente, come -\texttt{HOME}, \texttt{SHELL}, ecc. Infine attraverso l'uso di \func{setuid}, -\func{setpid} e \func{initgroups} verrà cambiata l'identità del proprietario -del processo, infatti, come spiegato in \secref{sec:proc_setuid}, avendo -invocato tali funzioni con i privilegi di amministratore, tutti gli userid ed -i groupid (reali, effettivi e salvati) saranno settati a quelli dell'utente. +Se invece la password corrisponde \cmd{login} esegue \func{chdir} per settare +la \textit{home directory} dell'utente, cambia i diritti di accesso al +terminale (con \func{chown} e \func{chmod}) per assegnarne la titolarità +all'utente ed al suo gruppo principale, assegnandogli al contempo i diritti di +lettura e scrittura. Inoltre il programma provvede a costruire gli opportuni +valori per le variabili di ambiente, come \texttt{HOME}, \texttt{SHELL}, ecc. +Infine attraverso l'uso di \func{setuid}, \func{setpid} e \func{initgroups} +verrà cambiata l'identità del proprietario del processo, infatti, come +spiegato in \secref{sec:proc_setuid}, avendo invocato tali funzioni con i +privilegi di amministratore, tutti gli user-ID ed i group-ID (reali, effettivi +e salvati) saranno settati a quelli dell'utente. A questo punto \cmd{login} provvederà (fatte salve eventuali altre azioni iniziali, come la stampa di messaggi di benvenuto o il controllo della posta) -ad eseguire con un'altra \func{exec} la shell di login, che si troverà con un -ambiente già pronto e con file standard di \secref{sec:file_std_descr} -impostati sul terminale, pronta ad eseguire i comandi fino all'uscita. Dato -che il processo genitore resta sempre \cmd{init} quest'ultimo provvederà, -ricevendo un \macro{SIGCHLD} all'uscita della shell, a rilanciare \cmd{getty} -per ripetere da capo tutto il procedimento. +ad eseguire con un'altra \func{exec} la shell, che si troverà con un ambiente +già pronto con i file standard di \secref{sec:file_std_descr} impostati sul +terminale, e pronta, nel ruolo di leader di sessione e di processo di +controllo per il terminale, a gestire l'esecuzione dei comandi come illustrato +in \secref{sec:sess_job_control_overview}. +Dato che il processo padre resta sempre \cmd{init} quest'ultimo potrà +provvedere, ricevendo un \const{SIGCHLD} all'uscita della shell quando la +sessione di lavoro è terminata, a rilanciare \cmd{getty} sul terminale per +ripetere da capo tutto il procedimento. -\subsection{Il login via rete} -\label{sec:sess_net_log} -Nel caso di un login via rete la cosa si fa leggermente diversa, in tal caso -infatti non essendo possibile prevedere +\subsection{Prescrizioni per un programma \textit{daemon}} +\label{sec:sess_daemon} -\subsection{Il login attraverso X} -\label{sec:sess_X_log} +Come sottolineato fin da \secref{sec:intro_base_concept}, in un sistema +unix-like tutte le operazioni sono eseguite tramite processi, comprese quelle +operazioni di sistema (come l'esecuzione dei comandi periodici, o la consegna +della posta, ed in generale tutti i programmi di servizio) che non hanno +niente a che fare con la gestione diretta dei comandi dell'utente. -Quanto scritto finora riguardo i terminali è piuttosto diverso quando si ha a -che fare con X. In tal caso infatti la procedura grafica per il login è -gestira da un apposito programma (il cosiddetto \textit{Display Manager}, come -\cmd{xdm}, che fa parte della distribuzione base di X o uno dei suoi molti -cloni) che viene lanciato all'avvio insieme agli altri demoni, e che si cura -di gestire la procedura di login, lanciare eventuali programmi che si vogliono -attivare all'avvio (sia fondamentali, come il gestore delle fineste, che -effimeri, come un notificatore di posta in arrivo). +Questi programmi, che devono essere eseguiti in modalità non interattiva e +senza nessun intervento dell'utente, sono normalmente chiamati +\textsl{demoni}, (o \textit{daemons}), nome ispirato dagli omonimi spiritelli +che svolgevano compiti vari, di cui parlava Socrate (che sosteneva di averne +uno al suo servizio).\footnote{NdT. ricontrollare, i miei ricordi di filosofia + sono piuttosto datati.} -In questo caso q +Se però si lancia un programma demone dalla riga di comando in un sistema che +supporta, come Linux, il \textit{job control} esso verrà comunque associato ad +un terminale di controllo e mantenuto all'interno di una sessione, e anche se +può essere mandato in background e non eseguire più nessun I/O su terminale, +si avranno comunque tutte le conseguenze che abbiamo appena visto in +\secref{sec:sess_ctrl_term} (in particolare l'invio dei segnali in +corrispondenza dell'uscita del leader di sessione). -\section{Il \textit{Job control}} -\label{sec:sess_job_control} +Per questo motivo un programma che deve funzionare come demone deve sempre +prendere autonomamente i provvedimenti opportuni (come distaccarsi dal +terminale e dalla sessione) ad impedire eventuali interferenze da parte del +sistema del \textit{job control}; questi sono riassunti in una lista di +prescrizioni\footnote{ad esempio sia Stevens in \cite{APUE}, che la + \textit{Unix Programming FAQ} \cite{UnixFAQ} ne riportano di sostanzialmente + identiche.} da seguire quando si scrive un demone. -Lo scopo del \textit{Job control} è quello di permettere ad un utente di poter -sfruttare le capacità multitasking di un sistema Unix per eseguire in -contemporanea più processi, pur potendo accedere, di solito, ad un solo -terminale,\footnote{con X e con i terminali vituali tutto questo non è più - vero, dato che si può accedere a molti terminali in contemporanea, ma il - sistema è nato prima dell'esistenza di tutto ciò.} avendo cioè un solo punto -in cui su può avere accesso all'input ed all'output degli stessi. +Pertanto, quando si lancia un programma che deve essere eseguito come demone +occorrerà predisporlo in modo che esso compia le seguenti azioni: +\begin{enumerate} +\item Eseguire una \func{fork} e terminare immediatamente il processo padre + proseguendo l'esecuzione nel figlio. In questo modo si ha la certezza che + il figlio non è un \textit{process group leader}, (avrà il \acr{pgid} del + padre, ma un \acr{pid} diverso) e si può chiamare \func{setsid} con + successo. Inoltre la shell considererà terminato il comando all'uscita del + padre. +\item Eseguire \func{setsid} per creare una nuova sessione ed un nuovo + raggruppamento di cui il processo diventa automaticamente il leader, che + però non ha associato nessun terminale di controllo. +\item Assicurarsi che al processo non venga associato in seguito nessun nuovo + terminale di controllo; questo può essere fatto sia avendo cura di usare + sempre l'opzione \const{O\_NOCTTY} nell'aprire i file di terminale, che + eseguendo una ulteriore \func{fork} uscendo nel padre e proseguendo nel + figlio. In questo caso, non essendo più quest'ultimo un leader di sessione + non potrà ottenere automaticamente un terminale di controllo. +\item Eseguire una \func{chdir} per impostare la directory di lavoro del + processo (su \file{/} o su una directory che contenga dei file necessari per + il programma), per evitare che la directory da cui si è lanciato il processo + resti in uso e non sia possibile rimuoverla o smontare il filesystem che la + contiene. +\item Impostare la maschera dei permessi (di solito con \code{umask(0)}) in + modo da non essere dipendenti dal valore ereditato da chi ha lanciato + originariamente il processo. +\item Chiudere tutti i file aperti che non servono più (in generale tutti); in + particolare vanno chiusi i file standard che di norma sono ancora associati + al terminale (un'altra opzione è quella di redirigerli verso + \file{/dev/null}). +\end{enumerate} +In Linux buona parte di queste azioni possono venire eseguite invocando la +funzione \funcd{daemon}, introdotta per la prima volta in BSD4.4; il suo +prototipo è: +\begin{prototype}{unistd.h}{int daemon(int nochdir, int noclose)} + Esegue le operazioni che distaccano il processo dal terminale di controllo e + lo fanno girare come demone. + + \bodydesc{La funzione restituisce (nel nuovo processo) 0 in caso di + successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà i + valori impostati dalle sottostanti \func{fork} e \func{setsid}.} +\end{prototype} -\subsection{La struttura di base} -\label{sec:sess_relation} +La funzione esegue una \func{fork}, per uscire subito, con \func{\_exit}, nel +padre, mentre l'esecuzione prosegue nel figlio che esegue subito una +\func{setsid}. In questo modo si compiono automaticamente i passi 1 e 2 della +precedente lista. Se \param{nochdir} è nullo la funzione imposta anche la +directory di lavoro su \file{/}, se \param{noclose} è nullo i file standard +vengono rediretti su \file{/dev/null} (corrispondenti ai passi 4 e 6); in caso +di valori non nulli non viene eseguita nessuna altra azione. +Dato che un programma demone non può più accedere al terminale, si pone il +problema di come fare per la notifica di eventuali errori, non potendosi più +utilizzare lo standard error; per il normale I/O infatti ciascun demone avrà +le sue modalità di interazione col sistema e gli utenti a seconda dei compiti +e delle funzionalità che sono sono previste; ma gli errori devono normalmente +essere notificati all'amministratore del sistema. +Una soluzione può essere quella di scrivere gli eventuali messaggi su uno +specifico file (cosa che a volte viene fatta comunque) ma questo comporta il +grande svantaggio che l'amministratore dovrà tenere sotto controllo un file +diverso per ciascun demone, e che possono anche generarsi conflitti di nomi. +Per questo in BSD4.2 venne introdotto un servizio di sistema, il +\textit{syslog}, che oggi si trova su tutti i sistemi Unix, e che permettesse +ai demoni di inviare messaggi all'amministratore in una maniera +standardizzata. +Il servizio prevede vari meccanismi di notifica, e, come ogni altro servizio +in un sistema unix-like, viene gestito attraverso un apposito programma, +\cmd{syslogd}, che è anch'esso un \textsl{demone}. In generale i messaggi di +errore vengono raccolti dal file speciale \file{/dev/log}, un +\textit{socket}\index{socket} locale (vedi \secref{sec:sock_sa_local}) +dedicato a questo scopo, o via rete, con un \textit{socket} UDP, o da un +apposito demone, \cmd{klogd}, che estrae i messaggi del kernel.\footnote{i + messaggi del kernel sono tenuti in un buffer circolare e scritti tramite la + funzione \func{printk}, analoga alla \func{printf} usata in user space; una + trattazione eccellente dell'argomento si trova in \cite{LinDevDri}, nel + quarto capitolo.} -\subsection{I \textit{process group}} -\label{sec:sess_proc_group} +Il servizio permette poi di trattare i vari messaggi classificandoli +attraverso due indici; il primo, chiamato \textit{facility}, suddivide in +diverse categorie i vari demoni in modo di raggruppare i messaggi provenienti +da operazioni che hanno attinenza fra loro, ed è organizzato in sottosistemi +(kernel, posta elettronica, demoni di stampa, ecc.). Il secondo, chiamato +\textit{priority}, identifica l'importanza dei vari messaggi, e permette di +classificarli e differenziare le modalità di notifica degli stessi. +Il sistema di \textit{syslog} attraverso \cmd{syslogd} provvede poi a +riportare i messaggi all'amministratore attraverso una serie differenti +meccanismi come: +\begin{itemize*} +\item scrivere sulla console. +\item inviare via mail ad uno specifico utente. +\item scrivere su un file (comunemente detto \textit{log file}). +\item inviare ad un altro demone (anche via rete). +\item scartare. +\end{itemize*} +secondo le modalità che questo preferisce e che possono essere impostate +attraverso il file di configurazione \file{/etc/syslog.conf} (maggiori +dettagli si possono trovare sulle pagine di manuale per questo file e per +\cmd{syslogd}). +Le \acr{glibc} definiscono una serie di funzioni standard con cui un processo +può accedere in maniera generica al servizio di \textit{syslog}, che però +funzionano solo localmente; se si vogliono inviare i messaggi ad un'altro +sistema occorre farlo esplicitamente con un socket\index{socket} UDP, o +utilizzare le capacità di reinvio del servizio. -\subsection{Le sessioni} -\label{sec:sess_sessions} +La prima funzione definita dall'interfaccia è \funcd{openlog}, che apre una +connessione al servizio di \textit{syslog}; essa in generale non è necessaria +per l'uso del servizio, ma permette di impostare alcuni valori che controllano +gli effetti delle chiamate successive; il suo prototipo è: +\begin{prototype}{syslog.h}{void openlog(const char *ident, int option, +int facility)} +Apre una connessione al sistema di \textit{syslog}. + +\bodydesc{La funzione non restituisce nulla.} +\end{prototype} +La funzione permette di specificare, tramite \param{ident}, l'identità di chi +ha inviato il messaggio (di norma si passa il nome del programma, come +specificato da \code{argv[0]}); la stringa verrà preposta all'inizio di ogni +messaggio. Si tenga presente che il valore di \param{ident} che si passa alla +funzione è un puntatore, se la stringa cui punta viene cambiata lo sarà pure +nei successivi messaggi, e se viene cancellata i risultati potranno essere +impredicibili, per questo è sempre opportuno usare una stringa costante. -\subsection{Il terminale di controllo} -\label{sec:sess_ctrl_term} +L'argomento \param{facility} permette invece di preimpostare per le successive +chiamate l'omonimo indice che classifica la categoria del messaggio. +L'argomento è interpretato come una maschera binaria, e pertanto è possibile +inviare i messaggi su più categorie alla volta; i valori delle costanti che +identificano ciascuna categoria sono riportati in +\tabref{tab:sess_syslog_facility}, il valore di \param{facility} deve essere +specificato con un OR aritmetico. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore}& \textbf{Significato}\\ + \hline + \hline + \const{LOG\_AUTH} & Messaggi relativi ad autenticazione e sicurezza, + obsoleto, è sostituito da \const{LOG\_AUTHPRIV}. \\ + \const{LOG\_AUTHPRIV} & Sostituisce \const{LOG\_AUTH}.\\ + \const{LOG\_CRON} & Messaggi dei demoni di gestione dei comandi + programmati (\cmd{cron} e \cmd{at}).\\ + \const{LOG\_DAEMON} & Demoni di sistema.\\ + \const{LOG\_FTP} & Server FTP.\\ + \const{LOG\_KERN} & Messaggi del kernel\\ + \const{LOG\_LOCAL0} & Riservato all'amministratore per uso locale\\ + --- & \\ + \const{LOG\_LOCAL7} & Riservato all'amministratore per uso locale\\ + \const{LOG\_LPR} & Messaggi del sistema di gestione delle stampanti \\ + \const{LOG\_MAIL} & Messaggi del sistema di posta elettronica\\ + \const{LOG\_NEWS} & Messaggi del sistema di gestione delle news + (USENET) \\ + \const{LOG\_SYSLOG} & Messaggi generati dallo stesso \cmd{syslogd}\\ + \const{LOG\_USER} & Messaggi generici a livello utente\\ + \const{LOG\_UUCP} & Messaggi del sistema UUCP\\ +\hline +\end{tabular} +\caption{Valori possibili per l'argomento \param{facility} di \func{openlog}.} +\label{tab:sess_syslog_facility} +\end{table} + +L'argomento \param{option} serve invece per controllare il comportamento della +funzione \func{openlog} e delle modalità con cui le successive chiamate +scriveranno i messaggi, esso viene specificato come maschera binaria composta +con un OR aritmetico di una qualunque delle costanti riportate in +\tabref{tab:sess_openlog_option}. + +\begin{table}[htb] + \footnotesize +\centering +\begin{tabular}[c]{|l|p{8cm}|} +\hline +\textbf{Valore}& \textbf{Significato}\\ +\hline +\hline +\const{LOG\_CONS} & Scrive sulla console quando. \\ +\const{LOG\_NDELAY} & Sostituisce \const{LOG\_AUTH}.\\ +\const{LOG\_NOWAIT} & Messaggi dei demoni di gestione dei comandi + programmati (\cmd{cron} e \cmd{at}).\\ +\const{LOG\_ODELAY} & .\\ +\const{LOG\_PERROR} & Stampa anche su \file{stderr}.\\ +\const{LOG\_PID} & Inserisce nei messaggi il \acr{pid} del processo + chiamante. \\ +\hline +\end{tabular} +\caption{Valori possibili per l'argomento \param{option} di \func{openlog}.} +\label{tab:sess_openlog_option} +\end{table} + +La funzione che si usa per generare un messaggio è \funcd{syslog}, dato che +l'uso di \func{openlog} è opzionale, sarà quest'ultima a provvede a chiamare la +prima qualora ciò non sia stato fatto (nel qual caso il valore di +\param{ident} è nullo). Il suo prototipo è: +\begin{prototype}{syslog.h} +{void syslog(int priority, const char *format, ...)} + +Genera un messaggio di priorità \param{priority}. + +\bodydesc{La funzione non restituisce nulla.} +\end{prototype} + +Il comportamento della funzione è analogo quello di \func{printf}, e il valore +dell'argomento \param{format} è identico a quello descritto nella pagina di +manuale di quest'ultima (per i valori principali si può vedere la trattazione +sommaria che se ne è fatto in \secref{sec:file_formatted_io}); l'unica +differenza è che la sequenza \val{\%m} viene rimpiazzata dalla stringa +restituita da \code{strerror(errno)}. Gli argomenti seguenti i primi due +devono essere forniti secondo quanto richiesto da \param{format}. + +L'argomento \param{priority} permette di impostare sia la \textit{facility} +che la \textit{priority} del messaggio. In realtà viene prevalentemente usato +per specificare solo quest'ultima in quanto la prima viene di norma +preimpostata con \func{openlog}. La priorità è indicata con un valore +numerico\footnote{le \acr{glibc}, seguendo POSIX.1-2001, prevedono otto + diverse priorità ordinate da 0 a 7, in ordine di importanza decrescente; + questo comporta che i tre bit meno significativi dell'argomento + \param{priority} sono occupati da questo valore, mentre i restanti bit più + significativi vengono usati per specificare la \textit{facility}.} +specificabile attraverso le costanti riportate in +\secref{tab:sess_syslog_priority}. Nel caso si voglia specificare anche la +\textit{facility} basta eseguire un OR aritmetico del valore della priorità +con la maschera binaria delle costanti di \tabref{tab:sess_syslog_facility}. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore}& \textbf{Significato}\\ + \hline + \hline + \const{LOG\_EMERG} & Il sistema è inutilizzabile. \\ + \const{LOG\_ALERT} & C'è una emergenza che richiede intervento + immediato.\\ + \const{LOG\_CRIT} & Si è in una condizione critica.\\ + \const{LOG\_ERR} & Si è in una condizione di errore.\\ + \const{LOG\_WARNING} & Messaggio di avvertimento.\\ + \const{LOG\_NOTICE} & Notizia significativa relativa al comportamento.\\ + \const{LOG\_INFO} & Messaggio informativo. \\ + \const{LOG\_DEBUG} & Messaggio di debug.\\ + \hline + \end{tabular} + \caption{Valori possibili per l'indice di importanza del messaggio da + specificare nell'argomento \param{priority} di \func{syslog}.} + \label{tab:sess_syslog_priority} +\end{table} + +Una ulteriore funzione, \funcd{setlogmask}, permette di filtrare +preliminarmente i messaggi in base alla loro priorità; il suo prototipo è: +\begin{prototype}{syslog.h}{int setlogmask(int mask)} + +Imposta la maschera dei log al valore specificato. + +\bodydesc{La funzione restituisce il precedente valore.} +\end{prototype} + +Le routine di gestione mantengono per ogni processo una maschera che determina +quale delle chiamate effettuate a \func{syslog} verrà effettivamente +registrata. La registrazione viene disabilitata per tutte quelle priorità che +non rientrano nella maschera; questa viene settata usando la macro +\macro{LOG\_MASK(p)} dove \code{p} è una delle costanti di +\secref{tab:sess_syslog_priority}. É inoltre disponibile anche la macro +\macro{LOG\_UPTO(p)} che permette di specificare automaticamente tutte le +priorità fino ad un certo valore. + + + +\section{L'I/O su terminale} +\label{sec:sess_terminal_io} + +Benché come ogni altro dispositivo i terminali siano accessibili come file, +essi hanno assunto storicamente (essendo stati a lungo l'unico modo di +accedere al sistema) una loro rilevanza specifica, che abbiamo già avuto modo +di incontrare nella precedente sezione. + +Esamineremo qui le peculiarità dell'I/O eseguito sui terminali, che per la +loro particolare natura presenta delle differenze rispetto ai normali file su +disco e agli altri dispositivi. + + + +\subsection{L'architettura} +\label{sec:term_design} + +I terminali sono una classe speciale di dispositivi a caratteri (si ricordi la +classificazione di \secref{sec:file_file_types}); un terminale ha infatti una +caratteristica che lo contraddistingue da un qualunque altro dispositivo, e +cioè che è destinato a gestire l'interazione con un utente (deve essere cioè +in grado di fare da terminale di controllo per una sessione), che comporta la +presenza di ulteriori capacità. + +L'interfaccia per i terminali è una delle più oscure e complesse, essendosi +stratificata dagli inizi dei sistemi Unix fino ad oggi. Questo comporta una +grande quantità di opzioni e controlli relativi ad un insieme di +caratteristiche (come ad esempio la velocità della linea) necessarie per +dispositivi, come i terminali seriali, che al giorno d'oggi sono praticamente +in disuso. + +Storicamente i primi terminali erano appunto terminali di telescriventi +(\textit{teletype}), da cui deriva sia il nome dell'interfaccia, \textit{TTY}, +che quello dei relativi file di dispositivo, che sono sempre della forma +\texttt{/dev/tty*}.\footnote{ciò vale solo in parte per i terminali virtuali, + essi infatti hanno due lati, un \textit{master}, che può assumere i nomi + \file{/dev/pty[p-za-e][0-9a-f]} ed un corrispondente \textit{slave} con nome + \file{/dev/tty[p-za-e][0-9a-f]}.} Oggi essi includono le porte seriali, le +console virtuali dello schermo, i terminali virtuali che vengono creati come +canali di comunicazione dal kernel e che di solito vengono associati alle +connessioni di rete (ad esempio per trattare i dati inviati con \cmd{telnet} o +\cmd{ssh}). + +L'I/O sui terminali si effettua con le stesse modalità dei file normali: si +apre il relativo file di dispositivo, e si leggono e scrivono i dati con le +usuali funzioni di lettura e scrittura, così se apriamo una console virtuale +avremo che \func{read} leggerà quanto immesso dalla tastiera, mentre +\func{write} scriverà sullo schermo. In realtà questo è vero solo a grandi +linee, perché non tiene conto delle caratteristiche specifiche dei terminali; +una delle principali infatti è che essi prevedono due modalità di operazione, +dette rispettivamente \textsl{modo canonico} e \textsl{modo non canonico}, che +comportano dei comportamenti nettamente diversi. + +La modalità preimpostata all'apertura del terminale è quella canonica, in cui +le operazioni di lettura vengono sempre effettuate assemblando i dati in una +linea;\footnote{per cui eseguendo una \func{read} su un terminale in modo + canonico la funzione si bloccherà, anche se si sono scritti dei caratteri, + fintanto che non si preme il tasto di ritorno a capo: a questo punto la + linea sarà completa e la funzione ritornerà.} ed in cui alcuni caratteri +vengono interpretati per compiere operazioni (come la generazione dei segnali +illustrati in \secref{sec:sig_job_control}), questa di norma è la modalità in +cui funziona la shell. + +Un terminale in modo non canonico invece non effettua nessun accorpamento dei +dati in linee né li interpreta; esso viene di solito usato dai programmi (gli +editor ad esempio) che necessitano di poter leggere un carattere alla volta e +che gestiscono al loro interno i vari comandi. + +Per capire le caratteristiche dell'I/O sui terminali, occorre esaminare le +modalità con cui esso viene effettuato; l'accesso, come per tutti i +dispositivi, viene gestito da un driver apposito, la cui struttura generica è +mostrata in \secref{fig:term_struct}. Ad un terminale sono sempre associate +due code per gestire l'input e l'output, che ne implementano una +bufferizzazione\footnote{completamente indipendente dalla eventuale ulteriore + bufferizzazione fornita dall'interfaccia standard dei file.} all'interno del +kernel. + +\begin{figure}[htb] + \centering \includegraphics[width=13cm]{img/term_struct} + \caption{Struttura interna generica di un driver per un terminale.} + \label{fig:term_struct} +\end{figure} + +La coda di ingresso mantiene i caratteri che sono stati letti dal terminale ma +non ancora letti da un processo, la sua dimensione è definita dal parametro di +sistema \const{MAX\_INPUT} (si veda \secref{sec:sys_file_limits}), che ne +specifica il limite minimo, in realtà la coda può essere più grande e cambiare +dimensione dinamicamente. Se è stato abilitato il controllo di flusso in +ingresso il driver emette i caratteri di STOP e START per bloccare e sbloccare +l'ingresso dei dati; altrimenti i caratteri immessi oltre le dimensioni +massime vengono persi; in alcuni casi il driver provvede ad inviare +automaticamente un avviso (un carattere di BELL, che provoca un beep) +sull'output quando si eccedono le dimensioni della coda. Se è abilitato il +modo canonico i caratteri in ingresso restano nella coda fintanto che non +viene ricevuto un a capo; un'altra parametro del sistema, \const{MAX\_CANON}, +specifica la dimensione massima di una riga in modo canonico. + +La coda di uscita è analoga a quella di ingresso e contiene i caratteri +scritti dai processi ma non ancora inviati al terminale. Se è abilitato il +controllo di flusso in uscita il driver risponde ai caratteri di START e STOP +inviati dal terminale. Le dimensioni della coda non sono specificate, ma non +hanno molta importanza, in quanto qualora esse vengano eccedute il driver +provvede automaticamente a bloccare la funzione chiamante. + + + +\subsection{La gestione delle caratteristiche di un terminale} +\label{sec:term_attr} + +Data le loro peculiarità, fin dall'inizio si è posto il problema di come +gestire le caratteristiche specifiche dei terminali; storicamente i vari +dialetti di Unix hanno utilizzato diverse funzioni, alla fine con POSIX.1, è +stata effettuata una standardizzazione, unificando le differenze fra BSD e +System V in una unica interfaccia, che è quella usata dal Linux. + +Alcune di queste funzioni prendono come argomento un file descriptor (in +origine molte operazioni venivano effettuate con \func{ioctl}), ma ovviamente +possono essere usate solo con file che corrispondano effettivamente ad un +terminale (altrimenti si otterrà un errore di \errcode{ENOTTY}); questo può +essere evitato utilizzando la funzione \funcd{isatty}, il cui prototipo è: +\begin{prototype}{unistd.h}{int isatty(int desc)} + + Controlla se il file descriptor \param{desc} è un terminale. + +\bodydesc{La funzione restituisce 1 se \param{desc} è connesso ad un + terminale, 0 altrimenti.} +\end{prototype} + +Un'altra funzione che fornisce informazioni su un terminale è \funcd{ttyname}, +che permette di ottenere il nome del terminale associato ad un file +descriptor; il suo prototipo è: +\begin{prototype}{unistd.h}{char *ttyname(int desc)} + + Restituisce il nome del terminale associato al file \param{desc}. + + \bodydesc{La funzione restituisce il puntatore alla stringa contenente il + nome del terminale associato \param{desc} e \val{NULL} in caso di + errore.} +\end{prototype} + +Si tenga presente che la funzione restituisce un indirizzo di dati statici, +che pertanto possono essere sovrascritti da successive chiamate. Una funzione +funzione analoga, anch'essa prevista da POSIX.1, è \funcd{ctermid}, il cui +prototipo è: +\begin{prototype}{stdio.h}{char *ctermid(char *s)} + + Restituisce il nome del terminale di controllo del processo. + + \bodydesc{La funzione restituisce il puntatore alla stringa contenente il + pathname del terminale.} +\end{prototype} + +La funzione scrive il pathname del terminale di controllo del processo +chiamante nella stringa posta all'indirizzo specificato dall'argomento +\param{s}. La memoria per contenere la stringa deve essere stata allocata in +precedenza ed essere lunga almeno +\const{L\_ctermid}\footnote{\const{L\_ctermid} è una delle varie costanti del + sistema, non trattata esplicitamente in \secref{sec:sys_characteristics} che + indica la dimensione che deve avere una stringa per poter contenere il nome + di un terminale.} caratteri. + +Esiste infine una versione rientrante \funcd{ttyname\_r} della funzione +\func{ttyname}, che non presenta il problema dell'uso di una zona di memoria +statica; il suo prototipo è: +\begin{prototype}{unistd.h}{int ttyname\_r(int desc, char *buff, size\_t len)} + + Restituisce il nome del terminale associato al file \param{desc}. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{ERANGE}] la lunghezza del buffer, \param{len}, non è + sufficiente per contenere la stringa restituita. + \end{errlist} + ed inoltre \errval{EBADF} ed \errval{ENOSYS}. +} +\end{prototype} + +La funzione prende due argomenti, il puntatore alla zona di memoria +\param{buff}, in cui l'utente vuole che il risultato venga scritto (dovrà +ovviamente essere stata allocata in precedenza), e la relativa dimensione, +\param{len}; se la stringa che deve essere restituita eccede questa dimensione +si avrà una condizione di errore. + +Se si passa come argomento \val{NULL} la funzione restituisce il puntatore +ad una stringa statica che può essere sovrascritta da chiamate successive. Si +tenga presente che il pathname restituito potrebbe non identificare +univocamente il terminale (ad esempio potrebbe essere \file{/dev/tty}), +inoltre non è detto che il processo possa effettivamente aprire il terminale. + +I vari attributi vengono mantenuti per ciascun terminale in una struttura +\struct{termios}, (la cui definizione è riportata in +\figref{fig:term_termios}), usata dalle varie funzioni dell'interfaccia. In +\figref{fig:term_termios} si sono riportati tutti i campi della definizione +usata in Linux; di questi solo i primi cinque sono previsti dallo standard +POSIX.1, ma le varie implementazioni ne aggiungono degli altri per mantenere +ulteriori informazioni.\footnote{la definizione della struttura si trova in + \file{bits/termios.h}, da non includere mai direttamente, Linux, seguendo + l'esempio di BSD, aggiunge i due campi \var{c\_ispeed} e \var{c\_ospeed} per + mantenere le velocità delle linee seriali, ed un campo ulteriore, + \var{c\_line} per ... (NdT, trovare a che serve).} + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{} +struct termios { + tcflag_t c_iflag; /* input modes */ + tcflag_t c_oflag; /* output modes */ + tcflag_t c_cflag; /* control modes */ + tcflag_t c_lflag; /* local modes */ + cc_t c_cc[NCCS]; /* control characters */ + cc_t c_line; /* line discipline */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \structd{termios}, che identifica le proprietà di un + terminale.} + \label{fig:term_termios} +\end{figure} + +I primi quattro campi sono quattro flag che controllano il comportamento del +terminale; essi sono realizzati come maschera binaria, pertanto il tipo +\type{tcflag\_t} è di norma realizzato con un intero senza segno di lunghezza +opportuna. I valori devono essere specificati bit per bit, avendo cura di non +modificare i bit su cui non si interviene. + +\begin{table}[b!ht] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{11cm}|} + \hline + \textbf{Valore}& \textbf{Significato}\\ + \hline + \hline + \const{INPCK} & Abilita il controllo di parità in ingresso. Se non viene + impostato non viene fatto nessun controllo ed i caratteri + vengono passati in input direttamente.\\ + \const{IGNPAR} & Ignora gli errori di parità, il carattere viene passato + come ricevuto. Ha senso solo se si è impostato + \const{INPCK}.\\ + \const{PARMRK} & Controlla come vengono riportati gli errori di parità. Ha + senso solo se \const{INPCK} è impostato e \const{IGNPAR} + no. Se impostato inserisce una sequenza \texttt{0xFF + 0x00} prima di ogni carattere che presenta errori di + parità, se non impostato un carattere con errori di + parità viene letto come uno \texttt{0x00}. Se un + carattere ha il valore \texttt{0xFF} e \const{ISTRIP} + non è settato, per evitare ambiguità esso viene sempre + riportato come \texttt{0xFF 0xFF}.\\ + \const{ISTRIP} & Se impostato i caratteri in input sono tagliati a sette + bit mettendo a zero il bit più significativo, altrimenti + vengono passati tutti gli otto bit.\\ + \const{IGNBRK} & Ignora le condizioni di BREAK sull'input. Una + \textit{condizione di BREAK} è definita nel contesto di + una trasmissione seriale asincrona come una sequenza di + bit nulli più lunga di un byte. \\ + \const{BRKINT} & Controlla la reazione ad un BREAK quando + \const{IGNBRK} non è impostato. Se \const{BRKINT} è + impostato il BREAK causa lo scarico delle code, + e se il terminale è il terminale di controllo per un + gruppo in foreground anche l'invio di \const{SIGINT} ai + processi di quest'ultimo. Se invece \const{BRKINT} non è + impostato un BREAK viene letto come un carattere + NUL, a meno che non sia settato \const{PARMRK} + nel qual caso viene letto come la sequenza di caratteri + \texttt{0xFF 0x00 0x00}.\\ + \const{IGNCR} & Se impostato il carattere di ritorno carrello + (\textit{carriage return}, \verb|'\r'|) viene scartato + dall'input. Può essere utile per i terminali che inviano + entrambi i caratteri di ritorno carrello e a capo + (\textit{newline}, \verb|'\n'|). \\ + \const{ICRNL} & Se impostato un carattere di ritorno carrello + (\verb|'\r'|) sul terminale viene automaticamente + trasformato in un a capo (\verb|'\n'|) sulla coda di + input. \\ + \const{INLCR} & Se impostato il carattere di a capo + (\verb|'\n'|) viene automaticamente trasformato in un + ritorno carrello (\verb|'\r'|).\\ + \const{IUCLC} & Se impostato trasforma i caratteri maiuscoli dal + terminale in minuscoli sull'ingresso (opzione non + POSIX).\\ + \const{IXON} & Se impostato attiva il controllo di flusso in uscita con i + caratteri di START e STOP. se si riceve + uno STOP l'output viene bloccato, e viene fatto + ripartire solo da uno START, e questi due + caratteri non vengono passati alla coda di input. Se non + impostato i due caratteri sono passati alla coda di input + insieme agli altri.\\ + \const{IXANY} & Se impostato con il controllo di flusso permette a + qualunque carattere di far ripartire l'output bloccato da + un carattere di STOP.\\ + \const{IXOFF} & Se impostato abilita il controllo di flusso in + ingresso. Il computer emette un carattere di STOP per + bloccare l'input dal terminale e lo sblocca con il + carattere START. \\ + \const{IMAXBEL}& Se impostato fa suonare il cicalino se si riempie la cosa + di ingresso; in Linux non è implementato e il kernel si + comporta cose se fosse sempre settato (è una estensione + BSD). \\ + \hline + \end{tabular} + \caption{Costanti identificative dei vari bit del flag di controllo + \var{c\_iflag} delle modalità di input di un terminale.} + \label{tab:sess_termios_iflag} +\end{table} + +Il primo flag, mantenuto nel campo \var{c\_iflag}, è detto \textsl{flag di + input} e controlla le modalità di funzionamento dell'input dei caratteri sul +terminale, come il controllo di parità, il controllo di flusso, la gestione +dei caratteri speciali; un elenco dei vari bit, del loro significato e delle +costanti utilizzate per identificarli è riportato in +\tabref{tab:sess_termios_iflag}. + +Si noti come alcuni di questi flag (come quelli per la gestione del flusso) +fanno riferimento a delle caratteristiche che ormai sono completamente +obsolete; la maggior parte inoltre è tipica di terminali seriali, e non ha +alcun effetto su dispositivi diversi come le console virtuali o gli +pseudo-terminali usati nelle connessioni di rete. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{11cm}|} + \hline + \textbf{Valore}& \textbf{Significato}\\ + \hline + \hline + \const{OPOST} & Se impostato i caratteri vengono convertiti opportunamente + (in maniera dipendente dall'implementazione) per la + visualizzazione sul terminale, ad esempio al + carattere di a capo (NL) può venire aggiunto un ritorno + carrello (CR).\\ + \const{OCRNL} & Se impostato converte automaticamente il carattere di a + capo (NL) nella coppia di caratteri ritorno carrello, a + capo (CR-NL).\\ + \const{OLCUC} & Se impostato trasforma i caratteri minuscoli in ingresso + in caratteri maiuscoli sull'uscita (non previsto da + POSIX.1).\\ + \const{ONLCR} & Se impostato converte automaticamente il carattere di a + capo (NL) in un carattere di ritorno carrello (CR).\\ + \const{ONOCR} & Se impostato converte il carattere di ritorno carrello + (CR) nella coppia di caratteri CR-NL.\\ + \const{ONLRET}& Se impostato rimuove dall'output il carattere di ritorno + carrello (CR).\\ + \const{OFILL} & Se impostato in caso di ritardo sulla linea invia dei + caratteri di riempimento invece di attendere.\\ + \const{OFDEL} & Se impostato il carattere di riempimento è DEL + (\texttt{0x3F}), invece che NUL (\texttt{0x00}).\\ + \const{NLDLY} & Maschera per i bit che indicano il ritardo per il + carattere di a capo (NL), i valori possibili sono + \val{NL0} o \val{NL1}.\\ + \const{CRDLY} & Maschera per i bit che indicano il ritardo per il + carattere ritorno carrello (CR), i valori possibili sono + \val{CR0}, \val{CR1}, \val{CR2} o \val{CR3}.\\ + \const{TABDLY}& Maschera per i bit che indicano il ritardo per il + carattere di tabulazione, i valori possibili sono + \val{TAB0}, \val{TAB1}, \val{TAB2} o \val{TAB3}.\\ + \const{BSDLY} & Maschera per i bit che indicano il ritardo per il + carattere di ritorno indietro (\textit{backspace}), i + valori possibili sono \val{BS0} o \val{BS1}.\\ + \const{VTDLY} & Maschera per i bit che indicano il ritardo per il + carattere di tabulazione verticale, i valori possibili sono + \val{VT0} o \val{VT1}.\\ + \const{FFDLY} & Maschera per i bit che indicano il ritardo per il + carattere di pagina nuova (\textit{form feed}), i valori + possibili sono \val{FF0} o \val{FF1}.\\ + \hline + \end{tabular} + \caption{Costanti identificative dei vari bit del flag di controllo + \var{c\_oflag} delle modalità di output di un terminale.} + \label{tab:sess_termios_oflag} +\end{table} + +Il secondo flag, mantenuto nel campo \var{c\_oflag}, è detto \textsl{flag di + output} e controlla le modalità di funzionamento dell'output dei caratteri, +come l'impacchettamento dei caratteri sullo schermo, la traslazione degli a +capo, la conversione dei caratteri speciali; un elenco dei vari bit, del loro +significato e delle costanti utilizzate per identificarli è riportato in +\tabref{tab:sess_termios_oflag}. + +Si noti come alcuni dei valori riportati in \tabref{tab:sess_termios_oflag} +fanno riferimento a delle maschere di bit; essi infatti vengono utilizzati per +impostare alcuni valori numerici relativi ai ritardi nell'output di alcuni +caratteri: una caratteristica originaria dei primi terminali su telescrivente, +che avevano bisogno di tempistiche diverse per spostare il carrello in +risposta ai caratteri speciali, e che oggi sono completamente in disuso. + +Si tenga presente inoltre che nel caso delle maschere il valore da inserire in +\var{c\_oflag} deve essere fornito avendo cura di cancellare prima tutti i bit +della maschera, i valori da immettere infatti (quelli riportati nella +spiegazione corrispondente) sono numerici e non per bit, per cui possono +sovrapporsi fra di loro. Occorrerà perciò utilizzare un codice del tipo: + +\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}% + c_oflag &= (~CRDLY); + c_oflag |= CR1; +\end{lstlisting} + +\noindent che prima cancella i bit della maschera in questione e poi setta il +valore. + + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{11cm}|} + \hline + \textbf{Valore}& \textbf{Significato}\\ + \hline + \hline + \const{CLOCAL} & Se impostato indica che il terminale è connesso in locale + e che le linee di controllo del modem devono essere + ignorate. Se non impostato effettuando una chiamata ad + \func{open} senza aver specificato il flag di + \const{O\_NOBLOCK} si bloccherà il processo finché + non si è stabilita una connessione con il modem; inoltre + se viene rilevata una disconessione viene inviato un + \const{SIGHUP} al processo di controllo del terminale. La + lettura su un terminale sconnesso comporta una condizione + di \textit{end of file} e la scrittura un errore di + \errcode{EIO}. \\ + \const{HUPCL} & Se è impostato viene distaccata la connessione del + modem quando l'ultimo dei processi che ha ancora un file + aperto sul terminale lo chiude o esce.\\ + \const{CREAD} & Se è impostato si può leggere l'input del terminale, + altrimenti i caratteri in ingresso vengono scartati + quando arrivano.\\ + \const{CSTOPB} & Se impostato vengono usati due bit di stop sulla linea + seriale, se non impostato ne viene usato soltanto uno.\\ + \const{PARENB} & Se impostato abilita la generazione il controllo di + parità. La reazione in caso di errori dipende dai + relativi valori per \var{c\_iflag}, riportati in + \tabref{tab:sess_termios_iflag}. Se non è impostato i bit + di parità non vengono + generati e i caratteri non vengono controllati.\\ + \const{PARODD} & Ha senso solo se è attivo anche \const{PARENB}. Se + impostato viene usata una parità è dispari, altrimenti + viene usata una parità pari.\\ + \const{CSIZE} & Maschera per i bit usati per specificare la dimensione + del carattere inviato lungo la linea di trasmissione, i + valore ne indica la lunghezza (in bit), ed i valori + possibili sono \val{CS5}, \val{CS6}, + \val{CS7} e \val{CS8} + corrispondenti ad un analogo numero di bit.\\ + \const{CBAUD} & Maschera dei bit (4+1) usati per impostare della velocità + della linea (il \textit{baud rate}) in ingresso. + In Linux non è implementato in quanto viene + usato un apposito campo di \struct{termios}.\\ + \const{CBAUDEX}& Bit aggiuntivo per l'impostazione della velocità della + linea, per le stesse motivazioni del precedente non è + implementato in Linux.\\ + \const{CIBAUD} & Maschera dei bit della velocità della linea in + ingresso. Analogo a \const{CBAUD}, anch'esso in Linux è + mantenuto in un apposito campo di \struct{termios}. \\ + \const{CRTSCTS}& Abilita il controllo di flusso hardware sulla seriale, + attraverso l'utilizzo delle dei due fili di RTS e CTS.\\ + \hline + \end{tabular} + \caption{Costanti identificative dei vari bit del flag di controllo + \var{c\_cflag} delle modalità di controllo di un terminale.} + \label{tab:sess_termios_cflag} +\end{table} + +Il terzo flag, mantenuto nel campo \var{c\_cflag}, è detto \textsl{flag di + controllo} ed è legato al funzionamento delle linee seriali, permettendo di +impostarne varie caratteristiche, come il numero di bit di stop, i settaggi +della parità, il funzionamento del controllo di flusso; esso ha senso solo per +i terminali connessi a linee seriali. Un elenco dei vari bit, del loro +significato e delle costanti utilizzate per identificarli è riportato in +\tabref{tab:sess_termios_cflag}. + +I valori di questo flag sono molto specifici, e completamente indirizzati al +controllo di un terminale mantenuto su una linea seriale; essi pertanto non +hanno nessuna rilevanza per i terminali che usano un'altra interfaccia, come +le console virtuali e gli pseudo-terminali usati dalle connessioni di rete. + +Inoltre alcuni valori sono previsti solo per quelle implementazioni (lo +standard POSIX non specifica nulla riguardo l'implementazione, ma solo delle +funzioni di lettura e scrittura) che mantengono le velocità delle linee +seriali all'interno dei flag; come accennato in Linux questo viene fatto +(seguendo l'esempio di BSD) attraverso due campi aggiuntivi, \var{c\_ispeed} e +\var{c\_ospeed}, nella struttura \struct{termios} (mostrati in +\figref{fig:term_termios}). + +\begin{table}[b!ht] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{11cm}|} + \hline + \textbf{Valore}& \textbf{Significato}\\ + \hline + \hline + \const{ICANON} & Se impostato il terminale opera in modo canonico, + altrimenti opera in modo non canonico.\\ + \const{ECHO} & Se è impostato viene attivato l'eco dei caratteri in + input sull'output del terminale.\\ + \const{ECHOE} & Se è impostato l'eco mostra la cancellazione di un + carattere in input (in reazione al carattere ERASE) + cancellando l'ultimo carattere della riga corrente dallo + schermo; altrimenti il carattere è rimandato in eco per + mostrare quanto accaduto (usato per i terminali con + l'uscita su una stampante). \\ + \const{ECHOPRT}& Se impostato abilita la visualizzazione del carattere di + cancellazione in una modalità adatta ai terminali con + l'uscita su stampante; l'invio del carattere di ERASE + comporta la stampa di un \verb|\| seguito dal carattere + cancellato, e così via in caso di successive + cancellazioni, quando si riprende ad immettere carattere + normali prima verrà stampata una \texttt{/}.\\ + \const{ECHOK} & Se impostato abilita il trattamento della visualizzazione + del carattere KILL, andando a capo dopo aver visualizzato + lo stesso, altrimenti viene solo mostrato il carattere e + sta all'utente ricordare che l'input precedente è stato + cancellato. \\ + \const{ECHOKE} & Se impostato abilita il trattamento della visualizzazione + del carattere KILL cancellando i caratteri precedenti + nella linea secondo le modalità specificate dai valori di + \const{ECHOE} e \const{ECHOPRT}.\\ + \const{ECHONL} & Se impostato viene effettuato l'eco di un a + capo (\verb|\n|) anche se non è stato impostato + \const{ECHO}. \\ + \const{ECHOCTL}& Se impostato insieme ad \const{ECHO} i caratteri di + controllo ASCII (tranne TAB, NL, START, e STOP) sono + mostrati nella forma che prepende un \verb|^| alla + lettera ottenuta sommando \texttt{0x40} al valore del + carattere (di solito questi si possono ottenere anche + direttamente premendo il tasto \texttt{ctrl} più la + relativa lettera).\\ + \const{ISIG} & Se impostato abilita il riconoscimento dei caratteri + INTR, QUIT, e SUSP generando il relativo segnale.\\ + \const{IEXTEN} & Abilita alcune estensioni previste dalla + implementazione. Deve essere impostato perché caratteri + speciali come EOL2, LNEXT, REPRINT e WERASE possano + essere interpretati. \\ + \const{NOFLSH} & Se impostato disabilita lo scarico delle code di ingresso + e uscita quando vengono emessi i segnali \const{SIGINT}, + \const{SIGQUIT} and \const{SIGSUSP}.\\ + \const{TOSTOP} & Se abilitato, con il supporto per il job control presente, + genera il segnale \const{SIGTTOU} per un processo in + background che cerca di scrivere sul terminale.\\ + \const{XCASE} & Se settato il terminale funziona solo con le + maiuscole. L'input è convertito in minuscole tranne per i + caratteri preceduti da una \verb|\|. In output le + maiuscole sono precedute da una \verb|\| e le minuscole + convertite in maiuscole.\\ + \const{DEFECHO}& Se impostate effettua l'eco solo se c'è un processo in + lettura.\\ + \const{FLUSHO} & Effettua la cancellazione della coda di uscita. Viene + attivato dal carattere DISCARD. Non è supportato in + Linux.\\ + \const{PENDIN} & Indica che la linea deve essere ristampata, viene + attivato dal carattere REPRINT e resta attivo fino alla + fine della ristampa. Non è supportato in Linux.\\ + \hline + \end{tabular} + \caption{Costanti identificative dei vari bit del flag di controllo + \var{c\_lflag} delle modalità locali di un terminale.} + \label{tab:sess_termios_lflag} +\end{table} + +Il quarto flag, mantenuto nel campo \var{c\_lflag}, è detto \textsl{flag + locale}, e serve per controllare il funzionamento dell'interfaccia fra il +driver e l'utente, come abilitare l'eco, gestire i caratteri di controllo e +l'emissione dei segnali, impostare modo canonico o non canonico; un elenco dei +vari bit, del loro significato e delle costanti utilizzate per identificarli è +riportato in \tabref{tab:sess_termios_lflag}. Con i terminali odierni l'unico +flag con cui probabilmente si può avere a che fare è questo, in quanto è con +questo che si impostano le caratteristiche generiche comuni a tutti i +terminali. + +Si tenga presente che i flag che riguardano le modalità di eco dei caratteri +(\const{ECHOE}, \const{ECHOPRT}, \const{ECHOK}, \const{ECHOKE}, +\const{ECHONL}) controllano solo il comportamento della visualizzazione, il +riconoscimento dei vari caratteri dipende dalla modalità di operazione, ed +avviene solo in modo canonico, pertanto questi flag non hanno significato se +non è impostato \const{ICANON}. + +Oltre ai vari flag per gestire le varie caratteristiche dei terminali, +\struct{termios} contiene pure il campo \var{c\_cc} che viene usato per +impostare i caratteri speciali associati alle varie funzioni di controllo. Il +numero di questi caratteri speciali è indicato dalla costante \const{NCCS}, +POSIX ne specifica almeno 11, ma molte implementazioni ne definiscono molti +altri.\footnote{in Linux il valore della costante è 32, anche se i caratteri + effettivamente definiti sono solo 17.} + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|c|c|p{8cm}|} + \hline + \textbf{Indice} & \textbf{Valore}&\textbf{Codice} & \textbf{Funzione}\\ + \hline + \hline + \const{VINTR} &\texttt{0x03}&(\verb|C-c|)& Carattere di interrupt, + provoca l'emissione di + \const{SIGINT}. \\ + \const{VQUIT} &\texttt{0x1C}&(\verb|C-\|)& Carattere di uscita provoca + l'emissione di + \const{SIGQUIT}.\\ + \const{VERASE} &\texttt{0x7f}& DEL & Carattere di ERASE, cancella + l'ultimo carattere precedente + nella linea.\\ + \const{VKILL} &\texttt{0x15}&(\verb|C-u|)& Carattere di KILL, cancella + l'intera riga.\\ + \const{VEOF} &\texttt{0x04}&(\verb|C-d|)& Carattere di + \textit{end-of-file}. Causa + l'invio del contenuto del + buffer di ingresso al + processo in lettura anche se + non è ancora stato ricevuto + un a capo. Se è il primo + carattere immesso comporta il + ritorno di \func{read} con + zero caratteri, cioè la + condizione di + \textit{end-of-file}.\\ + \const{VTIME} & --- & --- & Timeout, in decimi di secondo, per + una lettura in modo non canonico. \\ + \const{VMIN} & --- & --- & Numero minimo di caratteri per una + lettura in modo non canonico.\\ + \const{VSWTC} &\texttt{0x00}& NUL & Carattere di switch. Non supportato + in Linux.\\ + \const{VSTART} &\texttt{0x21}&(\verb|C-q|)& Carattere di START. Riavvia un + output bloccato da uno STOP.\\ + \const{VSTOP} &\texttt{0x23}&(\verb|C-s|)& Carattere di STOP. Blocca + l'output fintanto che non + viene premuto un carattere di + START.\\ + \const{VSUSP} &\texttt{0x1A}&(\verb|C-z|)& Carattere di + sospensione. Invia il segnale + \const{SIGTSTP}.\\ + \const{VEOL} &\texttt{0x00}& NUL & Carattere di fine riga. Agisce come + un a capo, ma non viene scartato ed + è letto come l'ultimo carattere + nella riga. \\ + \const{VREPRINT}&\texttt{0x12}&(\verb|C-r|)& Ristampa i caratteri non + ancora letti. \\ + \const{VDISCARD}&\texttt{0x07}&(\verb|C-o|)& Non riconosciuto in Linux. \\ + \const{VWERASE} &\texttt{0x17}&(\verb|C-w|)& Cancellazione di una parola.\\ + \const{VLNEXT} &\texttt{0x16}&(\verb|C-v|)& Carattere di escape, serve a + quotare il carattere + successivo che non viene + interpretato ma passato + direttamente all'output. \\ + \const{VEOL2} &\texttt{0x00}& NUL & Ulteriore carattere di fine + riga. Ha lo stesso effetto di + \const{VEOL} ma può essere un + carattere diverso. \\ + \hline + \end{tabular} + \caption{Valori dei caratteri di controllo mantenuti nel campo \var{c\_cc} + della struttura \struct{termios}.} + \label{tab:sess_termios_cc} +\end{table} + + +A ciascuna di queste funzioni di controllo corrisponde un elemento del vettore +\var{c\_cc} che specifica quale è il carattere speciale associato; per +portabilità invece di essere indicati con la loro posizione numerica nel +vettore, i vari elementi vengono indicizzati attraverso delle opportune +costanti, il cui nome corrisponde all'azione ad essi associata. Un elenco +completo dei caratteri di controllo, con le costanti e delle funzionalità +associate è riportato in \tabref{tab:sess_termios_cc}, usando quelle +definizioni diventa possibile assegnare un nuovo carattere di controllo con un +codice del tipo: +\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}% + value.c_cc[VEOL2] = '\n'; +\end{lstlisting} + +La maggior parte di questi caratteri (tutti tranne \const{VTIME} e +\const{VMIN}) hanno effetto solo quando il terminale viene utilizzato in modo +canonico; per alcuni devono essere essere soddisfatte ulteriori richieste, ad +esempio \const{VINTR}, \const{VSUSP}, e \const{VQUIT} richiedono sia settato +\const{ISIG}; \const{VSTART} e \const{VSTOP} richiedono sia settato +\const{IXON}; \const{VLNEXT}, \const{VWERASE}, \const{VREPRINT} richiedono sia +settato \const{IEXTEN}. In ogni caso quando vengono attivati i caratteri +vengono interpretati e non sono passati sulla coda di ingresso. + +Per leggere ed scrivere tutte le varie impostazioni dei terminali viste finora +lo standard POSIX prevede due funzioni che utilizzano come argomento un +puntatore ad una struttura \struct{termios} che sarà quella in cui andranno +immagazzinate le impostazioni. Le funzioni sono \funcd{tcgetattr} e +\funcd{tcsetattr} ed il loro prototipo è: +\begin{functions} + \headdecl{unistd.h} + \headdecl{termios.h} + \funcdecl{int tcgetattr(int fd, struct termios *termios\_p)} + Legge il valore delle impostazioni di un terminale. + + \funcdecl{int tcsetattr(int fd, int optional\_actions, struct termios + *termios\_p)} + Scrive le impostazioni di un terminale. + + \bodydesc{Entrambe le funzioni restituiscono 0 in caso di successo e -1 in + caso di errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINTR}] La funzione è stata interrotta. + \end{errlist} + ed inoltre \errval{EBADF}, \errval{ENOTTY} ed \errval{EINVAL}. + } +\end{functions} + +Le funzioni operano sul terminale cui fa riferimento il file descriptor +\param{fd} utilizzando la struttura indicata dal puntatore \param{termios\_p} +per lo scambio dei dati. Si tenga presente che le impostazioni sono associate +al terminale e non al file descriptor; questo significa che se si è cambiata +una impostazione un qualunque altro processo che apra lo stesso terminale, od +un qualunque altro file descriptor che vi faccia riferimento, vedrà le nuove +impostazioni pur non avendo nulla a che fare con il file descriptor che si è +usato per effettuare i cambiamenti. + +Questo significa che non è possibile usare file descriptor diversi per +utilizzare automaticamente il terminale in modalità diverse, se esiste una +necessità di accesso differenziato di questo tipo occorrerà cambiare +esplicitamente la modalità tutte le volte che si passa da un file descriptor +ad un altro. + +La funzione \func{tcgetattr} legge i valori correnti delle impostazioni di un +terminale qualunque nella struttura puntata da \param{termios\_p}; +\func{tcsetattr} invece effettua la scrittura delle impostazioni e quando +viene invocata sul proprio terminale di controllo può essere eseguita con +successo solo da un processo in foreground. Se invocata da un processo in +background infatti tutto il gruppo riceverà un segnale di \const{SIGTTOU} come +se si fosse tentata una scrittura, a meno che il processo chiamante non abbia +\const{SIGTTOU} ignorato o bloccato, nel qual caso l'operazione sarà eseguita. + +La funzione \func{tcsetattr} prevede tre diverse modalità di funzionamento, +specificabili attraverso l'argomento \param{optional\_actions}, che permette +di stabilire come viene eseguito il cambiamento delle impostazioni del +terminale, i valori possibili sono riportati in +\tabref{tab:sess_tcsetattr_option}; di norma (come fatto per le due funzioni +di esempio) si usa sempre \const{TCSANOW}, le altre opzioni possono essere +utili qualora si cambino i parametri di output. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore}& \textbf{Significato}\\ + \hline + \hline + \const{TCSANOW} & Esegue i cambiamenti in maniera immediata. \\ + \const{TCSADRAIN}& I cambiamenti vengono eseguiti dopo aver atteso che + tutto l'output presente sulle code è stato scritto. \\ + \const{TCSAFLUSH}& È identico a \const{TCSADRAIN}, ma in più scarta + tutti i dati presenti sulla coda di input.\\ + \hline + \end{tabular} + \caption{Possibili valori per l'argomento \param{optional\_actions} della + funzione \func{tcsetattr}.} + \label{tab:sess_tcsetattr_option} +\end{table} + +Occorre infine tenere presente che \func{tcsetattr} ritorna con successo anche +se soltanto uno dei cambiamenti richiesti è stato eseguito. Pertanto se si +effettuano più cambiamenti è buona norma controllare con una ulteriore +chiamata a \func{tcgetattr} che essi siano stati eseguiti tutti quanti. + +\begin{figure}[!htb] + \footnotesize + \begin{lstlisting}{}% +#include +#include +#include + +int SetTermAttr(int fd, tcflag_t flag) +{ + struct termios values; + int res; + res = tcgetattr (desc, &values); + if (res) { + perror("Cannot get attributes"); + return res; + } + values.c_lflag |= flag; + res = tcsetattr (desc, TCSANOW, &values); + if (res) { + perror("Cannot set attributes"); + return res; + } + return 0; +} + \end{lstlisting} + \caption{Codice della funzione \func{SetTermAttr} che permette di + impostare uno dei flag di controllo locale del terminale.} + \label{fig:term_set_attr} +\end{figure} + +Come già accennato per i cambiamenti effettuati ai vari flag di controllo +occorre che i valori di ciascun bit siano specificati avendo cura di mantenere +intatti gli altri; per questo motivo in generale si deve prima leggere il +valore corrente delle impostazioni con \func{tcgetattr} per poi modificare i +valori impostati. + +In \figref{fig:term_set_attr} e \figref{fig:term_unset_attr} si è riportato +rispettivamente il codice delle due funzioni \func{SetTermAttr} e +\func{UnSetTermAttr}, che possono essere usate per impostare o rimuovere, con +le dovute precauzioni, un qualunque bit di \var{c\_lflag}. Il codice di +entrambe le funzioni può essere trovato nel file \file{SetTermAttr.c} dei +sorgenti allegati. + +La funzione \func{SetTermAttr} provvede ad impostare il bit specificato +dall'argomento \param{flag}; prima si leggono i valori correnti +(\texttt{\small 10}) con \func{tcgetattr}, uscendo con un messaggio in caso di +errore (\texttt{\small 11--14}), poi si provvede a impostare solo i bit +richiesti (possono essere più di uno) con un OR binario (\texttt{\small 15}); +infine si scrive il nuovo valore modificato con \func{tcsetattr} +(\texttt{\small 16}), notificando un eventuale errore (\texttt{\small 11--14}) +o uscendo normalmente. + +\begin{figure}[!htb] + \footnotesize + \begin{lstlisting}{}% +int UnSetTermAttr(int fd, tcflag_t flag) +{ + struct termios values; + int res; + res = tcgetattr (desc, &values); + if (res) { + perror("Cannot get attributes"); + return res; + } + values.c_lflag &= (~flag); + res = tcsetattr (desc, TCSANOW, &values); + if (res) { + perror("Cannot set attributes"); + return res; + } + return 0; +} + \end{lstlisting} + \caption{Codice della funzione \func{UnSetTermAttr} che permette di + rimuovere uno dei flag di controllo locale del terminale.} + \label{fig:term_unset_attr} +\end{figure} + +La seconda funzione, \func{UnSetTermAttr}, è assolutamente identica alla +prima, solo che in questo caso (in \texttt{\small 15}) si rimuovono i bit +specificati dall'argomento \param{flag} usando un AND binario del valore +negato. + + +Al contrario di tutte le altre caratteristiche dei terminali, che possono +essere impostate esplicitamente utilizzando gli opportuni campi di +\struct{termios}, per le velocità della linea (il cosiddetto \textit{baud + rate}) non è prevista una implementazione standardizzata, per cui anche se +in Linux sono mantenute in due campi dedicati nella struttura, questi non +devono essere acceduti direttamente ma solo attraverso le apposite funzioni di +interfaccia provviste da POSIX.1. + +Lo standard prevede due funzioni per scrivere la velocità delle linee seriali, +\funcd{cfsetispeed} per la velocità della linea di ingresso e +\funcd{cfsetospeed} per la velocità della linea di uscita; i loro prototipi +sono: +\begin{functions} + \headdecl{unistd.h} + \headdecl{termios.h} + \funcdecl{int cfsetispeed(struct termios *termios\_p, speed\_t speed)} + Imposta la velocità delle linee seriali in ingresso. + + \funcdecl{int cfsetospeed(struct termios *termios\_p, speed\_t speed)} + Imposta la velocità delle linee seriali in uscita. + + \bodydesc{Entrambe le funzioni restituiscono 0 in caso di successo e -1 in + caso di errore, che avviene solo quando il valore specificato non è + valido.} +\end{functions} + +Si noti che le funzioni si limitano a scrivere opportunamente il valore della +velocità prescelta \param{speed} all'interno della struttura puntata da +\param{termios\_p}; per effettuare l'impostazione effettiva occorrerà poi +chiamare \func{tcsetattr}. + +Si tenga presente che per le linee seriali solo alcuni valori di velocità sono +validi; questi possono essere specificati direttamente (le \acr{glibc} +prevedono che i valori siano indicati in bit per secondo), ma in generale +altre versioni di librerie possono utilizzare dei valori diversi; per questo +POSIX.1 prevede una serie di costanti che però servono solo per specificare le +velocità tipiche delle linee seriali: +\begin{verbatim} + B0 B50 B75 + B110 B134 B150 + B200 B300 B600 + B1200 B1800 B2400 + B4800 B9600 B19200 + B38400 B57600 B115200 + B230400 B460800 +\end{verbatim} + +Un terminale può utilizzare solo alcune delle velocità possibili, le funzioni +però non controllano se il valore specificato è valido, dato che non possono +sapere a quale terminale le velocità saranno applicate; sarà l'esecuzione di +\func{tcsetattr} a fallire quando si cercherà di eseguire l'impostazione. + +Di norma il valore ha senso solo per i terminali seriali dove indica appunto +la velocità della linea di trasmissione; se questa non corrisponde a quella +del terminale quest'ultimo non potrà funzionare: quando il terminale non è +seriale il valore non influisce sulla velocità di trasmissione dei dati. + +In generale impostare un valore nullo (\val{B0}) sulla linea di output fa si +che il modem non asserisca più le linee di controllo, interrompendo di fatto +la connessione, qualora invece si utilizzi questo valore per la linea di input +l'effetto sarà quello di rendere la sua velocità identica a quella della linea +di output. + +Analogamente a quanto avviene per l'impostazione, le velocità possono essere +lette da una struttura \struct{termios} utilizzando altre due funzioni, +\funcd{cfgetispeed} e \funcd{cfgetospeed}, i cui prototipi sono: +\begin{functions} + \headdecl{unistd.h} + \headdecl{termios.h} + \funcdecl{speed\_t cfgetispeed(struct termios *termios\_p)} + Legge la velocità delle linee seriali in ingresso. + + \funcdecl{speed\_t cfgetospeed(struct termios *termios\_p)} + Legge la velocità delle linee seriali in uscita. + + \bodydesc{Entrambe le funzioni restituiscono la velocità della linea, non + sono previste condizioni di errore.} +\end{functions} + +Anche in questo caso le due funzioni estraggono i valori della velocità della +linea da una struttura, il cui indirizzo è specificato dall'argomento +\param{termios\_p} che deve essere stata letta in precedenza con +\func{tcgetaddr}. + + + +\subsection{La gestione della disciplina di linea.} +\label{sec:term_line_discipline} + +Come illustrato dalla struttura riportata in \figref{fig:term_struct} tutti i +terminali hanno un insieme di funzionalità comuni, che prevedono la presenza +di code di ingresso ed uscita; in generale si fa riferimento ad esse con il +nome di \textsl{discipline di linea}. + + +Lo standard POSIX prevede alcune funzioni che permettono di intervenire +direttamente sulla gestione di quest'ultime e sull'interazione fra i dati in +ingresso ed uscita e le relative code. In generale tutte queste funzioni +vengono considerate, dal punto di vista dell'accesso al terminale, come delle +funzioni di scrittura, pertanto se usate da processi in background sul loro +terminale di controllo provocano l'emissione di \const{SIGTTOU} come +illustrato in \secref{sec:sess_ctrl_term}.\footnote{con la stessa eccezione, + già vista per \func{tcsetaddr}, che quest'ultimo sia bloccato o ignorato dal + processo chiamante.} + +Una prima funzione, che è efficace solo in caso di terminali seriali asincroni +(non fa niente per tutti gli altri terminali), è \funcd{tcsendbreak}; il suo +prototipo è: +\begin{functions} + \headdecl{unistd.h} + \headdecl{termios.h} + + \funcdecl{int tcsendbreak(int fd, int duration)} Genera una condizione di + break inviando un flusso di bit nulli. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori \errval{EBADF} o + \errval{ENOTTY}.} +\end{functions} + +La funzione invia un flusso di bit nulli (che genera una condizione di break) +sul terminale associato a \param{fd}; un valore nullo di \param{duration} +implica una durata del flusso fra 0.25 e 0.5 secondi, un valore diverso da +zero implica una durata pari a \code{duration*T} dove \code{T} è un valore +compreso fra 0.25 e 0.5.\footnote{POSIX specifica il comportamento solo nel + caso si sia impostato un valore nullo per \param{duration}; il comportamento + negli altri casi può dipendere dalla implementazione.} + +Le altre funzioni previste da POSIX servono a controllare il comportamento +dell'interazione fra le code associate al terminale e l'utente; la prima è +\funcd{tcdrain}, il cui prototipo è: +\begin{functions} + \headdecl{unistd.h} + \headdecl{termios.h} + + \funcdecl{int tcdrain(int fd)} Attende lo svuotamento della coda di output. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori \errval{EBADF} o + \errval{ENOTTY}.} +\end{functions} + +La funzione blocca il processo fino a che tutto l'output presente sulla coda +di uscita non è stato trasmesso al terminale associato ad \param{fd}. % La + % funzione è un punto di cancellazione per i + % programmi multi-thread, in tal caso le + % chiamate devono essere protette con dei + % gestori di cancellazione. + +Una seconda funzione, \funcd{tcflush}, permette svuotare immediatamente le code +di cancellando tutti i dati presenti al loro interno; il suo prototipo è: +\begin{functions} + \headdecl{unistd.h} \headdecl{termios.h} + + \funcdecl{int tcflush(int fd, int queue)} Cancella i dati presenti + nelle code di ingresso o di uscita. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori \errval{EBADF} o + \errval{ENOTTY}.} +\end{functions} + +La funzione agisce sul terminale associato a \param{fd}, l'argomento +\param{queue} permette di specificare su quale coda (ingresso, uscita o +entrambe), operare. Esso può prendere i valori riportati in +\tabref{tab:sess_tcflush_queue}, nel caso si specifichi la coda di ingresso +cancellerà i dati ricevuti ma non ancora letti, nel caso si specifichi la coda +di uscita cancellerà i dati scritti ma non ancora trasmessi. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore}& \textbf{Significato}\\ + \hline + \hline + \const{TCIFLUSH} & Cancella i dati sulla coda di ingresso. \\ + \const{TCOFLUSH} & Cancella i dati sulla coda di uscita. \\ + \const{TCIOFLUSH}& Cancella i dati su entrambe le code.\\ + \hline + \end{tabular} + \caption{Possibili valori per l'argomento \param{queue} della + funzione \func{tcflush}.} + \label{tab:sess_tcflush_queue} +\end{table} + + +L'ultima funzione dell'interfaccia che interviene sulla disciplina di linea è +\funcd{tcflow}, che viene usata per sospendere la trasmissione e la ricezione +dei dati sul terminale; il suo prototipo è: +\begin{functions} + \headdecl{unistd.h} + \headdecl{termios.h} + + \funcdecl{int tcflow(int fd, int action)} + + Sospende e riavvia il flusso dei dati sul terminale. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori \errval{EBADF} o + \errval{ENOTTY}.} +\end{functions} + +La funzione permette di controllare (interrompendo e facendo riprendere) il +flusso dei dati fra il terminale ed il sistema sia in ingresso che in uscita. +Il comportamento della funzione è regolato dall'argomento \param{action}, i +cui possibili valori, e relativa azione eseguita dalla funzione, sono +riportati in \secref{tab:sess_tcflow_action}. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore}& \textbf{Azione}\\ + \hline + \hline + \const{TCOOFF}& Sospende l'output.\\ + \const{TCOON} & Riprende un output precedentemente sospeso.\\ + \const{TCIOFF}& Il sistema trasmette un carattere di STOP, che + fa interrompere la trasmissione dei dati dal terminale. \\ + \const{TCION} & Il sistema trasmette un carattere di START, che + fa riprendere la trasmissione dei dati dal terminale.\\ + \hline + \end{tabular} + \caption{Possibili valori per l'argomento \param{action} della + funzione \func{tcflow}.} + \label{tab:sess_tcflow_action} +\end{table} + + +\subsection{Operare in \textsl{modo non canonico}} +\label{sec:term_non_canonical} + +Operare con un terminale in modo canonico è relativamente semplice; basta +eseguire una lettura e la funzione ritornerà quando una il driver del +terminale avrà completato una linea di input. Non è detto che la linea sia +letta interamente (si può aver richiesto un numero inferiore di byte) ma in +ogni caso nessun dato verrà perso, e il resto della linea sarà letto alla +chiamata successiva. + +Inoltre in modo canonico la gestione dell'input è di norma eseguita +direttamente dal driver del terminale, che si incarica (a seconda di quanto +impostato con le funzioni viste nei paragrafi precedenti) di cancellare i +caratteri, bloccare e riavviare il flusso dei dati, terminare la linea quando +viene ricevuti uno dei vari caratteri di terminazione (NL, EOL, EOL2, EOF). +In modo non canonico tocca invece al programma gestire tutto quanto, i +caratteri NL, EOL, EOL2, EOF, ERASE, KILL, CR, REPRINT non vengono +interpretati automaticamente ed inoltre, non dividendo più l'input in linee, +il sistema non ha più un limite definito per quando ritornare i dati ad un +processo. Per questo motivo abbiamo visto che in \var{c\_cc} sono previsti due +caratteri speciali, MIN e TIME (specificati dagli indici \const{VMIN} e +\const{VTIME} in \var{c\_cc}) che dicono al sistema di ritornare da una +\func{read} quando è stata letta una determinata quantità di dati o è passato +un certo tempo. +Come accennato nella relativa spiegazione in \tabref{tab:sess_termios_cc}, +TIME e MIN non sono in realtà caratteri ma valori numerici. Il comportamento +del sistema per un terminale in modalità non canonica prevede quattro casi +distinti: +\begin{description} +\item[MIN$>0$, TIME$>0$] In questo caso MIN stabilisce il numero minimo di + caratteri desiderati e TIME un tempo di attesa, in decimi di secondo, fra un + carattere e l'altro. Una \func{read} ritorna se vengono ricevuti almeno MIN + caratteri prima della scadenza di TIME (MIN è solo un limite inferiore, se + la funzione ha richiesto un numero maggiore di caratteri ne possono essere + restituiti di più); se invece TIME scade vengono restituiti i byte ricevuti + fino ad allora (un carattere viene sempre letto, dato che il timer inizia a + scorrere solo dopo la ricezione del primo carattere). +\item[MIN$>0$, TIME$=0$] Una \func{read} ritorna solo dopo che sono stati + ricevuti almeno MIN caratteri. Questo significa che una \func{read} può + bloccarsi indefinitamente. +\item[MIN$=0$, TIME$>0$] In questo caso TIME indica un tempo di attesa dalla + chiamata di \func{read}, la funzione ritorna non appena viene ricevuto un + carattere o scade il tempo. Si noti che è possibile che \func{read} ritorni + con un valore nullo. +\item[MIN$=0$, TIME$=0$] In questo caso una \func{read} ritorna immediatamente + restituendo tutti i caratteri ricevuti. Anche in questo caso può ritornare + con un valore nullo. +\end{description} -\subsection{La shell e i programmi} -\label{sec:sess_shell} %%% Local Variables: