antiro-
vanno byt' korrektnymi na diske, ispol'zuetsya otkrytie fajla
fd = open( imya, O_RDWR | O_SYNC);
kotoroe oznachaet, chto pri kazhdom write blok iz kesh-bufera nemedlenno zapisyvaetsya na
disk. |to delaet rabotu nadezhnee, no sushchestvenno medlennee.
Special'nye fajly ustrojstv ne mogut byt' sozdany vyzovom creat, sozdayushchim
tol'ko obychnye fajly. Fajly ustrojstv sozdayutsya vyzovom mknod:
#include <sys/sysmacros.h>
dev_t dev = makedev(major, minor);
/* (major << 8) | minor */
mknod( imyaFajla, kodyDostupa|tip, dev);
gde dev - para (mazhor,minor) sozdavaemogo ustrojstva; kodyDostupa - kody dostupa k
fajlu (0777)|=; tip - eto odna iz konstant S_IFIFO, S_IFCHR, S_IFBLK iz include-fajla
<sys/stat.h>.
mknod dostupen dlya vypolneniya tol'ko superpol'zovatelyu (za isklyucheniem sluchaya
S_IFIFO). Esli by eto bylo ne tak, to mozhno bylo by sozdat' fajl ustrojstva, svyazan-
nyj s sushchestvuyushchim diskom, i chitat' informaciyu s nego napryamuyu, v obhod mehanizmov
logicheskoj fajlovoj sistemy i zashchity fajlov kodami dostupa.
Mozhno sozdat' fajl ustrojstva s mazhorom i/ili minorom, ne otvechayushchim nikakomu
real'nomu ustrojstvu (net takogo drajvera ili minor slishkom velik). Otkrytie takih
____________________
|= Obychno k blochnym ustrojstvam (diskam) dostup razreshaetsya tol'ko superpol'zova-
telyu, v protivnom sluchae mozhno prochitat' s "syrogo" diska (v obhod mehanizmov fajlo-
voj sistemy) fizicheskie bloki lyubogo fajla i ves' mehanizm zashchity okazhetsya nerabotayu-
shchim.
A. Bogatyrev, 1992-95 - 251 - Si v UNIX
ustrojstv vydaet kod oshibki ENODEV.
Iz nashej programmy my mozhem vyzovom stat() uznat' kod ustrojstva, na kotorom
raspolozhen fajl. On budet soderzhat'sya v pole dev_t st_dev; a esli fajl yavlyaetsya spe-
cial'nym fajlom (interfejsom drajvera ustrojstva), to kod samogo etogo ustrojstva
mozhno uznat' iz polya dev_t st_rdev; Rassmotrim primer, kotoryj vyyasnyaet, otnosyatsya li
dva imeni k odnomu i tomu zhe fajlu:
#include <sys/types.h>
#include <sys/stat.h>
void main(ac, av) char *av[]; {
struct stat st1, st2; int eq;
if(ac != 3) exit(13);
stat(av[1], &st1); stat(av[2], &st2);
if(eq =
(st1.st_ino == st2.st_ino && /* nomera I-uzlov */
st1.st_dev == st2.st_dev)) /* kody ustrojstv */
printf("%s i %s - dva imeni odnogo fajla\n",av[1],av[2]);
exit( !eq );
}
Nakonec, vernemsya k sklejke neskol'kih fajlovyh sistem v odnu ob®edinennuyu ierarhiyu:
ino=2
*------ kornevaya fajlovaya sistema
/ \ /\ na diske /dev/hd0
/ /\ /\
\
*-/mnt/hd1
:
* ino=2 FS na diske /dev/hd1
/ \ (removable FS)
/\ \
Dlya togo, chtoby pomestit' kornevoj katalog fajlovoj sistemy, nahodyashchejsya na diske
/dev/hd1, vmesto kataloga /mnt/hd1 uzhe "sobrannoj" fajlovoj sistemy, my dolzhny izdat'
sisvyzov
mount("/dev/hd1", "/mnt/hd1", 0);
Dlya otklyucheniya smontirovannoj fajlovoj sistemy my dolzhny vyzvat'
umount("/dev/hd1");
(katalog, k kotoromu ona smontirovana, uzhe chislitsya v tablice yadra, poetomu ego zada-
vat' ne nado). Pri montirovanii vse soderzhimoe kataloga /mnt/hd1 stanet nedostupnym,
zato pri obrashchenii k imeni /mnt/hd1 my na samom dele doberemsya do (bezymyannogo) kor-
nevogo kataloga na diske /dev/hd1. Takoj katalog nosit nazvanie mount point i mozhet
byt' vyyavlen po tomu priznaku, chto "." i ".." v nem lezhat na raznyh ustrojstvah:
struct stat st1, st2;
stat("/mnt/hd1/.", &st1); stat("/mnt/hd1/..", &st2);
if( st1.st_dev != st2.st_dev) ... ; /*mount point*/
Dlya st1 pole st_dev oznachaet kod ustrojstva /dev/hd1, a dlya st2 - ustrojstva, soder-
zhashchego kornevuyu fajlovuyu sistemu. Operacii montirovaniya i otmontirovaniya fajlovyh
sistem dostupny tol'ko superpol'zovatelyu.
I naposledok - sravnenie struktur I-uzla.
na diske v pamyati v vyzove stat
<sys/ino.h> <sys/inode.h> <sys/stat.h>
A. Bogatyrev, 1992-95 - 252 - Si v UNIX
struct dinode struct inode struct stat
// kody dostupa i tip fajla
ushort di_mode i_mode st_mode
// chislo imen fajla
short di_nlink i_nlink st_nlink
// nomer I-uzla
ushort --- i_number st_ino
// identifikator vladel'ca
ushort di_uid i_uid st_uid
// identifikator gruppy vladel'ca
ushort di_gid i_gid st_gid
// razmer fajla v bajtah
off_t di_size i_size st_size
// vremya sozdaniya
time_t di_ctime i_ctime st_ctime
// vremya poslednego izmeneniya (write)
time_t di_mtime i_mtime st_mtime
// vremya poslednego dostupa (read/write)
time_t di_atime i_atime st_atime
// ustrojstvo, na kotorom raspolozhen fajl
dev_t --- i_dev st_dev
// ustrojstvo, k kotoromu privodit spec.fajl
dev_t --- i_rdev st_rdev
// adresa blokov
char di_addr[39] i_addr[]
// schetchik ssylok na strukturu v yadre
cnt_t i_count
// i koe-chto eshche
Minusy oznachayut, chto dannoe pole ne hranitsya na diske, a vychislyaetsya yadrom. V sovre-
mennyh versiyah UNIX mogut byt' legkie otlichiya ot vyshenapisannoj tablicy.
6.10.1. Napishite programmu pwd, opredelyayushchuyu polnoe imya tekushchego rabochego kataloga.
#define U42 opredelyaet fajlovuyu sistemu s dlinnymi imenami, otsutstvie etogo flaga -
s korotkimi (14 simvolov).
A. Bogatyrev, 1992-95 - 253 - Si v UNIX
/* Komanda pwd.
* Tekst getwd() vzyat iz ishodnyh tekstov biblioteki yazyka Si.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#define ediag(e,r) (e)
/*
* getwd() vozvrashchaet polnoe imya tekushchego rabochego kataloga.
* Pri oshibke vozvrashchaetsya NULL, a v pathname kopiruetsya soobshchenie
* ob oshibke.
*/
#ifndef MAXPATHLEN
#define MAXPATHLEN 128
#endif
#define CURDIR "." /* imya tekushchego kataloga */
#define PARENTDIR ".." /* imya roditel'skogo kataloga */
#define PATHSEP "/" /* razdelitel' komponent puti */
#define ROOTDIR "/" /* kornevoj katalog */
#define GETWDERR(s) strcpy(pathname, (s));
#define CP(to,from) strncpy(to,from.d_name,DIRSIZ),to[DIRSIZ]='\0'
char *strcpy(char *, char *); char *strncpy(char *, char *, int);
char *getwd(char *pathname);
static char *prepend(char *dirname, char *pathname);
static int pathsize; /* dlina imeni */
#ifndef U42
char *getwd(char *pathname)
{
char pathbuf[MAXPATHLEN]; /* temporary pathname buffer */
char *pnptr = &pathbuf[(sizeof pathbuf)-1]; /* pathname pointer */
dev_t rdev; /* root device number */
int fil = (-1); /* directory file descriptor */
ino_t rino; /* root inode number */
struct direct dir; /* directory entry struct */
struct stat d ,dd; /* file status struct */
/* d - "." dd - ".." | dname */
char dname[DIRSIZ+1]; /* an directory entry */
pathsize = 0;
*pnptr = '\0';
if (stat(ROOTDIR, &d) < 0) {
GETWDERR(ediag("getwd: can't stat /",
"getwd: nel'zya vypolnit' stat /"));
return (NULL);
}
rdev = d.st_dev; /* kod ustrojstva, na kotorom razmeshchen koren' */
rino = d.st_ino; /* nomer I-uzla, predstavlyayushchego kornevoj katalog */
A. Bogatyrev, 1992-95 - 254 - Si v UNIX
for (;;) {
if (stat(CURDIR, &d) < 0) {
CantStat:
GETWDERR(ediag("getwd: can't stat .",
"getwd: nel'zya vypolnit' stat ."));
goto fail;
}
if (d.st_ino == rino && d.st_dev == rdev)
break; /* dostigli kornevogo kataloga */
if ((fil = open(PARENTDIR, O_RDONLY)) < 0) {
GETWDERR(ediag("getwd: can't open ..",
"getwd: nel'zya otkryt' .."));
goto fail;
}
if (chdir(PARENTDIR) < 0) {
GETWDERR(ediag("getwd: can't chdir to ..",
"getwd: nel'zya perejti v .."));
goto fail;
}
if (fstat(fil, &dd) < 0)
goto CantStat;
if (d.st_dev == dd.st_dev) { /* to zhe ustrojstvo */
if (d.st_ino == dd.st_ino) {
/* dostigli kornya ".." == "." */
close(fil); break;
}
do {
if (read(fil, (char *) &dir,
sizeof(dir)) < sizeof(dir)
){
ReadErr:
close(fil);
GETWDERR(ediag("getwd: read error in ..",
"getwd: oshibka chteniya .."));
goto fail;
}
} while (dir.d_ino != d.st_ino);
CP(dname,dir);
} else /* ".." nahoditsya na drugom diske: mount point */
do {
if (read(fil, (char *) &dir,
sizeof(dir)) < sizeof(dir))
goto ReadErr;
if( dir.d_ino == 0 ) /* fajl stert */
continue;
CP(dname,dir);
if (stat(dname, &dd) < 0) {
sprintf (pathname, "getwd: %s %s",
ediag ("can't stat",
"nel'zya vypolnit' stat"), dname);
goto fail;
}
} while(dd.st_ino != d.st_ino ||
dd.st_dev != d.st_dev);
close(fil);
pnptr = prepend(PATHSEP, prepend(dname, pnptr));
}
A. Bogatyrev, 1992-95 - 255 - Si v UNIX
if (*pnptr == '\0') /* tekushchij katalog == kornevomu */
strcpy(pathname, ROOTDIR);
else {
strcpy(pathname, pnptr);
if (chdir(pnptr) < 0) {
GETWDERR(ediag("getwd: can't change back to .",
"getwd: nel'zya vernut'sya v ."));
return (NULL);
}
}
return (pathname);
fail:
close(fil);
chdir(prepend(CURDIR, pnptr));
return (NULL);
}
#else /* U42 */
extern char *strcpy ();
extern DIR *opendir();
char *getwd (char *pathname)
{
char pathbuf[MAXPATHLEN];/* temporary pathname buffer */
char *pnptr = &pathbuf[(sizeof pathbuf) - 1];/* pathname pointer */
char *prepend (); /* prepend dirname to pathname */
dev_t rdev; /* root device number */
DIR * dirp; /* directory stream */
ino_t rino; /* root inode number */
struct dirent *dir; /* directory entry struct */
struct stat d,
dd; /* file status struct */
pathsize = 0;
*pnptr = '\0';
stat (ROOTDIR, &d);
rdev = d.st_dev;
rino = d.st_ino;
for (;;) {
stat (CURDIR, &d);
if (d.st_ino == rino && d.st_dev == rdev)
break; /* reached root directory */
if ((dirp = opendir (PARENTDIR)) == NULL) {
GETWDERR ("getwd: can't open ..");
goto fail;
}
if (chdir (PARENTDIR) < 0) {
closedir (dirp);
GETWDERR ("getwd: can't chdir to ..");
goto fail;
}
A. Bogatyrev, 1992-95 - 256 - Si v UNIX
fstat (dirp -> dd_fd, &dd);
if (d.st_dev == dd.st_dev) {
if (d.st_ino == dd.st_ino) {
/* reached root directory */
closedir (dirp);
break;
}
do {
if ((dir = readdir (dirp)) == NULL) {
closedir (dirp);
GETWDERR ("getwd: read error in ..");
goto fail;
}
} while (dir -> d_ino != d.st_ino);
}
else
do {
if ((dir = readdir (dirp)) == NULL) {
closedir (dirp);
GETWDERR ("getwd: read error in ..");
goto fail;
}
stat (dir -> d_name, &dd);
} while (dd.st_ino != d.st_ino || dd.st_dev != d.st_dev);
closedir (dirp);
pnptr = prepend (PATHSEP, prepend (dir -> d_name, pnptr));
}
if (*pnptr == '\0') /* current dir == root dir */
strcpy (pathname, ROOTDIR);
else {
strcpy (pathname, pnptr);
if (chdir (pnptr) < 0) {
GETWDERR ("getwd: can't change back to .");
return (NULL);
}
}
return (pathname);
fail:
chdir (prepend (CURDIR, pnptr));
return (NULL);
}
#endif
A. Bogatyrev, 1992-95 - 257 - Si v UNIX
/*
* prepend() tacks a directory name onto the front of a pathname.
*/
static char *prepend (
register char *dirname, /* chto dobavlyat' */
register char *pathname /* k chemu dobavlyat' */
) {
register int i; /* dlina imeni kataloga */
for (i = 0; *dirname != '\0'; i++, dirname++)
continue;
if ((pathsize += i) < MAXPATHLEN)
while (i-- > 0)
*--pathname = *--dirname;
return (pathname);
}
#ifndef CWDONLY
void main(){
char buffer[MAXPATHLEN+1];
char *cwd = getwd(buffer);
printf( "%s%s\n", cwd ? "": "ERROR:", buffer);
}
#endif
6.10.2. Napishite funkciyu canon(), kanoniziruyushchuyu imya fajla, t.e. prevrashchayushchuyu ego v
polnoe imya (ot kornevogo kataloga), ne soderzhashchee komponent "." i "..", a takzhe lish-
nih simvolov slesh '/'. Pust', k primeru, tekushchij rabochij katalog est' /usr/abs/C-
book. Togda funkciya preobrazuet
. -> /usr/abs/C-book
.. -> /usr/abs
../.. -> /usr
////.. -> /
/aa -> /aa
/aa/../bb -> /bb
cc//dd/../ee -> /usr/abs/C-book/cc/ee
../a/b/./d -> /usr/abs/a/b/d
Otvet:
#include <stdio.h>
/* slesh, razdelitel' komponent puti */
#define SLASH '/'
extern char *strchr (char *, char),
*strrchr(char *, char);
struct savech{ char *s, c; };
#define SAVE(sv, str) (sv).s = (str); (sv).c = *(str)
#define RESTORE(sv) if((sv).s) *(sv).s = (sv).c
/* |to struktura dlya ispol'zovaniya v takom kontekste:
void main(){
char *d = "hello"; struct savech ss;
SAVE(ss, d+3); *(d+3) = '\0'; printf("%s\n", d);
RESTORE(ss); printf("%s\n", d);
}
*/
/* OTSECHX POSLEDNYUYU KOMPONENTU PUTI */
struct savech parentdir(char *path){
char *last = strrchr( path, SLASH );
A. Bogatyrev, 1992-95 - 258 - Si v UNIX
char *first = strchr ( path, SLASH );
struct savech sp; sp.s = NULL; sp.c = '\0';
if( last == NULL ) return sp; /* ne polnoe imya */
if( last[1] == '\0' ) return sp; /* kornevoj katalog */
if( last == first ) /* edinstvennyj slesh: /DIR */
last++;
sp.s = last; sp.c = *last; *last = '\0';
return sp;
}
#define isfullpath(s) (*s == SLASH)
/* KANONIZIROVATX IMYA FAJLA */
void canon(
char *where, /* kuda pomestit' otvet */
char *cwd, /* polnoe imya tekushchego kataloga */
char *path /* ishodnoe imya dlya kanonizacii */
){ char *s, *slash;
/* Sformirovat' imya kataloga - tochki otscheta */
if( isfullpath(path)){
s = strchr(path, SLASH); /* @ */
strncpy(where, path, s - path + 1);
where[s - path + 1] = '\0';
/* ili dazhe prosto strcpy(where, "/"); */
path = s+1; /* ostatok puti bez '/' v nachale */
} else strcpy(where, cwd);
/* Pokomponentnyj prosmotr puti */
do{ if(slash = strchr(path, SLASH)) *slash = '\0';
/* teper' path soderzhit ocherednuyu komponentu puti */
if(*path == '\0' || !strcmp(path, ".")) ;
/* to prosto proignorirovat' "." i lishnie "///" */
else if( !strcmp(path, ".."))
(void) parentdir(where);
else{ int len = strlen(where);
/* dobavit' v konec razdelyayushchij slesh */
if( where[len-1] != SLASH ){
where[len] = SLASH;
where[len+1] = '\0';
}
strcat( where+len, path );
/* +len chisto dlya uskoreniya poiska
* konca stroki vnutri strcat(); */
}
if(slash){ *slash = SLASH; /* vosstanovit' */
path = slash + 1;
}
} while (slash != NULL);
}
char cwd[256], input[256], output[256];
void main(){
/* Uznat' polnoe imya tekushchego kataloga.
* getcwd() - standartnaya funkciya, vyzyvayushchaya
* cherez popen() komandu pwd (i potomu medlennaya).
*/
getcwd(cwd, sizeof cwd);
while( gets(input)){
canon(output, cwd, input);
printf("%-20s -> %s\n", input, output);
}
}
A. Bogatyrev, 1992-95 - 259 - Si v UNIX
V etom primere (iznachal'no pisavshemsya dlya MS DOS) est' "strannoe" mesto, pomechennoe
/*@*/. Delo v tom, chto v DOS funkciya isfullpath byla sposobna raspoznavat' imena faj-
lov vrode C:\aaa\bbb, kotorye ne obyazatel'no nachinayutsya so slesha.
6.11. Mul'tipleksirovanie vvoda-vyvoda.
Dannaya glava posvyashchena sistemnomu vyzovu select, kotoryj, odnako, my predostav-
lyaem vam issledovat' samostoyatel'no. Ego rol' takova: on pozvolyaet oprashivat' nes-
kol'ko deskriptorov otkrytyh fajlov (ili ustrojstv) i kak tol'ko v fajle poyavlyaetsya
novaya informaciya - soobshchat' ob etom nashej programme. Obychno eto byvaet svyazano s
deskriptorami, vedushchimi k setevym ustrojstvam.
6.11.1.
/* Primer ispol'zovaniya vyzova select() dlya mul'tipleksirovaniya
* neskol'kih kanalov vvoda. |tot vyzov mozhno takzhe ispol'zovat'
* dlya polucheniya tajmauta.
* Vyzov: vojti na terminalah tty01 tty02 i nabrat' na kazhdom
* sleep 30000
* zatem na tty00 skazat' select /dev/tty01 /dev/tty02
* i vvodit' chto-libo na terminalah tty01 i tty02
* Sborka: cc select.c -o select -lsocket
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h> /* fd_set, FD_SET, e.t.c. */
#include <sys/param.h> /* NOFILE */
#include <sys/select.h>
#include <sys/time.h>
#include <sys/filio.h> /* dlya FIONREAD */
#define max(a,b) ((a) > (b) ? (a) : (b))
char buf[512]; /* bufer chteniya */
int fdin, fdout; /* deskriptory kanalov stdin, stdout */
int nready; /* chislo gotovyh kanalov */
int nopen; /* chislo otkrytyh kanalov */
int maxfd = 0; /* maksimal'nyj deskriptor */
int nfds; /* skol'ko pervyh deskriptorov proveryat' */
int f; /* tekushchij deskriptor */
fd_set set, rset; /* maski */
/* tablica otkrytyh nami fajlov */
struct _fds {
int fd; /* deskriptor */
char name[30]; /* imya fajla */
} fds[ NOFILE ] = { /* NOFILE - maks. chislo otkrytyh fajlov na process */
{ 0, "stdin" }, { 1, "stdout" }, { 2, "stderr" }
/* vse ostal'noe - nuli */
};
struct timeval timeout, rtimeout;
/* vydat' imya fajla po deskriptoru */
char *N( int fd ){
register i;
for(i=0; i < NOFILE; i++)
if(fds[i].fd == fd ) return fds[i].name;
return "???";
}
A. Bogatyrev, 1992-95 - 260 - Si v UNIX
void main( int ac, char **av ){
nopen = 3; /* stdin, stdout, stderr */
for( f = 3; f < NOFILE; f++ ) fds[f].fd = (-1);
fdin = fileno(stdin); fdout = fileno(stdout);
setbuf(stdout, NULL); /* otmena buferizacii */
FD_ZERO(&set); /* ochistka maski */
for(f=1; f < ac; f++ )
if((fds[nopen].fd = open(av[f], O_RDONLY)) < 0 ){
fprintf(stderr, "Can't read %s\n", av[f] );
continue;
} else {
FD_SET(fds[nopen].fd, &set ); /* uchest' v maske */
maxfd = max(maxfd, fds[nopen].fd );
strncpy(fds[nopen].name, av[f], sizeof(fds[0].name) - 1);
nopen++;
}
if( nopen == 3 ){
fprintf(stderr, "Nothing is opened\n");
exit(1);
}
FD_SET(fdin, &set); /* uchest' stdin */
maxfd = max(maxfd, fdin );
nopen -= 2; /* stdout i stderr ne uchastvuyut v select */
timeout.tv_sec = 10; /* sekund */
timeout.tv_usec = 0; /* millisekund */
/* nfds - eto KOLICHESTVO pervyh deskriptorov, kotorye nado
* prosmatrivat'. Zdes' mozhno ispol'zovat'
* nfds = NOFILE; (kol-vo VSEH deskriptorov )
* ili nfds = maxfd+1; (kol-vo = nomer poslednego+1)
* ( +1 t.k. numeraciya fd idet s nomera 0, a kolichestvo - s 1).
*/
nfds = maxfd + 1;
while( nopen ){
rset = set; rtimeout = timeout; /* kopiruem, t.k. izmenyatsya */
/* oprashivat' mozhno FIFO-fajly, terminaly, pty, socket-y, stream-y */
nready = select( nfds, &rset, NULL, NULL, &rtimeout );
/* Esli vmesto &rtimeout napisat' NULL, to ozhidanie budet
* beskonechnym (poka ne sob'yut signalom)
*/
if( nready <= 0 ){ /* nichego ne postupilo */
fprintf(stderr, "Timed out, nopen=%d\n", nopen);
continue;
}
A. Bogatyrev, 1992-95 - 261 - Si v UNIX
/* opros gotovyh deskriptorov */
for(f=0; f < nfds; f++ )
if( FD_ISSET(f, &rset)){ /* deskriptor f gotov */
int n;
/* Vyzov FIONREAD pozvolyaet zaprosit'
* chislo bajt gotovyh k peredache
* cherez deskriptor.
*/
if(ioctl(f, FIONREAD, &n) < 0)
perror("FIONREAD");
else printf("%s have %d bytes.\n", N(f), n);
if((n = read(f, buf, sizeof buf)) <= 0 ){
eof:
FD_CLR(f, &set); /* isklyuchit' */
close(f); nopen--;
fprintf(stderr, "EOF in %s\n", N(f));
} else {
fprintf(stderr, "\n%d bytes from %s:\n", n, N(f));
write(fdout, buf, n);
if( n == 4 && !strncmp(buf, "end\n", 4))
/* ncmp, t.k. buf mozhet ne okanchivat'sya \0 */
goto eof;
}
}
}
exit(0);
}
6.11.2. V kachestve samostoyatel'noj raboty predlagaem vam primer programmy, vedushchej
protokol seansa raboty. Informaciyu o psevdoterminalah izuchite samostoyatel'no.
A. Bogatyrev, 1992-95 - 262 - Si v UNIX
/*
* script.c
* Programma polucheniya trassirovki raboty drugih programm.
* Ispol'zuetsya sistemnyj vyzov oprosa gotovnosti kanalov
* vvoda/vyvoda select() i psevdoterminal (para ttyp+ptyp).
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/param.h> /* NOFILE */
#include <sys/times.h>
#include <sys/wait.h>
#include <errno.h>
#ifdef TERMIOS
# include <termios.h>
# define TERMIO struct termios
# define GTTY(fd, tadr) tcgetattr(fd, tadr)
# define STTY(fd, tadr) tcsetattr(fd, TCSADRAIN, tadr)
#else
# include <termio.h>
# define TERMIO struct termio
# define GTTY(fd, tadr) ioctl(fd, TCGETA, tadr)
# define STTY(fd, tadr) ioctl(fd, TCSETAW, tadr)
#endif
A. Bogatyrev, 1992-95 - 263 - Si v UNIX
#ifdef __SVR4
# include <stropts.h> /* STREAMS i/o */
extern char *ptsname();
#endif
#if defined(ISC2_2)
# include <sys/bsdtypes.h>
#else
# include <sys/select.h>
#endif
#ifndef BSIZE
# define BSIZE 512
#endif
#define LOGFILE "/usr/spool/scriptlog"
#define max(a,b) ((a) > (b) ? (a) : (b))
extern int errno;
TERMIO told, tnew, ttypmodes;
FILE *fpscript = NULL; /* fajl s trassirovkoj (esli nado) */
int go = 0;
int scriptflg = 0;
int halfflag = 0; /* HALF DUPLEX */
int autoecho = 0;
char *protocol = "typescript";
#define STDIN 0 /* fileno(stdin) */
#define STDOUT 1 /* fileno(stdout) */
#define STDERR 2 /* fileno(stderr) */
/* kakie kanaly svyazany s terminalom? */
int tty_stdin, tty_stdout, tty_stderr;
int TTYFD;
void wm_checkttys(){
TERMIO t;
tty_stdin = ( GTTY(STDIN, &t) >= 0 );
tty_stdout = ( GTTY(STDOUT, &t) >= 0 );
tty_stderr = ( GTTY(STDERR, &t) >= 0 );
if ( tty_stdin ) TTYFD = STDIN;
else if( tty_stdout ) TTYFD = STDOUT;
else if( tty_stderr ) TTYFD = STDERR;
else {
fprintf(stderr, "Cannot access tty\n");
exit(7);
}
}
A. Bogatyrev, 1992-95 - 264 - Si v UNIX
/* Opisatel' trassiruemogo processa */
struct ptypair {
char line[25]; /* terminal'naya liniya: /dev/ttyp? */
int pfd; /* deskriptor master pty */
long in_bytes; /* prochteno bajt s klaviatury */
long out_bytes; /* poslano bajt na ekran */
int pid; /* identifikator processa */
time_t t_start, t_stop; /* vremya zapuska i okonchaniya */
char *command; /* zapushchennaya komanda */
} PP;
/* |ta funkciya vyzyvaetsya pri okonchanii trassiruemogo processa -
* po signalu SIGCLD
*/
char Reason[128];
void ondeath(sig){
int pid;
extern void wm_done();
int status;
int fd;
/* vyyavit' prichinu okonchaniya processa */
while((pid = wait(&status)) > 0 ){
if( WIFEXITED(status))
sprintf( Reason, "Pid %d died with retcode %d",
pid, WEXITSTATUS(status));
else if( WIFSIGNALED(status)) {
sprintf( Reason, "Pid %d killed by signal #%d",
pid, WTERMSIG(status));
#ifdef WCOREDUMP
if(WCOREDUMP(status)) strcat( Reason, " Core dumped" );
#endif
} else if( WIFSTOPPED(status))
sprintf( Reason, "Pid %d suspended by signal #%d",
pid, WSTOPSIG(status));
}
wm_done(0);
}
void wm_init(){
wm_checkttys();
GTTY(TTYFD, &told);
/* Skonstruirovat' "syroj" rezhim dlya nashego _bazovogo_ terminala */
tnew = told;
tnew.c_cc[VINTR] = '\0';
tnew.c_cc[VQUIT] = '\0';
tnew.c_cc[VERASE] = '\0';
tnew.c_cc[VKILL] = '\0';
#ifdef VSUSP
tnew.c_cc[VSUSP] = '\0';
#endif
A. Bogatyrev, 1992-95 - 265 - Si v UNIX
/* CBREAK */
tnew.c_cc[VMIN] = 1;
tnew.c_cc[VTIME] = 0;
tnew.c_cflag &= ~(PARENB|CSIZE);
tnew.c_cflag |= CS8;
tnew.c_iflag &= ~(ISTRIP|ICRNL);
tnew.c_lflag &= ~(ICANON|ECHO|ECHOK|ECHOE|XCASE);
tnew.c_oflag &= ~OLCUC;
/* no ostavit' c_oflag ONLCR i TAB3, esli oni byli */
/* mody dlya psevdoterminala */
ttypmodes = told;
/* ne vypolnyat' preobrazovaniya na vyvode:
* ONLCR: \n --> \r\n
* TAB3: \t --> probely
*/
ttypmodes.c_oflag &= ~(ONLCR|TAB3);
(void) signal(SIGCLD, ondeath);
}
void wm_fixtty(){
STTY(TTYFD, &tnew);
}
void wm_resettty(){
STTY(TTYFD, &told);
}
/* Podobrat' svobodnyj psevdoterminal dlya trassiruemogo processa */
struct ptypair wm_ptypair(){
struct ptypair p;
#ifdef __SVR4
p.pfd = (-1); p.pid = 0;
p.in_bytes = p.out_bytes = 0;
/* Otkryt' master side pary pty (eshche est' slave) */
if((p.pfd = open( "/dev/ptmx", O_RDWR)) < 0 ){
/* |to kloniruemyj STREAMS driver.
* Poskol'ku on kloniruemyj, to est' sozdayushchij novoe psevdoustrojstvo
* pri kazhdom otkrytii, to na master-storone mozhet byt' tol'ko
* edinstvennyj process!
*/
perror( "Open /dev/ptmx" );
goto err;
}
A. Bogatyrev, 1992-95 - 266 - Si v UNIX
# ifdef notdef
/* Sdelat' prava dostupa k slave-storone moimi. */
if( grantpt (p.pfd) < 0 ){
perror( "grantpt");
exit(errno);
}
# endif
/* Razblokirovat' slave-storonu psevdoterminala:
pozvolit' pervyj open() dlya nee */
if( unlockpt(p.pfd) < 0 ){
perror( "unlockpt");
exit(errno);
}
/* Poluchit' i zapisat' imya novogo slave-ustrojstva-fajla. */
strcpy( p.line, ptsname(p.pfd));
#else
register i;
char c;
struct stat st;
p.pfd = (-1); p.pid = 0;
p.in_bytes = p.out_bytes = 0;
strcpy( p.line, "/dev/ptyXX" );
for( c = 'p'; c <= 's'; c++ ){
p.line[ strlen("/dev/pty") ] = c;
p.line[ strlen("/dev/ptyp")] = '0';
if( stat(p.line, &st) < 0 )
goto err;
for(i=0; i < 16; i++){
p.line[ strlen("/dev/ptyp") ] =
"0123456789abcdef" [i] ;
if((p.pfd = open( p.line, O_RDWR )) >= 0 ){
p.line[ strlen("/dev/") ] = 't';
return p;
}
}
}
#endif
err: return p;
}
A. Bogatyrev, 1992-95 - 267 - Si v UNIX
/* Vedenie statistiki po vyzovam script */
void write_stat( in_bytes, out_bytes, time_here , name, line, at )
long in_bytes, out_bytes;
time_t time_here;
char *name;
char *line;
char *at;
{
FILE *fplog;
struct flock lock;
if((fplog = fopen( LOGFILE, "a" )) == NULL )
return;
lock.l_type = F_WRLCK;
lock.l_whence = 0;
lock.l_start = 0;
lock.l_len = 0; /* zablokirovat' ves' fajl */
fcntl ( fileno(fplog), F_SETLKW, &lock );
fprintf( fplog, "%s (%s) %ld bytes_in %ld bytes_out %ld secs %s %s %s",
PP.command, Reason, in_bytes, out_bytes,
time_here, name, line, at );
fflush ( fplog );
lock.l_type = F_UNLCK;
lock.l_whence = 0;
lock.l_start = 0;
lock.l_len = 0; /* razblokirovat' ves' fajl */
fcntl ( fileno(fplog), F_SETLK, &lock );
fclose ( fplog );
}
void wm_done(sig){
char *getlogin(), *getenv(), *logname = getlogin();
time( &PP.t_stop ); /* zapomnit' vremya okonchaniya */
wm_resettty(); /* vosstanovit' rezhim bazovogo terminala */
if( fpscript )
fclose(fpscript);
if( PP.pid > 0 ) kill( SIGHUP, PP.pid ); /* "obryv svyazi" */
if( go ) write_stat( PP.in_bytes, PP.out_bytes,
PP.t_stop - PP.t_start,
logname ? logname : getenv("LOGNAME"),
PP.line, ctime(&PP.t_stop) );
printf( "\n" );
exit(0);
}
A. Bogatyrev, 1992-95 - 268 - Si v UNIX
/* Zapusk trassiruemogo processa na psevdoterminale */
void wm_startshell (ac, av)
char **av;
{
int child, fd, sig;
if( ac == 0 ){
static char *avshell[] = { "/bin/sh", "-i", NULL };
av = avshell;
}
if((child = fork()) < 0 ){
perror("fork");
wm_done(errno);
}
if( child == 0 ){ /* SON */
if( tty_stdin )
setpgrp(); /* otkaz ot upravlyayushchego terminala */
/* poluchit' novyj upravlyayushchij terminal */
if((fd = open( PP.line, O_RDWR )) < 0 ){
exit(errno);
}
/* zakryt' lishnie kanaly */
if( fpscript )
fclose(fpscript);
close( PP.pfd );
#ifdef __SVR4
/* Push pty compatibility modules onto stream */
ioctl(fd, I_PUSH, "ptem"); /* pseudo tty module */
ioctl(fd, I_PUSH, "ldterm"); /* line discipline module */
ioctl(fd, I_PUSH, "ttcompat"); /* BSD ioctls module */
#endif
/* perenapravit' kanaly, svyazannye s terminalom */
if( fd != STDIN && tty_stdin ) dup2(fd, STDIN);
if( fd != STDOUT && tty_stdout ) dup2(fd, STDOUT);
if( fd != STDERR && tty_stderr ) dup2(fd, STDERR);
if( fd > STDERR )
(void) close(fd);
/* ustanovit' mody terminala */
STTY(TTYFD, &ttypmodes);
/* vosstanovit' reakcii na signaly */
for(sig=1; sig < NSIG; sig++)
signal( sig, SIG_DFL );
execvp(av[0], av);
system( "echo OBLOM > HELP.ME");
perror("execl");
exit(errno);
A. Bogatyrev, 1992-95 - 269 - Si v UNIX
} else { /* FATHER */
PP.pid = child;
PP.command = av[0];
time( &PP.t_start ); PP.t_stop = PP.t_start;
signal( SIGHUP, wm_done );
signal( SIGINT, wm_done );
signal( SIGQUIT, wm_done );
signal( SIGTERM, wm_done );
signal( SIGILL, wm_done );
signal( SIGBUS, wm_done );
signal( SIGSEGV, wm_done );
}
}
char buf[ BSIZE ]; /* bufer dlya peredachi dannyh */
/* /dev/pty? /dev/ttyp?
ekran *--------* *--------*
/||| | | PP.pfd | |
|||||<-STDOUT--| moj |<---------| psevdo |<-STDOUT---|
\||| |terminal| |terminal|<-STDERR---|trassiruemyj
|(bazovyj) | | |process
------- | | STDIN | | |
|.....|-STDIN--> |----------> |--STDIN--->|
|_____| | | | |
klaviatura *--------* *--------*
master slave
*/
/* Opros deskriptorov */
void wm_select(){
int nready;
int nfds;
int maxfd;
int nopen; /* chislo oprashivaemyh deskriptorov */
register f;
fd_set s