home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / minix1 / mxkerm.msg < prev    next >
Internet Message Format  |  2020-01-01  |  45KB

  1. Date:     26 February 1988
  2. From:     Adrian Godwin, 78 Putnoe Street, Bedford, England.
  3. Subject:  Additional C-Kermit Implementation Notes for Minix
  4.  
  5.     Modifying C-Kermit 4D-061 for use under Andrew Tanenbaum's 'MINIX' has
  6. required rather more changes to Minix than to Kermit. The C source files are
  7. included; they all began as the CK---.--- files for the 4D(061) distribution
  8. set. Here the names have been changed to MX---.--- . Hints, fixes and library
  9. changes are also attached - most of these are applicable for anyone
  10. implementing a serial i/o driver for Minix, and many library fixes are useful
  11. for porting other utilities. 
  12.  
  13.     Kermit cannot be built under version 1.1 Minix, as it compiles to
  14. about 85K and the initial Minix assembler cannot separate I&D model output.
  15. The executable file was therefore built under MS-DOS using the Lattice 3.10 C 
  16. compiler. Some care is needed in cross-compiling : see the notes in Tanenbaum's
  17. book about libraries, and read the enclosed Lattice makefile, cktker.mak.
  18.  
  19. A port of this version to the latest C-Kermit version 4E(070) is now underway 
  20. and will be released at some future time.
  21.  
  22. Associated Files:
  23. =================
  24.  
  25.     I have included with this help file (cktker.hlp) the following :
  26.  
  27. mxcmai.c    -    Modified Kermit source files
  28. mxutio.c    
  29. mxufio.c    
  30. mxuusr.c    
  31. mxuus3.c    
  32. mxcfns.c    
  33.  
  34. mxtker.mak    -    Makefile for Lattice 'lmk' and 3.10 compiler.
  35.  
  36. mxtker.inp    -    linker command file (included in mxtker.mak)
  37.  
  38. mxtker.boo    -    boo encoding of cktker.out
  39.  
  40.  
  41. Changes to Kermit:
  42. ==================
  43.  
  44. ckutio.c    As usual, most of the changes are here. Fixes are largely
  45.         a matter of picking a suitable option from the variety of
  46.         code that exists. Use V7, but enable use of MYREAD and DON'T
  47.         use the kmem (initrawq) functions. The uucp locks directory
  48.         doesn't exist, but since it's a PC and you'll know if you're
  49.         using the serial port, remove locking code altogether.
  50.         For some reason, Lattice returns the unsigned char result
  51.         of myread() expanded to a signed int. A badly read character
  52.         can thus appear to other functions as an error code. Make
  53.         myread return ((int)ch) & 0377.
  54.         Ttychk and conchk need fixing to stop the accesses to kmem. 
  55.         Reorder ttychk so that if myread() finds nothing in it's 
  56.         buffer, it will execute other (system dependent) code, 
  57.         like FIONREAD, to determine if typahead exists in the system 
  58.         buffers. Then implement a FIONREAD ioctl call if you want. 
  59.         It makes a slight improvement to 'connect' performance, but 
  60.         is more worthwhile for conchk(), where nonblocking read is
  61.         no use. The transfer escape characters can then be used.
  62.  
  63. ckcmai.c    Shorten the help message provided for remote help -
  64.         Lattice can only compile strings less than 256 bytes long.
  65.         Why isn't this list compiled from the command tables, like
  66.         the command line interpreter's command completion?
  67.  
  68. ckuusr.c    Minix has a curious interpretation of printf, requiring
  69. ckuus3.c    "%D" to indicate a long integer. Change all the "%ld" formats
  70.         in these two files, or better still fix the do_printf() in the
  71.         Minix library to accept K&R formats.
  72.         Also calculate efficiency, etc. for statistics using long
  73.         constants - Lattice makes a hash of it otherwise.
  74.  
  75. ckcfns.c    Calculate effective speed & efficiency for tlog using 
  76.         specific long arithmetic. Use a long for x, the argument
  77.         for F101 in TLOG.
  78.  
  79. ckufio.c    No include file <sys/files.h>. Lattice would prefer the 
  80.         functions FILE *fopen(), *fdopen() to be declared as such.
  81.         The function zclosif() attempts to kill a completed child
  82.         process. If it has already completed, this fails, but MINIX
  83.         still wants a wait() call to clean up the memory allocation,
  84.         otherwise a fwe invocations of 'remote directory' will use
  85.         up any remaining memory. This problem will also occur in the
  86.         minix shell if you invoke all processes in the background.
  87.         Use the shell command 'wait' to clean up completed processes
  88.         (zombies).
  89.  
  90.  
  91. Changes to Minix library routines.
  92. ==================================
  93.  
  94.     I started developing this set of library fixes when building Micro-
  95. Emacs. A few of them may be unnecessary, but I recommend you fix them anyway.
  96. Many routines are here because they aren't in the standard library.
  97. The lattice makefile includes all these routines as an object called 'emlib' -
  98. this is for debugging purposes only (they should be built into the library).
  99. While not strictly a library change, fix dos2out before attempting to 
  100. convert this large executable : the calculation of load_size will be wrong
  101. unless all the operands are forced to type long. The calculation of the
  102. total memory allocation, a_totb is also wrong, since it includes (for the 
  103. separate I & D model) the text size a_text in the sum. This results in a 
  104. 154K memory allocation, which is not permitted. chmem =20000 will fix it.
  105.  
  106.     Some include file changes. Stdio.h defines a macro 'puts()' as an 
  107. fputs() to stdout. This is wrong, as puts() should append a newline while 
  108. fputs() doesn't. Remove the macro and create a library function.
  109.  
  110. There is no <sys/dir.h>. It should contain:
  111.  
  112. /* dir.h
  113.  *
  114.  * The structure of a directory entry.
  115.  */
  116.  
  117. #define NAME_SIZE 14        /* defined in fs/const.h */
  118.  
  119. struct direct {
  120.   inode_nr d_ino;        /* inode number */
  121.   char d_name[NAME_SIZE];    /* name fills it out to 16 bytes */
  122. } ;
  123.  
  124.     The file h/type.h is needed by kermit, but included as <sys/types.h>. 
  125. Create a <sys/types.h> that includes h/type.h.
  126.  
  127.     Sgtty.h also needs some changes to define tty functions that don't 
  128. exist in standard Minix. The kermit I have built used this version of sgtty.h, 
  129. so if you want to use that executable, your tty driver must correspond. The 
  130. i/o functions are an upwards compatible superset of the Minix sgtty functions,
  131. and are inspired by SCO Xenix. Note particularly the implementation of
  132. stty() and gtty() as macros. 
  133.  
  134. /* sgtty.h 
  135.  *
  136.  * Data structures for IOCTL. 
  137.  */
  138.  
  139. struct sgttyb {
  140.   char sg_ispeed;        /* input speed      (has precedence)    */
  141.   char sg_ospeed;        /* output speed     (may be ignored)    */
  142.   char sg_erase;        /* erase character             */
  143.   char sg_kill;            /* kill character             */
  144.   int  sg_flags;        /* mode flags                 */
  145. };
  146.  
  147. /* simulate stty and gtty with ioctl                     */
  148.  
  149. #define stty(fd, arg)    ioctl(fd, TIOCSETP, arg)
  150. #define gtty(fd, arg)    ioctl(fd, TIOCGETP, arg)
  151.  
  152.  
  153. struct tchars {
  154.   char t_intrc;            /* SIGINT char                 */
  155.   char t_quitc;            /* SIGQUIT char             */
  156.   char t_startc;        /* start output (initially CTRL-Q)     */
  157.   char t_stopc;            /* stop output    (initially CTRL-S)     */
  158.   char t_eofc;            /* EOF (initially CTRL-D)         */
  159.   char t_brkc;            /* input delimiter (like nl)         */
  160. };
  161.  
  162. /* Fields in sg_flags. */
  163.  
  164. #define COOKED       0000000    /* neither CBREAK nor RAW         */
  165. #define    TANDEM         0000001    /* XON XOFF flowcontrol on input    */
  166. #define CBREAK         0000002    /* enable cbreak mode             */
  167. #define LCASE        0000004    /* support lower case            */
  168. #define ECHO         0000010    /* echo input                 */
  169. #define CRMOD         0000020    /* map lf to cr + lf             */
  170. #define RAW         0000040    /* enable raw mode             */
  171. #define ODDP         0000100    /* odd parity                */
  172. #define EVENP        0000200    /* even parity                */
  173. #define ANYP         0000300    /* parity not set / ignored        */
  174. #define XTABS         0006000    /* do tab expansion             */
  175.  
  176.  
  177. #define TIOCGETP (('t'<<8) | 8)
  178. #define TIOCSETP (('t'<<8) | 9)
  179. #define TIOCGETC (('t'<<8) | 18)
  180. #define TIOCSETC (('t'<<8) | 17)
  181.  
  182. /*
  183.  * I can't find a 'standard' definition for this one, so I've assigned
  184.  * a '1' arbitrarily.
  185.  */
  186.  
  187. #define FIONREAD (('f'<<8) | 1)
  188.  
  189. /* Baud rate settings for async ttys. */
  190.  
  191. #define B0        0        /* hangup line (drop DTR)    */
  192. #define B50        1
  193. #define B75        2
  194. #define B110        3
  195. #define B134        4
  196. #define B150        5
  197. #define B200        6
  198. #define B300        7
  199. #define B600        8
  200. #define B1200        9
  201. #define B1800        10
  202. #define B2400        11
  203. #define B4800        12
  204. #define B9600        13
  205.  
  206. #define EXTA        14
  207. #define B19200        EXTA
  208.  
  209. #define EXTB        15
  210. #define B38400        EXTB
  211.  
  212.  
  213.     There are no time management calls in MINIX other than time, times()
  214. etc. I have defined asctime() and gmtime() functions based on the conversions
  215. done in date.c, but avoided support for timezones and daylight saving.
  216.  
  217.  
  218. /* time.h : structure and function definitions for time library calls. */
  219.  
  220. struct tm {
  221.     int    tm_sec;
  222.     int    tm_min;
  223.     int    tm_hour;
  224.     int    tm_mday;
  225.     int    tm_mon;
  226.     int    tm_year;
  227.     int    tm_wday;
  228.     int    tm_yday;
  229.     int    tm_isdst;    /* not currently supported */
  230. };
  231.  
  232. extern struct tm    *localtime();
  233. extern struct tm     *gmtime();
  234. extern char        *asctime();
  235.  
  236.  
  237. /* system(command)
  238.  * 
  239.  * Code copied from the MINIX make utility 'mysystem' function, with
  240.  * minor modifications.
  241.  */
  242.  
  243. char *_defpath = "/bin/sh";
  244.  
  245. system(cmd)
  246. char *cmd;
  247. {
  248.     int ccode,pid,status;
  249.     char *shell, *getenv();
  250.  
  251.     if ( (shell = getenv("SHELL")) == NULL)
  252.     shell = _defpath;
  253.  
  254.     if ( (pid = fork()) == 0 ) {    /* child execs a shell */
  255.     execl(shell,shell,(*cmd ? "-c" : "-i"),cmd,0);
  256.     }
  257.  
  258.     if ( pid < 0 ) {              /* parent waits for child */
  259.     return(pid);
  260.     }
  261.     else {
  262.     while ( ((ccode = wait(&status)) != pid) && (ccode != -1))
  263.         ;
  264.     return(status);
  265.     }
  266. }
  267.  
  268.  
  269.  
  270. /* This function copied from the MINIX library, but with some bugs fixed. */
  271.  
  272. char *getenv(name)
  273. register char *name;
  274. {
  275.   extern char **environ;
  276.   register char **v = environ, *p, *q;
  277.  
  278.   while ((p = *v++) != NULL) {        /* fix : increment v */
  279.     q = name;
  280.     while (*p++ == *q)
  281.         if (*q++ == 0)
  282.             break;    /* fix : break rather than continue */
  283.     if (*(p - 1) != '=')
  284.         continue;
  285.     return(p);
  286.   }
  287.   return(0);
  288. }
  289.  
  290.  
  291.  
  292. /* getc
  293.  *
  294.  * fixed to ensure _count always indicates number of chars in the buffer.
  295.  * - used to indicate correctly only when zero. Decrement _count after
  296.  * taking char from recently filled buffer. Note : fseek() had a bodge 
  297.  * which allowed it to work with this error. This also requires a fix.
  298.  *
  299.  * note when rebuilding library : this module should appear AFTER scanf,
  300.  * which calls it, to ensure it can be found.
  301.  */
  302.  
  303. #include "stdio.h"
  304.  
  305. getc(iop)
  306. FILE *iop;
  307. {
  308.     int ch;
  309.  
  310.     if ( testflag(iop, (_EOF | _ERR )))
  311.         return (EOF); 
  312.  
  313.     if ( !testflag(iop, READMODE) ) 
  314.         return (EOF);
  315.  
  316.     if (--iop->_count < 0){        /* changed from <= 0 */
  317.  
  318.         if ( testflag(iop, UNBUFF) )
  319.             iop->_count = read(iop->_fd,&ch,1);
  320.         else 
  321.             iop->_count = read(iop->_fd,iop->_buf,BUFSIZ);
  322.  
  323.         if (iop->_count <= 0){
  324.             if (iop->_count == 0)
  325.                 iop->_flags |= _EOF;
  326.             else 
  327.                 iop->_flags |= _ERR;
  328.  
  329.             return (EOF);
  330.         }
  331.         else {
  332.             iop->_ptr = iop->_buf;
  333.             iop->_count--;        /* inserted */
  334.         }
  335.     }
  336.  
  337.     if (testflag(iop,UNBUFF))
  338.         return (ch & CMASK);
  339.     else
  340.         return (*iop->_ptr++ & CMASK);
  341. }
  342.  
  343. /* ttyname() - missing library function. Gets the inode number of the 
  344.  * passed filedescriptor, then searches the /dev directory for a match.
  345.  * Returns a pointer to the name (static data) or NULL.
  346.  */
  347.  
  348. #include "stdio.h"
  349. #include "stat.h"
  350. #include "dir.h"
  351.  
  352. char *ttyname(fd)
  353. int fd;
  354. {
  355.   struct stat s;
  356.   int n, dd;
  357.   char *p = NULL;
  358.   static struct direct d[2];    /* big enough to nul-terminate name */
  359.  
  360.   if (fstat(fd, &s) != 0 || (s.st_mode&S_IFMT) != S_IFCHR)
  361.     return(NULL);
  362.  
  363.   /* isatty. now search /dev for the inode. */
  364.  
  365.     n = s.st_ino;
  366.     if ((dd = open("/dev", 0)) < 0)
  367.         return NULL;        /* cannot read directory */
  368.  
  369.     while (read( dd, d, sizeof(*d)) == sizeof(*d)) 
  370.  
  371.         if (d[0].d_ino == n) {
  372.             p = d[0].d_name;
  373.             p[sizeof(*d)] = '\0';
  374.             break;
  375.         }
  376.     close(dd);
  377.     return(p);
  378. }
  379.  
  380. /* fdopen() - missing library function. Returns a file pointer set up from
  381.  * the passed file descriptor, or NULL if it can't.
  382.  */
  383.  
  384. #include "stdio.h"
  385.  
  386. FILE *fdopen(fd,mode)
  387. int fd;
  388. char *mode;
  389. {
  390.     register int i;
  391.     FILE *fp;
  392.     char *malloc();
  393.     int flags = 0;
  394.  
  395.     for (i = 0; _io_table[i] != 0 ; i++) 
  396.         if ( i >= NFILES )
  397.             return(NULL);
  398.  
  399.     switch(*mode){
  400.  
  401.     case 'w':
  402.     case 'a': 
  403.         flags |= WRITEMODE;
  404.         break;
  405.  
  406.     case 'r':
  407.         flags |= READMODE;    
  408.         break;
  409.  
  410.     default:
  411.         return(NULL);
  412.     }
  413.  
  414.  
  415.     if (( fp = (FILE *) malloc (sizeof( FILE))) == NULL )
  416.         return(NULL);
  417.  
  418.     fp->_count = 0;
  419.     fp->_fd = fd;
  420.     fp->_flags = flags;
  421.     fp->_buf = malloc( BUFSIZ );
  422.     if ( fp->_buf == NULL )
  423.         fp->_flags |=  UNBUFF;
  424.     else 
  425.         fp->_flags |= IOMYBUF;
  426.  
  427.     fp->_ptr = fp->_buf;
  428.     _io_table[i] = fp;
  429.     return(fp);
  430. }
  431.  
  432. /* modified exit() which flushes stdio buffers before dying. This means
  433.  * standard io library is included whether you wanted it or not - add a
  434.  * dummy "_cleanup() {}" declaration to avoid it if you're writing very
  435.  * small programs like the Minix utilities. This is more likely to
  436.  * be compatible with other UNIX programs than the exit() in the standard
  437.  * release.
  438.  */ 
  439.  
  440. #include "../include/lib.h"
  441.  
  442. int exit(status)
  443. int status;
  444. {
  445.   _cleanup();
  446.   return callm1(MM, EXIT, status, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR);
  447. }
  448.  
  449.  
  450. /* strncmp() - library module, changed to operate correctly when
  451.  * n is zero. (causes problem in kermit interactive command processor)
  452.  */
  453.  
  454. int strncmp(s1, s2, n)
  455. register char *s1, *s2;
  456. int n;
  457. {
  458. /* Compare two strings, but at most n characters. */
  459.  
  460.   while (n-- != 0) {
  461.     if (*s1 != *s2) return(*s1 - *s2);
  462.     if (*s1 == 0) return(0);
  463.     s1++;
  464.     s2++;
  465.   }
  466.   return 0;
  467. }
  468.  
  469.  
  470. /* These functions based on the similar operations in date.c. No support
  471.  * for timezones or daylight saving - localtime() currently just calls 
  472.  * gmtime() and returns the results unmodified.
  473.  */
  474.  
  475. #include <time.h>
  476.  
  477. static int _days_per_month[] =
  478.   { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  479.  
  480. static char *_months[] =
  481.   { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  482.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  483.  
  484. static char *_days[] =
  485.   { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  486.  
  487. static long _s_p_min  = 60L;
  488. static long _s_p_hour = 60L * 60L;
  489. static long _s_p_day  = 60L * 60L * 24L;
  490. static long _s_p_year = 60L * 60L * 24L * 365L;
  491.  
  492.  
  493. char *asctime(tmp)
  494. struct tm *tmp;
  495. {
  496.   static char _asctbuf[30];
  497.  
  498.   sprintf(_asctbuf,"%s %s %2d %02d:%02d:%02d %d\n", 
  499.         _days[tmp->tm_wday], _months[tmp->tm_mon], 
  500.         tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, 
  501.         tmp->tm_year + 1900); 
  502.  
  503.   return _asctbuf;
  504. }
  505.  
  506. struct tm *gmtime(ltp)
  507. long *ltp;
  508. {
  509.   static struct tm _tm;
  510.   char  *p;
  511.   long   t = *ltp;
  512.  
  513.   for (p = (char *)&_tm; p < ((char *)&_tm) + sizeof(_tm); p++)
  514.     *p = 0;
  515.  
  516.   /* get day-of-week. (Add 4 because 1.1.70 was a Thursday) */
  517.   _tm.tm_wday = ((t / _s_p_day)+4L) % 7L;
  518.  
  519.   /* reduce t by years and days, leaving seconds this year. */
  520.   while (t >= _s_p_year) {
  521.     if (((_tm.tm_year + 2) % 4) == 0)
  522.         t -= _s_p_day;
  523.     _tm.tm_year += 1;
  524.     t -= _s_p_year;
  525.   }
  526.   _tm.tm_yday = t / _s_p_day;
  527.  
  528.   /* adjust february for leap year and reduce to seconds this month */
  529.   if (((_tm.tm_year + 2) % 4) == 0)
  530.     _days_per_month[1]++;
  531.   while ( t >= (_days_per_month[_tm.tm_mon] * _s_p_day))
  532.     t -= _days_per_month[_tm.tm_mon++] * _s_p_day;
  533.   _days_per_month[1] = 28;
  534.   _tm.tm_year += 70;
  535.  
  536.   /* find day of month */
  537.   _tm.tm_mday = 1;
  538.   while (t >= _s_p_day) {
  539.     t -= _s_p_day;
  540.     _tm.tm_mday++;
  541.   }
  542.  
  543.   /* and time today */
  544.   while (t >= _s_p_hour) {
  545.     t -= _s_p_hour;
  546.     _tm.tm_hour++;
  547.   }
  548.   while (t >= _s_p_min) {
  549.     t -= _s_p_min;
  550.     _tm.tm_min++;
  551.   }
  552.   _tm.tm_sec = (int) t;
  553.  
  554.   return &_tm;
  555. }
  556.  
  557. struct tm *localtime(ltp)
  558. long *ltp;
  559. {
  560.   return gmtime(ltp);
  561. }
  562.  
  563. The following fixes may be regarded as dubious, since they actually make
  564. the operation of minix less like standard unix. However, the performance
  565. of unbuffered tty output (as used in kermit help messages and command 
  566. completion) is so appalling I felt something was needed. 
  567.  
  568. If a call is made to any of the string output routines (printf, puts ..),
  569. when the channel is unbuffered, then a temporary buffer is attached to
  570. the channel for the duration of the call and flushed at the end. Since
  571. setting stdout unbuffered freed it's buffer allocation, the buffer used
  572. for this is probably already available on the heap. 
  573.  
  574. The speed-up effect of this on unbuffered i/o (as used for all stderr
  575. in the system utilities) is startling, and I can't think why these changes 
  576. should cause any incompatibilities. 
  577.  
  578.  
  579. #include <stdio.h>
  580.  
  581. /* tmpbuf() creates a temporary buffer for the file, if possible.
  582.  * Only call this if the file is unbuffered.
  583.  */
  584.  
  585. static char *_locbuf = NULL;
  586. static int _locflags;
  587.  
  588. static void tmpbuf(file)
  589. register FILE *file;
  590. {
  591.     if (_locbuf == NULL)        /* no buffer - get one */
  592.     _locbuf = malloc(BUFSIZ);
  593.  
  594.     if (_locbuf == NULL)        /* no space - cannot buffer */
  595.     return;
  596.  
  597.     _locflags = file->_flags;
  598.     file->_flags &= ~UNBUFF;
  599.     file->_buf = file->_ptr = _locbuf;
  600.     file->_count = 0;
  601. }
  602.  
  603. /* endbuf() restores the FILE flags and flushes the buffer if required. */
  604.  
  605. static endbuf(file, fflag)
  606. register FILE *file;
  607. int fflag;
  608. {
  609.     if (fflag || file->_buf == _locbuf)
  610.     fflush(file);
  611.  
  612.     if (file->_buf == _locbuf && _locbuf != NULL) {
  613.  
  614.     /* temporary buffer used - restore old settings */
  615.     file->_buf = file->_ptr = NULL;
  616.     file->_count = 0;
  617.     file->_flags = _locflags;
  618.     }
  619. }
  620.  
  621.  
  622.  
  623. /* puts() - replaces macro in stdio.h, since a newline is required 
  624.  * after the given string. 
  625.  */
  626.  
  627. puts(s)
  628. register char *s;
  629. {
  630.     if ( testflag(stdout, UNBUFF) )
  631.         tmpbuf(stdout);
  632.  
  633.     while ( *s ) 
  634.         putc(*s++,stdout);
  635.     putc('\n',stdout);
  636.  
  637.     endbuf(stdout, 1);    /* always flush */
  638. }
  639.  
  640.  
  641. fputs(s,file)
  642. register char *s;
  643. FILE *file;
  644. {
  645.     if ( testflag(file, UNBUFF) )
  646.         tmpbuf(file);
  647.  
  648.     while ( *s ) 
  649.         putc(*s++,file);
  650.  
  651.     endbuf(file, 0);
  652. }
  653.  
  654.  
  655.  
  656. fprintf (file, fmt, args)
  657. FILE *file;
  658. char *fmt;
  659. int args;
  660. {
  661.     if ( testflag(file, UNBUFF) )
  662.         tmpbuf(file);
  663.  
  664.     _doprintf (file, fmt, &args);
  665.  
  666.     endbuf(file, testflag(file, PERPRINTF));
  667. }
  668.  
  669.  
  670. printf (fmt, args)
  671. char *fmt;
  672. int args;
  673. {
  674.     if ( testflag(stdout, UNBUFF) )
  675.         tmpbuf(stdout);
  676.  
  677.     _doprintf (stdout, fmt, &args);
  678.  
  679.     endbuf(stdout, testflag(stdout, PERPRINTF));
  680. }
  681.  
  682.  
  683.  
  684. Changes to Minix
  685. ================
  686.  
  687.     Here's where it gets more experimental: I've done the following fixes 
  688. to MINIX in order to get Kermit working or to solve problems I've found along 
  689. the way. Some may not be essential - I didn't want to rebuild with all my 
  690. older fixes taken out just to determine which are necessary. Since all the 
  691. fixes are for major problems, they're worth doing anyway.
  692.  
  693.  
  694.     Any SIGALRM sent to a process will cancel all pending alarms. This 
  695. has the effect of suspending forever (say) the update process if another
  696. process executes sleep(). MM/signal.c, check_sig() resets not only the 
  697. alarm bit for process getting the signal, but for all others. Resetting the
  698. bit needs to be conditional on the state of the send_sig flag. The clock
  699. task looks in the message field CLOCK_PROC_NR for the process number of
  700. the task that needs the alarm signal. MM actually puts the number in the 
  701. field PROC_NR, which is not the same. Hence the clock actually sends the 
  702. signal to process 0, which is taken to mean all processes.
  703.  
  704.     It is possible for a message from FS to a task to be overwritten 
  705. before it reaches its destination if the task supports multiple minor
  706. devices and process suspension, as a TTY driver with more than one line 
  707. would. This will occur if the TTY driver has suspended a task, and is busy
  708. with an interrupt request (which will revive the task) when FS send-receives
  709. a message about a different minor device. FS will be forced to wait for the 
  710. task until the REVIVE message appears. FS is then sent the message, since it 
  711. appears to be waiting for a reply. The tty task will then look for more 
  712. messages and will find the FS request, now overwritten with the REVIVE message. 
  713. The TTY will be given its own REVIVE message and reject it as invalid. FS 
  714. will continue to wait for a reply to it's TTY request (now lost) and hang.
  715.  
  716.     I have fixed this by treating REVIVE messages in the same way as 
  717. signals they are rather similar, since they are sent to a caller which is not
  718. waiting for them. Instead of replying with a REVIVE message, I call
  719. set_revive(), a function similar to cause_sig() which sets a flag in the 
  720. proc table p_flags and saves the reply status in a new member, p_status.
  721. Inform() is then called for FS as well as MM, and searches for the revive
  722. flag. Any pending revives are actioned as a HARDWARE message, just like a 
  723. kernel signal. FS will then no longer get a reply from a previous message
  724. when send-receiving to a task, since the REVIVE call comes from HARDWARE.
  725. FS can thus be simplified in areas like rw_dev().
  726.  
  727.     FS may panic with a "couldn't revive anyone" error when it recovers 
  728. a process from waiting on a broken pipe. If the pipe is reading from two file
  729. descriptors (as in the Kermit procedure to pipe process stdout and stderr to 
  730. a remote machine for the REMOTE HOST command), then FS/misc.c, do_exit() will 
  731. call FS/open.c, do_close() for both descriptors. On each call, FS/pipe.c, 
  732. release() will be called, setting the REVIVING flag for the reading process (e.g. the
  733. Kermit) twice, and also incrementing 'reviving'. FS/main.c, get_work() revives
  734. the reading process, then can't find a process to account for the other 
  735. increment of 'reviving'. release() should check for the REVIVING flag before
  736. calling revive() again.
  737.  
  738.     FS can send a cancel message for an out-of-range tty number when a 
  739. process is killed, due to FS/pipe.c, do_unpause() not setting up the global 
  740. fp before calling get_filp(). The tty driver does not catch out-of-range line 
  741. numbers on either messages or characters from the overrun buffer, and can 
  742. index a tty structure that doesn't exist.
  743.  
  744.     Do_unpause() doesn't check that get_filp() returns a valid result.
  745. The return can be NULL if the file (that the process is paused on) has been
  746. closed. This happens if a signal killed the process. The distributed code of 
  747. MM/signal.c, check_sig() differs from the printed listings in Tanenbaum's 
  748. book in the order of execution of unpause() and sig_proc(). This means that 
  749. FS expects do_unpause to have a process to restart, when the filehandles it 
  750. should look at are already closed. It seems OK to return the order of the 
  751. calls to their original state, but I'd like to know why they changed.
  752.  
  753.     CONTROL-S will pause the user process by waiting before replying to 
  754. FS. This causes the entire system to get stuck waiting for FS to return from
  755. the TTY call. The TTY needs to SUSPEND the process on output, just as it does 
  756. on input. Do_cancel() in the TTY driver also has to change to ensure that FS
  757. obtains a reply or a REVIVE when output is completed.
  758.  
  759. The following comments are not bug reports, but code changes I have done
  760. to improve the system's performance at high interrupt rates. I found
  761. character processing at 9600 baud on an 8MHz 8086 a problem, and had to
  762. make several changes :
  763.  
  764.     When interrupts are arriving at high speed (characters at 9600 baud),
  765. some may be lost due to the time taken to process an interrupt in the kernel.
  766. This time may be reduced by moving the check for now-ready tasks in kernel/
  767. proc.c, interrupt() to the end of mini-rec(). This reduces the number of calls
  768. to mini-send within the interrupt handler to 1. If a clock interrupt was 
  769. pending, the clock processing can double or triple the time needed to process
  770. the call to interrupt(). If the tty task is often scheduled, the clock task
  771. is often behind, since tasks do not preempt each other. Thus clock interrupts
  772. often are pending when interrupt() is called at high repetition rates.
  773.  
  774.     However, placing this check in mini_rec() will mean that interrupts 
  775. will always be serviced when the task becomes ready. This, while reducing the 
  776. interrupt latency of a task, means that fast interrupts can make the task 
  777. permanently computable. No other process can then obtain any time to read 
  778. the incoming characters, and the task's buffer fills. Flowcontrol and possibly
  779. ignoring unbufferable input makes it possible to empty the buffer again. 
  780. File transfer with Kermit is OK, because the packets are considerably smaller
  781. than the task's buffer, but long packets, sliding windows and connect sessions
  782. will have problems if flowcontrol is not possible. 
  783.  
  784.     The mini_send() call in interrupt() should also be removed and the 
  785. message copy be performed directly within interrupt(), avoiding the 
  786. substantial checking performed on call parameters, which should be unnecessary 
  787. on messages within the kernel. The message copy might also be performed by 
  788. a faster version of copy_mess(), for use within the kernel, which need not
  789. span segments. Better still, since interrupts may overwrite each other and 
  790. only affect a task's private data area, they tend to use a single known
  791. buffer for data transfer to the calling task. (I haven't made these last 2 changes yet).
  792.  
  793.  
  794.  
  795. The following code fragments indicate most of the above fixes. Since the code
  796. was originally that distributed by Prentice-Hall, I have included their
  797. copyright notice.
  798.  
  799. /* Copyright (C) 1987 by Prentice-Hall, Inc.  Permission is hereby granted to
  800.  * private individuals and educational institutions to modify and
  801.  * redistribute the binary and source programs of this system to other
  802.  * private individuals and educational institutions for educational and
  803.  * research purposes.  For corporate or commercial use, permission from
  804.  * Prentice-Hall is required.  In general, such permission will be granted,
  805.  * subject to a few conditions.
  806.  */
  807.  
  808. =============
  809. Kernel/proc.c
  810. =============
  811.  
  812. /*===========================================================================*
  813.  *                interrupt                     * 
  814.  *===========================================================================*/
  815. PUBLIC interrupt(task, m_ptr)
  816. int task;            /* number of task to be started */
  817. message *m_ptr;            /* interrupt message to send to the task */
  818. {
  819. /* An interrupt has occurred.  Schedule the task that handles it. */
  820.  
  821.   int this_bit;
  822.   register struct proc *dest_ptr;
  823.  
  824. #ifdef ibmpc
  825.   /* Re-enable the 8259A interrupt controller. */
  826.   this_bit = 1 << (-task);
  827.   port_out(INT_CTL, ENABLE);    /* this re-enables the 8259A controller chip */
  828.   if (pc_at) port_out(INT2_CTL, ENABLE);    /* re-enable second 8259A */
  829. #endif
  830.  
  831.   /* Try to send the interrupt message to the indicated task. */
  832.   this_bit = 1 << (-task);
  833.   dest_ptr = proc_addr(task);
  834.   if ( ((dest_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING) 
  835.   &&   (dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == HARDWARE) ) {
  836.  
  837.     /* Destination is indeed waiting for this message. */
  838.     cp_mess(HARDWARE, proc[NR_TASKS+HARDWARE].p_map[D].mem_phys, m_ptr, 
  839.             dest_ptr->p_map[D].mem_phys, dest_ptr->p_messbuf);
  840.     dest_ptr->p_flags &= ~RECEIVING;    /* deblock destination */
  841.     if (dest_ptr->p_flags == 0) ready(dest_ptr);
  842.     busy_map &= ~this_bit;    /* turn off the bit in case it was on */
  843.  
  844.   } else {
  845.  
  846.     /* The message could not be sent to the task; it was not waiting. */
  847.     if (task == CLOCK) {
  848.         lost_ticks++;
  849.     } else {
  850.         busy_map |= this_bit;        /* mark task as busy */
  851.         task_mess[-task] = m_ptr;    /* record message pointer */
  852.     }
  853.   }
  854.  
  855.   /* If a task has just been readied and a user is running, run the task. */
  856.   if (rdy_head[TASK_Q] != NIL_PROC && (cur_proc >= 0 || cur_proc == IDLE))
  857.     pick_proc();
  858. }
  859.  
  860. /*===========================================================================*
  861.  *                mini_send                     * 
  862.  *===========================================================================*/
  863. PUBLIC int mini_send(caller, dest, m_ptr)
  864. int caller;            /* who is trying to send a message? */
  865. int dest;            /* to whom is message being sent? */
  866. message *m_ptr;            /* pointer to message buffer */
  867. {
  868. /* Send a message from 'caller' to 'dest'.  If 'dest' is blocked waiting for
  869.  * this message, copy the message to it and unblock 'dest'.  If 'dest' is not
  870.  * waiting at all, or is waiting for another source, queue 'caller'.
  871.  */
  872.  
  873.   register struct proc *caller_ptr, *dest_ptr, *next_ptr;
  874.   vir_bytes vb;            /* message buffer pointer as vir_bytes */
  875.   vir_clicks vlo, vhi;        /* virtual clicks containing message to send */
  876.   vir_clicks len;        /* length of data segment in clicks */
  877.  
  878.   /* User processes are only allowed to send to FS and MM.  Check for this. */
  879.   if (caller >= LOW_USER && (dest != FS_PROC_NR && dest != MM_PROC_NR))
  880. #ifdef DEBUG
  881.     if (dest != SYSTASK) return(E_BAD_DEST);
  882. #else
  883.     return(E_BAD_DEST);
  884. #endif
  885.   caller_ptr = proc_addr(caller);    /* pointer to source's proc entry */
  886.   dest_ptr = proc_addr(dest);    /* pointer to destination's proc entry */
  887.   if (dest_ptr->p_flags & P_SLOT_FREE) return(E_BAD_DEST);    /* dead dest */
  888.  
  889.   /* Check for messages wrapping around top of memory or outside data seg. */
  890.   len = caller_ptr->p_map[D].mem_len;
  891.   vb = (vir_bytes) m_ptr;
  892.   vlo = vb >> CLICK_SHIFT;    /* vir click for bottom of message */
  893.   vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT;    /* vir click for top of message */
  894.   if (vhi < vlo || vhi - caller_ptr->p_map[D].mem_vir >= len)return(E_BAD_ADDR);
  895.  
  896.   /* Check to see if 'dest' is blocked waiting for this message. */
  897.  
  898.   if ( ((dest_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING) &&
  899.         (dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == caller) ) {
  900.  
  901.     /* Destination is indeed waiting for this message. */
  902.     cp_mess(caller, caller_ptr->p_map[D].mem_phys, m_ptr, 
  903.                 dest_ptr->p_map[D].mem_phys, dest_ptr->p_messbuf);
  904.     dest_ptr->p_flags &= ~RECEIVING;    /* deblock destination */
  905.     if (dest_ptr->p_flags == 0) ready(dest_ptr);
  906.  
  907.   } else {
  908.     /* Destination is not waiting.  Block and queue caller. */
  909.     if (caller == HARDWARE) return(E_OVERRUN);
  910.     caller_ptr->p_messbuf = m_ptr;
  911.     caller_ptr->p_flags |= SENDING;
  912.     unready(caller_ptr);
  913.  
  914.     /* Process is now blocked.  Put in on the destination's queue. */
  915.     if ( (next_ptr = dest_ptr->p_callerq) == NIL_PROC) {
  916.         dest_ptr->p_callerq = caller_ptr;
  917.     } else {
  918.         while (next_ptr->p_sendlink != NIL_PROC)
  919.             next_ptr = next_ptr->p_sendlink;
  920.         next_ptr->p_sendlink = caller_ptr;
  921.     }
  922.     caller_ptr->p_sendlink = NIL_PROC;
  923.   }
  924.  
  925. #ifdef DEBUG
  926. /* This message has been successfully handled. See if it should be saved
  927.  * in the message log. 
  928.  */
  929.   if ((save_flags[caller+NR_TASKS] & CALL_LOG) 
  930.   ||  (save_flags[dest  +NR_TASKS] & DEST_LOG))
  931.     save_msg(caller, dest, m_ptr);
  932. #endif
  933.  
  934.   return(OK);
  935. }
  936.  
  937.  
  938. /*===========================================================================*
  939.  *                mini_rec                     * 
  940.  *===========================================================================*/
  941. PRIVATE int mini_rec(caller, src, m_ptr)
  942. int caller;            /* process trying to get message */
  943. int src;            /* which message source is wanted (or ANY) */
  944. message *m_ptr;            /* pointer to message buffer */
  945. {
  946. /* A process or task wants to get a message.  If one is already queued,
  947.  * acquire it and deblock the sender.  If no message from the desired source
  948.  * is available, block the caller.  No need to check parameters for validity.
  949.  * Users calls are always sendrec(), and mini_send() has checked already.  
  950.  * Calls from the tasks, MM, and FS are trusted.
  951.  */
  952.  
  953.   register struct proc *caller_ptr, *sender_ptr, *prev_ptr;
  954.   int sender, this_bit;
  955.   int locked = FALSE;
  956.  
  957.   caller_ptr = proc_addr(caller);    /* pointer to caller's proc structure */
  958.  
  959.   /* if we're ready to receive, find a sender. Else just block. */
  960.   if ((caller_ptr->p_flags & SENDING) == 0) {
  961.  
  962.     /* Check to see if a message from desired source is already available. */
  963.  
  964.     sender_ptr = caller_ptr->p_callerq;
  965.     while (sender_ptr != NIL_PROC) {
  966.     sender = sender_ptr - proc - NR_TASKS;
  967.     if (src == ANY || src == sender) {
  968.         /* An acceptable message has been found. */
  969.         cp_mess(sender, sender_ptr->p_map[D].mem_phys, sender_ptr->p_messbuf,
  970.                     caller_ptr->p_map[D].mem_phys, m_ptr);
  971.         sender_ptr->p_flags &= ~SENDING;    /* deblock sender */
  972.         if (sender_ptr->p_flags == 0) ready(sender_ptr);
  973.         if (sender_ptr == caller_ptr->p_callerq)
  974.             caller_ptr->p_callerq = sender_ptr->p_sendlink;
  975.         else
  976.             prev_ptr->p_sendlink = sender_ptr->p_sendlink;
  977.         return(OK);
  978.     }
  979.     prev_ptr = sender_ptr;
  980.     sender_ptr = sender_ptr->p_sendlink;
  981.     }
  982.  
  983.     /* If the caller is a task, there may be an interrupt waiting. 
  984.      * Check now, rather than before checking callers, so fast interrupts
  985.      * give the process a chance to absorb them. Lock between determining the
  986.      * state of busy_map and setting RECEIVING so an interrupt occurring 
  987.      * between cannot be forgotten.
  988.      */
  989.  
  990.     if (caller < HARDWARE && (src == ANY || src == HARDWARE)) {
  991.  
  992.     lock();
  993.     locked = TRUE;
  994.     this_bit = 1 << (-caller);
  995.     if (busy_map & this_bit) {
  996.         cp_mess(HARDWARE, proc[NR_TASKS+HARDWARE].p_map[D].mem_phys,
  997.             task_mess[-caller], caller_ptr->p_map[D].mem_phys,
  998.             m_ptr);
  999.         busy_map &= ~this_bit;        /* must be locked here too */
  1000.         restore();
  1001.         return(OK);
  1002.     }
  1003.     }
  1004.   }
  1005.  
  1006.   /* No suitable message is available. Block the process trying to receive. */
  1007.  
  1008.   caller_ptr->p_getfrom = src;
  1009.   caller_ptr->p_messbuf = m_ptr;
  1010.   caller_ptr->p_flags |= RECEIVING;
  1011.   if (locked) restore();
  1012.   unready(caller_ptr);
  1013.  
  1014.   /* If MM has just blocked and there are kernel signals pending, now is the
  1015.    * time to tell MM about them, since it will be able to accept the message.
  1016.    * Also applies to revive messages for FS. No problems with receive-before-
  1017.    * send, since (src == ANY) cannot be true for sendrec messages.
  1018.    */
  1019.  
  1020.   if (rev_procs > 0 && caller == FS_PROC_NR && src == ANY) inform(FS_PROC_NR);
  1021.   if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) inform(MM_PROC_NR);
  1022.   return(OK);
  1023. }
  1024.  
  1025. ===============
  1026. kernel\system.c
  1027. ===============
  1028.  
  1029. /*===========================================================================*
  1030.  *                set_revive                     * 
  1031.  *===========================================================================*/
  1032. PUBLIC set_revive(proc_nr, status)
  1033. int proc_nr;
  1034. int status;
  1035. /* Similar function to cause_sig. Tasks sending a revive message directly 
  1036.  * to FS may collide with a send in the opposite direction. Therefore, this
  1037.  * function saves the event and associated status (error code, bytes read etc)
  1038.  * in the target process' proc entry, and the kernel informs FS later with
  1039.  * inform(FS_PROC_NR). It would be nice to share the proc table's p_pending
  1040.  * member with MM, but the signals recorded in it may be ignored - only MM
  1041.  * knows. Note that the device's id is lost, and only FS may receive a message.
  1042.  * This is not currently a problem.
  1043.  */
  1044. {
  1045.   register struct proc *rp;
  1046.  
  1047.   rp = proc_addr(proc_nr);
  1048.   if (rp->p_flags & NEED_REVIVE) 
  1049.     panic ("cannot revive twice",proc_nr);
  1050.   rp->p_flags |= NEED_REVIVE;
  1051.   rp->p_rstatus = status;
  1052.   rev_procs++;
  1053.  
  1054.   inform(FS_PROC_NR);        /* perhaps it's safe to do it now ? */
  1055. }
  1056.  
  1057. /*===========================================================================*
  1058.  *                inform                         * 
  1059.  *===========================================================================*/
  1060. PUBLIC inform(proc_nr)
  1061. int proc_nr;            /* MM_PROC_NR or FS_PROC_NR */
  1062. {
  1063. /* When a signal is detected by the kernel (e.g., DEL), or generated by a task
  1064.  * (e.g. clock task for SIGALRM), cause_sig() is called to set a bit in the
  1065.  * p_pending field of the process to signal.  Then inform() is called to see
  1066.  * if MM is idle and can be told about it.  Whenever MM blocks, a check is
  1067.  * made to see if 'sig_procs' is nonzero; if so, inform() is called.
  1068.  * Likewise REVIVE messages for FS. These are sent only when FS is ready to
  1069.  * receive. Code here changed to check recipient is not blocked on send as
  1070.  * well as receive.
  1071.  */
  1072.  
  1073.   register struct proc *rp, *mmp;
  1074.   int r;
  1075.  
  1076.   /* If MM/FS is not waiting for new input, forget it. */
  1077.   mmp = proc_addr(proc_nr);
  1078.   if ( ((mmp->p_flags & RECEIVING) == 0) || mmp->p_getfrom != ANY) return;
  1079.  
  1080.   /* If MM is waiting for new input,  find a process with pending signals. */
  1081.   if (proc_nr == MM_PROC_NR) {
  1082.     for (rp = proc_addr(0); rp < proc_addr(NR_PROCS); rp++)
  1083.     if (rp->p_pending != 0) {
  1084.         m.m_type = KSIG;
  1085.         m.PROC1 = rp - proc - NR_TASKS;
  1086.         m.SIG_MAP = rp->p_pending;
  1087.         sig_procs--;
  1088.         if ((r=mini_send(HARDWARE, proc_nr, &m)) != OK) 
  1089.             panic("can't inform MM : ", r);
  1090.         rp->p_pending = 0;    /* the ball is now in MM's court */
  1091.         return;
  1092.     }
  1093.   }
  1094.  
  1095.   /* If FS is waiting for new input,  find a process with pending revive. */
  1096.   else if (proc_nr == FS_PROC_NR) {
  1097.     for (rp = proc_addr(0); rp < proc_addr(NR_PROCS); rp++)
  1098.     if (rp->p_flags & NEED_REVIVE) {
  1099.         m.m_type = REVIVE;
  1100.         m.REP_PROC_NR = rp - proc - NR_TASKS;
  1101.         m.REP_STATUS  = rp->p_rstatus;
  1102.         rev_procs--;
  1103.         rp->p_flags &= ~NEED_REVIVE;
  1104.         if ((r=mini_send(HARDWARE, proc_nr, &m)) != OK) 
  1105.             panic("can't inform FS : ", r);
  1106.         return;        /* the ball is now in FS's court */
  1107.     }
  1108.   }
  1109. }
  1110.  
  1111. ===========
  1112. mm/signal.c
  1113. ===========
  1114.  
  1115. /*===========================================================================*
  1116.  *                check_sig                     *
  1117.  *===========================================================================*/
  1118. PRIVATE int check_sig(proc_id, sig_nr, send_uid)
  1119. int proc_id;            /* pid of process to signal, or 0 or -1 */
  1120. int sig_nr;            /* which signal to send (1-16) */
  1121. uid send_uid;            /* identity of process sending the signal */
  1122. {
  1123. /* Check to see if it is possible to send a signal.  The signal may have to be
  1124.  * sent to a group of processes.  This routine is invoked by the KILL system
  1125.  * call, and also when the kernel catches a DEL or other signal. SIGALRM too.
  1126.  */
  1127.  
  1128.   register struct mproc *rmp;
  1129.   int count, send_sig;
  1130.   unshort mask;
  1131.   extern unshort core_bits;
  1132.  
  1133.   if (sig_nr < 1 || sig_nr > NR_SIGS) return(EINVAL);
  1134.   count = 0;            /* count # of signals sent */
  1135.   mask = 1 << (sig_nr - 1);
  1136.  
  1137.   /* Search the proc table for processes to signal.  Several tests are made:
  1138.    *     - if proc's uid != sender's, and sender is not superuser, don't signal
  1139.    *    - if specific process requested (i.e., 'procpid' > 0, check for match
  1140.    *    - if a process has already exited, it can't receive signals
  1141.    *    - if 'proc_id' is 0 signal everyone in same process group except caller
  1142.    */
  1143.   for (rmp = &mproc[INIT_PROC_NR + 1]; rmp < &mproc[NR_PROCS]; rmp++ ) {
  1144.     if ( (rmp->mp_flags & IN_USE) == 0) continue;
  1145.     send_sig = TRUE;    /* if it's FALSE at end of loop, don't signal */
  1146.     if (send_uid != rmp->mp_effuid && send_uid != SUPER_USER)send_sig=FALSE;
  1147.     if (proc_id > 0 && proc_id != rmp->mp_pid) send_sig = FALSE;
  1148.     if (rmp->mp_flags & HANGING) send_sig = FALSE;   /*don't wake the dead*/
  1149.     if (proc_id == 0 && mp->mp_procgrp != rmp->mp_procgrp) send_sig = FALSE;
  1150.     if (send_uid == SUPER_USER && proc_id == -1) send_sig = TRUE;
  1151.  
  1152.     /* SIGALARM is a little special.  When a process exits, a clock signal
  1153.      * can arrive just as the timer is being turned off.  Also, turn off
  1154.      * ALARM_ON bit when timer goes off to keep it accurate.
  1155.      */
  1156.     /* Change : only reset the ALARM_ON bit for processes that are
  1157.      * going to get the signal - don't wipe out all the other alarms! 
  1158.      */
  1159.  
  1160.     if (send_sig && sig_nr == SIGALRM) {
  1161.         if ( (rmp->mp_flags & ALARM_ON) == 0) continue;
  1162.         rmp->mp_flags &= ~ALARM_ON;
  1163.     }
  1164.  
  1165.     if (send_sig == FALSE || rmp->mp_ignore & mask) continue;
  1166.  
  1167.     /* If process is hanging on PAUSE, WAIT, tty, pipe, etc. release it. */
  1168.     unpause(rmp - mproc);    /* check to see if process is paused */
  1169.     count++;
  1170.  
  1171.     /* Send the signal or kill the process, possibly with core dump. */
  1172.     sig_proc(rmp, sig_nr);
  1173.  
  1174.     if (proc_id > 0) break;    /* only one process being signalled */
  1175.   }
  1176.  
  1177.   /* If the calling process has killed itself, don't reply. */
  1178.   if ((mp->mp_flags & IN_USE) == 0 || (mp->mp_flags & HANGING))dont_reply =TRUE;
  1179.   return(count > 0 ? OK : ESRCH);
  1180. }
  1181.  
  1182.  
  1183. /*===========================================================================*
  1184.  *                set_alarm                     *
  1185.  *===========================================================================*/
  1186. PUBLIC int set_alarm(proc_nr, sec)
  1187. int proc_nr;            /* process that wants the alarm */
  1188. unsigned sec;            /* how many seconds delay before the signal */
  1189. {
  1190. /* This routine is used by do_alarm() to set the alarm timer.  It is also
  1191.  * to turn the timer off when a process exits with the timer still on.
  1192.  */
  1193.  
  1194.   int remaining;
  1195.  
  1196.   m_sig.m_type = SET_ALARM;
  1197.  
  1198. /*---  m_sig.PROC_NR = proc_nr;    ---*/
  1199.   m_sig.CLOCK_PROC_NR = proc_nr;    /* clock uses different member     */
  1200.  
  1201.   m_sig.DELTA_TICKS = HZ * sec;
  1202.   if (sec != 0)
  1203.     mproc[proc_nr].mp_flags |= ALARM_ON;    /* turn ALARM_ON bit on */
  1204.   else
  1205.     mproc[proc_nr].mp_flags &= ~ALARM_ON;    /* turn ALARM_ON bit off */
  1206.  
  1207.   /* Tell the clock task to provide a signal message when the time comes. */
  1208.   if (sendrec(CLOCK, &m_sig) != OK) panic("alarm er", NO_NUM);
  1209.   remaining = (int) m_sig.SECONDS_LEFT;
  1210.   return(remaining);
  1211. }
  1212.  
  1213. =========
  1214. fs/pipe.c
  1215. =========
  1216.  
  1217.  
  1218. /*===========================================================================*
  1219.  *                release                         *
  1220.  *===========================================================================*/
  1221. PUBLIC release(ip, call_nr, count)
  1222. register struct inode *ip;    /* inode of pipe */
  1223. int call_nr;            /* READ or WRITE */
  1224. int count;            /* max number of processes to release */
  1225. {
  1226. /* Check to see if any process is hanging on the pipe whose inode is in 'ip'.
  1227.  * If one is, and it was trying to perform the call indicated by 'call_nr'
  1228.  * (READ or WRITE), release it.
  1229.  */
  1230.  
  1231.   register struct fproc *rp;
  1232.  
  1233.   /* Search the proc table. */
  1234.   for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) {
  1235.     if (rp->fp_suspended == SUSPENDED 
  1236.     && (rp->fp_fd & BYTE) == call_nr 
  1237.     &&  rp->fp_revived != REVIVING
  1238.     &&  rp->fp_filp[rp->fp_fd>>8]->filp_ino == ip) {
  1239.         revive(rp - fproc, 0);
  1240.         susp_count--;    /* keep track of who is suspended */
  1241.         if (--count == 0) return;
  1242.     }
  1243.   }
  1244. }
  1245.  
  1246.  
  1247. /*===========================================================================*
  1248.  *                do_unpause                     *
  1249.  *===========================================================================*/
  1250. PUBLIC int do_unpause()
  1251. {
  1252. /* A signal has been sent to a user who is paused on the file system.
  1253.  * Abort the system call with the EINTR error message.
  1254.  */
  1255.  
  1256.   register struct fproc *rfp;
  1257.   int proc_nr, task, susfd;
  1258.   struct filp *f;
  1259.   dev_nr dev;
  1260.   extern struct filp *get_filp();
  1261.  
  1262.   if (who > MM_PROC_NR) return(EPERM);
  1263.   proc_nr = pro;
  1264.   if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("unpause err 1", proc_nr);
  1265.   rfp = &fproc[proc_nr];
  1266.   if (rfp->fp_suspended == NOT_SUSPENDED) return(OK);
  1267.   task = -rfp->fp_task;
  1268.  
  1269.   if (task != XPIPE) {
  1270.     susfd = rfp->fp_fd >> 8;
  1271.     f = rfp->fp_filp[susfd];    /* don't use get_filp - fp is wrong */
  1272.     if (susfd > NR_FDS || susfd < 0 || f == NIL_FILP) {
  1273.         panic("unpause err 4", proc_nr);
  1274.     }
  1275.     dev = f->filp_ino->i_zone[0];    /* device on which proc is hanging */
  1276.     mess.TTY_LINE = (dev >> MINOR) & BYTE;
  1277.     mess.PROC_NR = proc_nr;
  1278.     mess.m_type = CANCEL;
  1279.     if (sendrec(task, &mess) != OK) panic("unpause err 2", NO_NUM);
  1280.     while (mess.REP_PROC_NR != proc_nr) {
  1281.         revive(mess.REP_PROC_NR, mess.REP_STATUS);
  1282.         if (receive(task, &m) != OK) panic("unpause err 3", NO_NUM);
  1283.     }
  1284.     revive(proc_nr, EINTR);    /* signal interrupted call */
  1285.   }
  1286.  
  1287.   return(OK);
  1288. }
  1289.  
  1290.  
  1291.  
  1292.     I haven't included my asynchronous tty driver, as I don't use IBM 
  1293. hardware and anybody interested in Kermit on Minix probably has their own
  1294. ideas. However, an outline of the driver follows for anybody who might be
  1295. interested (I'm happy to answer any further queries or comments as I 
  1296. continue this development).
  1297.  
  1298.     The tty driver is split into three parts:
  1299.  
  1300. tty.c        Contains the 'device independent' part of the original driver.
  1301. screen.c    Contains hardware-specific code for console screen and
  1302.         keyboard driver.
  1303. async.c        Contains hardware-specific code for asynchronous serial
  1304.         tty driver.
  1305.  
  1306.  
  1307. The tty_struct structure is extended, and now contains fields for ioctl
  1308. function addresses (so do_ioctl sets characters, modes etc then calls
  1309. the device - dependent code for baud rate, etc) and a single character
  1310. echo function for that port. Tty_ramqueue is a union of the integer queue 
  1311. required for screen driving and a char queue for buffering output, ready 
  1312. for async interrupts. The flag used for RUNNING / STOPPED indication is 
  1313. now a bit mask, another bit being STALLED, to indicate that an XOFF has 
  1314. been sent to the device on the other end of the serial line. 
  1315.  
  1316. TANDEM flowcontrol is implemented in tty.c, flipping the STALLED bit
  1317. and echoing an XOFF or XON when the buffer contents cross high and
  1318. low water marks. Interrupt processing is sufficiently fast that XOFF
  1319. does not need to be sent if the overrun queue is getting full.
  1320.  
  1321. Transmitter ready interrupts cause a character to be read from tty_rwords
  1322. and written to the UART. When tty_rwords is empty, a message is sent to
  1323. the tty task, and the queue is refilled. In the meantime, the transmitter
  1324. is disabled to avoid continuous interrupts.
  1325.  
  1326. Characters and refill requests are both sent through the same interrupt
  1327. message - use of the suggested TTY_O_DONE message would cause other
  1328. interrupts to be overwritten and lost. The message is now a structure 
  1329. consisting of a count, a queue of ints for character and line number,
  1330. and a bit map for refill requests. Thus refill requests are not lost
  1331. if the input queue is full. The kernel interrupt() call is only
  1332. made if the count of queued characters incremented from zero, or a bit
  1333. was set for the first time in the bitmap. This reduces the interrupt
  1334. processing time if an interrupt had already been sent, but had not been
  1335. processed.
  1336.  
  1337. A bug exists in the processing of input characters - DEL and QUIT send
  1338. a signal to the proper process only if it is the only login. The calculation
  1339. of the proc to be signalled as 'LOW_USER + line + 1' is wrong, since the 
  1340. process slot above the first user is occupied not by the second user's first
  1341. shell but by the update process. For proper processing of tty signals in 
  1342. a multiple-user (or multiple screen) system, FS should tell the tty task when 
  1343. a tty is controlling a process family.
  1344.  
  1345.