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