TOPIC   #  NAME           DESCRIPTION
-----  --- ------------   ------------------------------------------------
timing 124 adjtimex       - adjtimex   - tune kernel clock
timing 105 getitimer      - get  value of an interval timer
timing  78 gettimeofday   - get time
timing 162 nanosleep      - pause execution for a specified time
timing 168 poll           - wait for some event on a file descriptor
timing  98 profil         - UNIMPLEMENTED execution time profile
timing  82 select         - synchronous I/O
timing 104 setitimer      - set an interval timer
timing  79 settimeofday   - set time
timing  25 stime          - set time
timing  13 time           - get time in seconds
timing  43 times          - get process times
timing 114 wait4          - wait for process termination, BSD style
timing   7 waitpid        - wait for process termination  

----------------------------------------------------------------------------
124 adjtimex       - adjtimex   - tune kernel clock
----------------------------------------------------------------------------
  mov   eax,124
  mov   ebx,struc ptr
  int   80h

       Linux  uses  David L. Mills' clock adjustment algorithm (see RFC 1305).
       The system call adjtimex reads and optionally sets  adjustment  parame-
       ters  for  this  algorithm.   It  takes a pointer to a timex structure,
       updates kernel parameters from  field  values,  and  returns  the  same
       structure  with  current  kernel values.  This structure is declared as
       follows:

              struct timex {
                  int modes;           /* mode selector */
                  long offset;         /* time offset (usec) */
                  long freq;           /* frequency offset (scaled ppm) */
                  long maxerror;       /* maximum error (usec) */
                  long esterror;       /* estimated error (usec) */
                  int status;          /* clock command/status */
                  long constant;       /* pll time constant */
                  long precision;      /* clock precision (usec) (read only) */
                  long tolerance;      /* clock frequency tolerance (ppm)
                                          (read only) */
                  struct timeval time; /* current time (read only) */
                  long tick;           /* usecs between clock ticks */
              };

       The modes field determines which parameters, if any, to  set.   It  may
       contain a bitwise-or combination of zero or more of the following bits:

              #define ADJ_OFFSET            0x0001 /* time offset */
              #define ADJ_FREQUENCY         0x0002 /* frequency offset */
              #define ADJ_MAXERROR          0x0004 /* maximum time error */
              #define ADJ_ESTERROR          0x0008 /* estimated time error */
              #define ADJ_STATUS            0x0010 /* clock status */
              #define ADJ_TIMECONST         0x0020 /* pll time constant */
              #define ADJ_TICK              0x4000 /* tick value */
              #define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */

       Ordinary users are restricted to a zero value for mode.  Only the supe-
       ruser may set any parameters.

RETURN VALUE
       On success, adjtimex returns the clock state:

              #define TIME_OK   0 /* clock synchronized */
              #define TIME_INS  1 /* insert leap second */
              #define TIME_DEL  2 /* delete leap second */
              #define TIME_OOP  3 /* leap second in progress */
              #define TIME_WAIT 4 /* leap second has occurred */
              #define TIME_BAD  5 /* clock not synchronized */

       On failure, adjtimex returns -1 and sets errno.

ERRORS
       EFAULT buf does not point to writable memory.

       EPERM  buf.mode is non-zero and the user is not super-user.

       EINVAL An  attempt  is  made  to  set buf.offset to a value outside the
              range -131071 to +131071, or to set buf.status to a value  other
              than  those  listed above, or to set buf.tick to a value outside
              the range 900000/HZ to 1100000/HZ, where HZ is the system  timer
              interrupt frequency.

CONFORMING TO
       adjtimex  is Linux specific and should not be used in programs intended
       to be portable.  There is a similar but less general  call  adjtime  in
       SVr4.

SEE ALSO
       settimeofday(2)


----------------------------------------------------------------------------
105 getitimer      - get  value of an interval timer
----------------------------------------------------------------------------
  mov eax,105
  mov ebx,which
  mov ecx,theValue
  int 80h

  which:   dd   ?
  ivalue:  dd   ?
  ovalue:  dd   ?

       The system provides each  process  with  three  interval  timers,  each
       decrementing in a distinct time domain.  When any timer expires, a sig-
       nal is sent to the process, and the timer (potentially) restarts.

       ITIMER_REAL    decrements in real time, and delivers SIGALRM upon expi-
                      ration.

       ITIMER_VIRTUAL decrements  only  when  the  process  is  executing, and
                      delivers SIGVTALRM upon expiration.

       ITIMER_PROF    decrements both when the process executes and  when  the
                      system  is  executing on behalf of the process.  Coupled
                      with ITIMER_VIRTUAL, this timer is usually used to  pro-
                      file  the time spent by the application in user and ker-
                      nel space.  SIGPROF is delivered upon expiration.

       Timer values are defined by the following structures:
            struct itimerval {
                struct timeval it_interval; /* next value */
                struct timeval it_value;    /* current value */
            };
            struct timeval {
                long tv_sec;                /* seconds */
                long tv_usec;               /* microseconds */
            };

       The function getitimer fills the structure indicated by value with  the
       current  setting  for the timer indicated by which (one of ITIMER_REAL,
       ITIMER_VIRTUAL, or ITIMER_PROF).  The element it_value is  set  to  the
       amount  of  time  remaining  on the timer, or zero if the timer is dis-
       abled.  Similarly, it_interval is set to the reset value.

       Timers decrement from it_value to zero, generate a signal, and reset to
       it_interval.   A  timer  which  is set to zero (it_value is zero or the
       timer expires and it_interval is zero) stops.

       Both tv_sec and tv_usec are significant in determining the duration  of
       a timer.

       Timers  will  never  expire before the requested time, instead expiring
       some short, constant time afterwards, dependent  on  the  system  timer
       resolution  (currently 10ms).  Upon expiration, a signal will be gener-
       ated and the timer reset.  If the timer expires while  the  process  is
       active (always true for ITIMER_VIRT) the signal will be delivered imme-
       diately when generated.  Otherwise the delivery will  be  offset  by  a
       small time dependent on the system loading.


RETURN VALUE
       On  success,  zero is returned.  On error, -1 is returned, and errno is
       set appropriately.

ERRORS
       EFAULT value or ovalue are not valid pointers.

       EINVAL which is not one of ITIMER_REAL, ITIMER_VIRT, or ITIMER_PROF.

SEE ALSO
       gettimeofday(2), sigaction(2), signal(2)

BUGS
       Under Linux, the generation and delivery of a signal are distinct,  and
       there each signal is permitted only one outstanding event.  It's there-
       fore conceivable that under pathologically heavy  loading,  ITIMER_REAL
       will  expire  before  the  signal  from  a previous expiration has been
       delivered.  The second signal in such an event will be lost.



----------------------------------------------------------------------------
 78 gettimeofday   - get time
----------------------------------------------------------------------------
  mov  eax,078
  mov  ebx,tv
  mov  ecx,tz
  int  80h

       The function gettimeofday can get the time as
       well  as a timezone.  The tv argument is a timeval struct, as specified
       in <sys/time.h>:

       struct timeval {
               time_t         tv_sec;        /* seconds */
               suseconds_t    tv_usec;  /* microseconds */
       };

       and gives the number of seconds and microseconds since the  Epoch  (see
       time(2)).  The tz argument is a timezone :

       struct timezone {
               int  tz_minuteswest; /* minutes W of Greenwich */
               int  tz_dsttime;     /* type of dst correction */
       };

       The  use  of  the timezone struct is obsolete; the tz_dsttime field has
       never been used under Linux - it has not been and will not be supported
       by  libc or glibc.  Each and every occurrence of this field in the ker-
       nel source (other than the declaration) is a bug. Thus,  the  following
       is purely of historic interest.

       The  field  tz_dsttime  contains  a symbolic constant (values are given
       below) that indicates in which part of the year Daylight Saving Time is
       in  force.  (Note:  its value is constant throughout the year - it does
       not indicate that DST is in force, it just selects an algorithm.)   The
       daylight saving time algorithms defined are as follows :

        DST_NONE     /* not on dst */
        DST_USA      /* USA style dst */
        DST_AUST     /* Australian style dst */
        DST_WET      /* Western European dst */
        DST_MET      /* Middle European dst */
        DST_EET      /* Eastern European dst */
        DST_CAN      /* Canada */
        DST_GB       /* Great Britain and Eire */
        DST_RUM      /* Rumania */
        DST_TUR      /* Turkey */
        DST_AUSTALT  /* Australian style with shift in 1986 */

       Of  course  it turned out that the period in which Daylight Saving Time
       is in force cannot be given by a simple  algorithm,  one  per  country;
       indeed, this period is determined by unpredictable political decisions.
       So this method of representing time zones  has  been  abandoned.  Under
       Linux, in a call to settimeofday the tz_dsttime field should be zero.

       Under Linux there is some peculiar `warp clock' semantics associated to
       the settimeofday system call if on the very first call (after  booting)
       that  has  a  non-NULL  tz  argument,  the  tv argument is NULL and the
       tz_minuteswest field is nonzero. In such a case it is assumed that  the
       CMOS  clock is on local time, and that it has to be incremented by this
       amount to get UTC system time.  No doubt it is a bad idea to  use  this
       feature.


RETURN VALUE
       gettimeofday return 0 for success, or -1  for  failure
       (in which case errno is set appropriately).

ERRORS
       EFAULT One of tv or tz pointed outside your accessible address space.

SEE ALSO
       date(1), adjtimex(2), time(2), ctime(3), ftime(3)


----------------------------------------------------------------------------
162 nanosleep      - pause execution for a specified time
----------------------------------------------------------------------------
  mov  eax,162
  mov  ebx,req
  mov  ecx,rem
  int  80h

       nanosleep  delays  the  execution  of the program for at least the time
       specified in *req.  The function can return earlier  if  a  signal  has
       been  delivered to the process. In this case, it returns -1, sets errno
       to EINTR, and writes the remaining time into the structure  pointed  to
       by  rem unless rem is NULL.  The value of *rem can then be used to call
       nanosleep again and complete the specified pause.

       The structure timespec is  used  to  specify  intervals  of  time  with
       nanosecond precision. It is specified in <time.h> and has the form

              struct timespec
              {
                      time_t  tv_sec;         /* seconds */
                      long    tv_nsec;        /* nanoseconds */
              };

       The  value  of  the nanoseconds field must be in the range 0 to 999 999
       999.

ERRORS
       In  case of an error or exception, the nanosleep system call returns -1
       instead of 0 and sets errno to one of the following values:

       EINTR  The pause has been interrupted by a non-blocked signal that  was
              delivered  to  the  process.  The  remaining sleep time has been
              written into *rem so that the process can easily call  nanosleep
              again and continue with the pause.

       EINVAL The  value  in  the  tv_nsec  field  was  not  in the range 0 to
              999 999 999 or tv_sec was negative.

       EFAULT Problem with copying information from user space.

BUGS
       The current implementation of nanosleep is based on the  normal  kernel
       timer  mechanism,  which  has  a  resolution  of  1/HZ s (i.e, 10 ms on
       Linux/i386 and  1 ms  on  Linux/Alpha).   Therefore,  nanosleep  pauses
       always for at least the specified time, however it can take up to 10 ms
       longer than specified until the process becomes runnable again. For the
       same  reason,  the value returned in case of a delivered signal in *rem
       is usually rounded to the next larger multiple of 1/HZ s.

       As some applications require much more precise pauses (e.g.,  in  order
       to  control  some time-critical hardware), nanosleep is also capable of
       short high-precision pauses. If the process is scheduled under a  real-
       time policy like SCHED_FIFO or SCHED_RR, then pauses of up to 2 ms will
       be performed as busy waits with microsecond precision.

SEE ALSO
        sched_setscheduler(2), timer_create(2)


----------------------------------------------------------------------------
168 poll           - wait for some event on a file descriptor
----------------------------------------------------------------------------
  mov  eax,168
  mov  ebx,ufds         ;ptr to array of structures
  mov  ecx,nfds         ;number of structures in array
  mov  edx,timeout      ;timeout
  int  80h

  struc pollfd
  fd  dword; /* file descriptor */
  events  word; /* requested events */
  revents  word; /* returned events */
  endstruc

       poll  is  a variation on the theme of select.  It specifies an array of
       nfds structures of type
               struct pollfd {
                       int fd;           /* file descriptor */
                       short events;     /* requested events */
                       short revents;    /* returned events */
               };
       and a timeout in milliseconds. A negative value means infinite timeout.
       The  field  fd  contains a file descriptor for an open file.  The field
       events is an input parameter,  a  bitmask  specifying  the  events  the
       application  is  interested in.  The field revents is an output parame-
       ter, filled by the kernel  with  the  events  that  actually  occurred,
       either of the type requested, or of one of the types POLLERR or POLLHUP
       or POLLNVAL.  (These three bits are meaningless in  the  events  field,
       and  will be set in the revents field whenever the corresponding condi-
       tion is true.)  If none of the events  requested  (and  no  error)  has
       occurred  for any of the file descriptors, the kernel waits for timeout
       milliseconds for one of these events to occur.  The following  possible
       bits in these masks are defined in <sys/poll.h>
           #define POLLIN      0x0001    /* There is data to read */
           #define POLLPRI     0x0002    /* There is urgent data to read */
           #define POLLOUT     0x0004    /* Writing now will not block */
           #define POLLERR     0x0008    /* Error condition */
           #define POLLHUP     0x0010    /* Hung up */
           #define POLLNVAL    0x0020    /* Invalid request: fd not open */
       When compiling XPG4.2 source one also has
       #ifdef _XOPEN_SOURCE
           #define POLLRDNORM  0x0040    /* Normal data may be read */
           #define POLLRDBAND  0x0080    /* Priority data may be read */
           #define POLLWRNORM  0x0100    /* Writing now will not block */
           #define POLLWRBAND  0x0200    /* Priority data may be written */
       #endif
       Finally, Linux knows about
       #ifdef _GNU_SOURCE
           #define POLLMSG     0x0400
       #endif

RETURN VALUE
       On success, a positive number is returned, where the number returned is
       the number of structures which have non-zero revents fields  (in  other
       words, those descriptors with events or errors reported).  A value of 0
       indicates that the call timed out and no  file  descriptors  have  been
       selected. On error, -1 is returned, and errno is set appropriately.

ERRORS
       EBADF  An invalid file descriptor was given in one of the sets.

       ENOMEM There was no space to allocate file descriptor tables.

       EFAULT The  array  given  as  argument was not contained in the calling
              program's address space.

       EINTR  A signal occurred before any requested event.

       EINVAL The nfds value exceeds the RLIMIT_NOFILE value.

CONFORMING TO
       XPG4-UNIX.

AVAILABILITY
       The poll() systemcall was  introduced  in  Linux  2.1.23.   The  poll()
       library  call  was  introduced  in  libc 5.4.28 (and provides emulation
       using select if your kernel does not have a poll syscall).

SEE ALSO
       select(2), select_tut(2)


----------------------------------------------------------------------------
 98 profil         - UNIMPLEMENTED execution time profile
----------------------------------------------------------------------------

----------------------------------------------------------------------------
 82 select         - synchronous I/O
----------------------------------------------------------------------------
  mov   eax,142
  mov   ebx,n           ;highest fd +1 , used to size bit arrarys that follow
  mov   ecx,readfds     ;ptr to read fd's (bit encoded)
  mov   edx,writefds    ;ptr to write fd's (bit encoded)
  mov   esi,exceptfds   ;ptr to except fd's (bit encoded)
  mov   edi,time        ;ptr to time structure
  int   80h

  time:  dd   ;seconds
         dd   ;useconds

  readfds: -variable lenght bit field set with "bts" instruction.
            example:  bts [readfds],eax   eax= file descriptor
            if the fd=20 then a large bit array is needed and the
            kernel is informed by setting n=21 (ebx).  For stdin
            a bit array of two bytes can be used.
  writefds: (same format as readfds)
  exceptfds: (same format as readfds)

       select is the pivot function that  handles  more than one simultaneous
       file descriptor (or socket handle) in an
       efficient manner. Its principal arguments  are  three  arrays  of  file
       descriptors:  readfds,  writefds, and exceptfds. The way that select is
       usually used is to block while waiting for a "change of status" on  one
       or  more  of  the  file  descriptors. A "change of status" is when more
       characters become available from the file  descriptor,  or  when  space
       becomes  available  within the kernel's internal buffers for more to be
       written to the file descriptor, or when a  file  descriptor  goes  into
       error  (in  the  case of a socket or pipe this is when the other end of
       the connection is closed).

       In summary, select just watches multiple file descriptors, and  is  the
       standard Unix call to do so.

       The  arrays  of file descriptors are called file descriptor sets.
       select modifies the contents of the sets according
       to the rules described below; after calling select you can test if your
       file descriptor is still present in the set.


ARGUMENTS
       readfds
              This set is watched to see if data is available for reading from
              any of its file descriptors. After select has returned,  readfds
              will  be  cleared  of all file descriptors except for those file
              descriptors that are immediately available for  reading  with  a
              recv()  (for  sockets) or read() (for pipes, files, and sockets)
              call.

       writefds
              This set is watched to see if there is space to  write  data  to
              any  of its file descriptor. After select has returned, writefds
              will be cleared of all file descriptors except  for  those  file
              descriptors  that  are  immediately available for writing with a
              send() (for sockets) or write() (for pipes, files, and  sockets)
              call.

       exceptfds
              This  set is watched for exceptions or errors on any of the file
              descriptors. However, that is actually just a rumor. How you use
              exceptfds  is  to  watch for out-of-band (OOB) data. OOB data is
              data sent  on  a  socket  using  the  MSG_OOB  flag,  and  hence
              exceptfds  only  really  applies  to  sockets.  See  recv(2) and
              send(2) about this. After select has returned, exceptfds will be
              cleared  of  all  file  descriptors except for those descriptors
              that are available for reading OOB data. You can only ever  read
              one  byte  of  OOB  data though (which is done with recv()), and
              writing OOB data (done with send) can be done at  any  time  and
              will not block. Hence there is no need for a fourth set to check
              if a socket is available for writing OOB data.

       nfds   This is an integer  one  more  than  the  maximum  of  any  file
              descriptor  in  any  of  the sets. In other words, while you are
              busy adding file descriptors to your sets,  you  must  calculate
              the  maximum  integer  value of all of them, then increment this
              value by one, and then pass this as nfds to select.

       utimeout
              This is the longest time select must wait before returning, even
              if  nothing  interesting  happened.  If  this value is passed as
              NULL, then select blocks  indefinitely  waiting  for  an  event.
              utimeout  can  be  set  to  zero seconds, which causes select to
              return immediately. The structure struct timeval is defined as,

              struct timeval {
                  time_t tv_sec;    /* seconds */
                  long tv_usec;     /* microseconds */
              };

       ntimeout
              This argument has the same meaning as utimeout but struct  time-
              spec has nanosecond precision as follows,

              struct timespec {
                  long tv_sec;    /* seconds */
                  long tv_nsec;   /* nanoseconds */
              };

       sigmask
              This argument holds a set of signals to allow while performing a
              pselect call (see sigaddset(3) and sigprocmask(2)).  It  can  be
              passed  as  NULL,  in  which  case it does not modify the set of
              allowed signals on entry and exit to the function. It will  then
              behave just like select.


COMBINING SIGNAL AND DATA EVENTS
       pselect  must  be  used if you are waiting for a signal as well as data
       from a file descriptor. Programs that receive signals  as  events  nor-
       mally  use  the  signal handler only to raise a global flag. The global
       flag will indicate that the event must be processed in the main loop of
       the program. A signal will cause the select (or pselect) call to return
       with errno set to EINTR. This behavior is essential so that signals can
       be  processed  in  the main loop of the program, otherwise select would
       block indefinitely. Now, somewhere in the main loop will  be  a  condi-
       tional  to  check  the  global  flag.  So we must ask: what if a signal
       arrives after the conditional, but before the select call?  The  answer
       is  that select would block indefinitely, even though an event is actu-
       ally pending. This race condition is solved by the pselect  call.  This
       call can be used to mask out signals that are not to be received except
       within the pselect call. For instance, let us say  that  the  event  in
       question  was the exit of a child process. Before the start of the main
       loop, we would block SIGCHLD using sigprocmask. Our pselect call  would
       enable  SIGCHLD by using the virgin signal mask. Our program would look
       like:

       int child_events = 0;

       void child_sig_handler (int x) {
           child_events++;
           signal (SIGCHLD, child_sig_handler);
       }

       int main (int argc, char **argv) {
           sigset_t sigmask, orig_sigmask;

           sigemptyset (&sigmask);
           sigaddset (&sigmask, SIGCHLD);
           sigprocmask (SIG_BLOCK, &sigmask,
                                       &orig_sigmask);

           signal (SIGCHLD, child_sig_handler);

           for (;;) { /* main loop */
               for (; child_events > 0; child_events--) {
                   /* do event work here */
               }
               r = pselect (nfds, &rd, &wr, &er, 0, &orig_sigmask);

               /* main body of program */
           }
       }

       Note that the above pselect call can be replaced with:

               sigprocmask (SIG_BLOCK, &orig_sigmask, 0);
               r = select (nfds, &rd, &wr, &er, 0);
               sigprocmask (SIG_BLOCK, &sigmask, 0);

       but then there is still the possibility  that  a  signal  could  arrive
       after  the  first sigprocmask and before the select. If you do do this,
       it is prudent to at least put a finite timeout so that the process does
       not  block. At present glibc probably works this way.  The Linux kernel
       does not have a native pselect system call as yet so this is all proba-
       bly much of a mute point.


PRACTICAL
       So  what  is  the  point  of  select? Can't I just read and write to my
       descriptors whenever I want? The point of select  is  that  it  watches
       multiple  descriptors at the same time and properly puts the process to
       sleep if there is no activity. It does this while enabling you to  han-
       dle  multiple  simultaneous  pipes  and sockets. Unix programmers often
       find themselves in a position where they have to handle  IO  from  more
       than  one  file  descriptor where the data flow may be intermittent. If
       you were to merely create a sequence of read and write calls, you would
       find  that  one of your calls may block waiting for data from/to a file
       descriptor, while another file descriptor is  unused  though  available
       for data. select efficiently copes with this situation.

       A classic example of select comes from the select man page:

       #include <stdio.h>
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int
       main(void) {
           fd_set rfds;
           struct timeval tv;
           int retval;

           /* Watch stdin (fd 0) to see when it has input. */
           FD_ZERO(&rfds);
           FD_SET(0, &rfds);
           /* Wait up to five seconds. */
           tv.tv_sec = 5;
           tv.tv_usec = 0;

           retval = select(1, &rfds, NULL, NULL, &tv);
           /* Don't rely on the value of tv now! */

           if (retval == -1)
               perror("select()");
           else if (retval)
               printf("Data is available now.\n");
               /* FD_ISSET(0, &rfds) will be true. */
           else
               printf("No data within five seconds.\n");

           exit(0);
       }



PORT FORWARDING EXAMPLE
       Here is an example that better demonstrates the true utility of select.
       The listing below a TCP forwarding program that forwards from  one  TCP
       port to another.

       #include <stdlib.h>
       #include <stdio.h>
       #include <unistd.h>
       #include <sys/time.h>
       #include <sys/types.h>
       #include <string.h>
       #include <signal.h>
       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
       #include <errno.h>

       static int forward_port;

       #undef max
       #define max(x,y) ((x) > (y) ? (x) : (y))

       static int listen_socket (int listen_port) {
           struct sockaddr_in a;
           int s;
           int yes;
           if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
               perror ("socket");
               return -1;
           }
           yes = 1;
           if (setsockopt
               (s, SOL_SOCKET, SO_REUSEADDR,
                (char *) &yes, sizeof (yes)) < 0) {
               perror ("setsockopt");
               close (s);
               return -1;
           }
           memset (&a, 0, sizeof (a));
           a.sin_port = htons (listen_port);
           a.sin_family = AF_INET;
           if (bind
               (s, (struct sockaddr *) &a, sizeof (a)) < 0) {
               perror ("bind");
               close (s);
               return -1;
           }
           printf ("accepting connections on port %d\n",
                   (int) listen_port);
           listen (s, 10);
           return s;
       }

       static int connect_socket (int connect_port,
                                  char *address) {
           struct sockaddr_in a;
           int s;
           if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
               perror ("socket");
               close (s);
               return -1;
           }

           memset (&a, 0, sizeof (a));
           a.sin_port = htons (connect_port);
           a.sin_family = AF_INET;

           if (!inet_aton
               (address,
                (struct in_addr *) &a.sin_addr.s_addr)) {
               perror ("bad IP address format");
               close (s);
               return -1;
           }

           if (connect
               (s, (struct sockaddr *) &a,
                sizeof (a)) < 0) {
               perror ("connect()");
               shutdown (s, SHUT_RDWR);
               close (s);
               return -1;
           }
           return s;
       }

       #define SHUT_FD1 {                      \
               if (fd1 >= 0) {                 \
                   shutdown (fd1, SHUT_RDWR);  \
                   close (fd1);                \
                   fd1 = -1;                   \
               }                               \
           }

       #define SHUT_FD2 {                      \
               if (fd2 >= 0) {                 \
                   shutdown (fd2, SHUT_RDWR);  \
                   close (fd2);                \
                   fd2 = -1;                   \
               }                               \
           }

       #define BUF_SIZE 1024

       int main (int argc, char **argv) {
           int h;
           int fd1 = -1, fd2 = -1;
           char buf1[BUF_SIZE], buf2[BUF_SIZE];
           int buf1_avail, buf1_written;
           int buf2_avail, buf2_written;

           if (argc != 4) {
               fprintf (stderr,
                        "Usage\n\tfwd <listen-port> \
       <forward-to-port> <forward-to-ip-address>\n");
               exit (1);
           }

           signal (SIGPIPE, SIG_IGN);

           forward_port = atoi (argv[2]);

           h = listen_socket (atoi (argv[1]));
           if (h < 0)
               exit (1);

           for (;;) {
               int r, nfds = 0;
               fd_set rd, wr, er;
               FD_ZERO (&rd);
               FD_ZERO (&wr);
               FD_ZERO (&er);
               FD_SET (h, &rd);
               nfds = max (nfds, h);
               if (fd1 > 0 && buf1_avail < BUF_SIZE) {
                   FD_SET (fd1, &rd);
                   nfds = max (nfds, fd1);
               }
               if (fd2 > 0 && buf2_avail < BUF_SIZE) {
                   FD_SET (fd2, &rd);
                   nfds = max (nfds, fd2);
               }
               if (fd1 > 0
                   && buf2_avail - buf2_written > 0) {
                   FD_SET (fd1, &wr);
                   nfds = max (nfds, fd1);
               }
               if (fd2 > 0
                   && buf1_avail - buf1_written > 0) {
                   FD_SET (fd2, &wr);
                   nfds = max (nfds, fd2);
               }
               if (fd1 > 0) {
                   FD_SET (fd1, &er);
                   nfds = max (nfds, fd1);
               }
               if (fd2 > 0) {
                   FD_SET (fd2, &er);
                   nfds = max (nfds, fd2);
               }

               r = select (nfds + 1, &rd, &wr, &er, NULL);

               if (r == -1 && errno == EINTR)
                   continue;
               if (r < 0) {
                   perror ("select()");
                   exit (1);
               }
               if (FD_ISSET (h, &rd)) {
                   unsigned int l;
                   struct sockaddr_in client_address;
                   memset (&client_address, 0, l =
                           sizeof (client_address));
                   r = accept (h, (struct sockaddr *)
                               &client_address, &l);
                   if (r < 0) {
                       perror ("accept()");
                   } else {
                       SHUT_FD1;
                       SHUT_FD2;
                       buf1_avail = buf1_written = 0;
                       buf2_avail = buf2_written = 0;
                       fd1 = r;
                       fd2 =
                           connect_socket (forward_port,
                                           argv[3]);
                       if (fd2 < 0) {
                           SHUT_FD1;
                       } else
                           printf ("connect from %s\n",
                                   inet_ntoa
                                   (client_address.sin_addr));
                   }
               }
       /* NB: read oob data before normal reads */
               if (fd1 > 0)
                   if (FD_ISSET (fd1, &er)) {
                       char c;
                       errno = 0;
                       r = recv (fd1, &c, 1, MSG_OOB);
                       if (r < 1) {
                           SHUT_FD1;
                       } else
                           send (fd2, &c, 1, MSG_OOB);
                   }
               if (fd2 > 0)
                   if (FD_ISSET (fd2, &er)) {
                       char c;
                       errno = 0;
                       r = recv (fd2, &c, 1, MSG_OOB);
                       if (r < 1) {
                           SHUT_FD1;
                       } else
                           send (fd1, &c, 1, MSG_OOB);
                   }
               if (fd1 > 0)
                   if (FD_ISSET (fd1, &rd)) {
                       r =
                           read (fd1, buf1 + buf1_avail,
                                 BUF_SIZE - buf1_avail);
                       if (r < 1) {
                           SHUT_FD1;
                       } else
                           buf1_avail += r;
                   }
               if (fd2 > 0)
                   if (FD_ISSET (fd2, &rd)) {
                       r =
                           read (fd2, buf2 + buf2_avail,
                                 BUF_SIZE - buf2_avail);
                       if (r < 1) {
                           SHUT_FD2;
                       } else
                           buf2_avail += r;
                   }
               if (fd1 > 0)
                   if (FD_ISSET (fd1, &wr)) {
                       r =
                           write (fd1,
                                  buf2 + buf2_written,
                                  buf2_avail -
                                  buf2_written);
                       if (r < 1) {
                           SHUT_FD1;
                       } else
                           buf2_written += r;
                   }
               if (fd2 > 0)
                   if (FD_ISSET (fd2, &wr)) {
                       r =
                           write (fd2,
                                  buf1 + buf1_written,
                                  buf1_avail -
                                  buf1_written);
                       if (r < 1) {
                           SHUT_FD2;
                       } else
                           buf1_written += r;
                   }
       /* check if write data has caught read data */
               if (buf1_written == buf1_avail)
                   buf1_written = buf1_avail = 0;
               if (buf2_written == buf2_avail)
                   buf2_written = buf2_avail = 0;
       /* one side has closed the connection, keep
          writing to the other side until empty */
               if (fd1 < 0
                   && buf1_avail - buf1_written == 0) {
                   SHUT_FD2;
               }
               if (fd2 < 0
                   && buf2_avail - buf2_written == 0) {
                   SHUT_FD1;
               }
           }
           return 0;
       }

       The  above  program  properly  forwards  most  kinds of TCP connections
       including OOB signal data transmitted by telnet servers. It handles the
       tricky  problem  of having data flow in both directions simultaneously.
       You might think it more efficient to use a fork()  call  and  devote  a
       thread to each stream. This becomes more tricky than you might suspect.
       Another idea is to set non-blocking IO using an ioctl() call. This also
       has  its  problems  because you end up having to have inefficient time-
       outs.

       The program does not handle more than one simultaneous connection at  a
       time,  although  it  could  easily be extended to do this with a linked
       list of buffers - one for each connection. At the moment,  new  connec-
       tions cause the current connection to be dropped.


SELECT LAW
       Many  people  who try to use select come across behavior that is diffi-
       cult to understand and produces non-portable or borderline results. For
       instance,  the  above  program is carefully written not to block at any
       point, even though it does not set its file descriptors to non-blocking
       mode  at all (see ioctl(2)). It is easy to introduce subtle errors that
       will remove the advantage of using select, hence I will present a  list
       of essentials to watch for when using the select call.


       1.     You should always try use select without a timeout. Your program
              should have nothing to do if there is no  data  available.  Code
              that  depends  on timeouts is not usually portable and difficult
              to debug.

       2.     The value nfds must be properly  calculated  for  efficiency  as
              explained above.

       3.     No file descriptor must be added to any set if you do not intend
              to check its result after the select call, and respond appropri-
              ately. See next rule.

       4.     After  select  returns, all file descriptors in all sets must be
              checked. Any file descriptor that is available for writing  must
              be  written  to,  and  any file descriptor available for reading
              must be read, etc.

       5.     The functions read(), recv(), write(), and send() do not  neces-
              sarily  read/write  the  full  amount  of  data  that  you  have
              requested. If they do read/write the full  amount,  its  because
              you  have  a  low  traffic  load  and a fast stream. This is not
              always going to be the case. You should cope with  the  case  of
              your functions only managing to send or receive a single byte.

       6.     Never  read/write only in single bytes at a time unless your are
              really sure that you have a small amount of data to process.  It
              is  extremely  inefficient not to read/write as much data as you
              can buffer each time.  The buffers in the example above are 1024
              bytes although they could easily be made as large as the maximum
              possible packet size on your local network.

       7.     The functions read(), recv(), write(), and send() as well as the
              select()  call  can  return  -1 with an errno of EINTR or EAGAIN
              (EWOULDBLOCK) which are not errors. These results must be  prop-
              erly  managed  (not done properly above). If your program is not
              going to receive any signals then it is unlikely  you  will  get
              EINTR.  If  your  program does not set non-blocking IO, you will
              not get EAGAIN. Nonetheless you should  still  cope  with  these
              errors for completeness.

       8.     Never  call  read(),  recv(),  write(),  or send() with a buffer
              length of zero.

       9.     Except  as  indicated  in  7.,  the  functions  read(),  recv(),
              write(), and send() never have a return value less than 1 except
              if an error has occurred. For instance, a read() on a pipe where
              the  other  end  has  died  returns zero (so does an end-of-file
              error), but only returns zero once (a  followup  read  or  write
              will  return  -1). Should any of these functions return 0 or -1,
              you should not pass that descriptor to select ever again. In the
              above  example, I close the descriptor immediately, and then set
              it to -1 to prevent it being included in a set.

       10.    The timeout value must be initialized  with  each  new  call  to
              select,  since some operating systems modify the structure. pse-
              lect however does not modify its timeout structure.

       11.    I have heard that the Windows socket layer does  not  cope  with
              OOB  data properly. It also does not cope with select calls when
              no file descriptors are set at all. Having no  file  descriptors
              set  is a useful way to sleep the process with sub-second preci-
              sion by using the timeout.  (See further on.)


USLEEP EMULATION
       On systems that do not have a usleep function, you can call select with
       a finite timeout and no file descriptors as follows:

           struct timeval tv;
           tv.tv_sec = 0;
           tv.tv_usec = 200000;  /* 0.2 seconds */
           select (0, NULL, NULL, NULL, &tv);

       This is only guarenteed to work on Unix systems, however.


RETURN VALUE
       On  success,  select returns the total number of file descriptors still
       present in the file descriptor sets.

       If select timed out, then the file descriptors sets should be all empty
       (but  may  not be on some systems). However the return value will defi-
       nitely be zero.

       A return value of -1 indicates an error, with errno being set appropri-
       ately.  In  the  case  of  an  error, the returned sets and the timeout
       struct contents are undefined and should not be used.  pselect  however
       never modifies ntimeout.


ERRORS
       EBADF  A  set  contained  an  invalid file descriptor. This error often
              occurs when you add a file descriptor to a  set  that  you  have
              already  issued  a  close  on,  or when that file descriptor has
              experienced some kind of error. Hence you should cease adding to
              sets  any  file  descriptor  that returns an error on reading or
              writing.

       EINTR  An interrupting signal was caught like SIGINT  or  SIGCHLD  etc.
              In  this  case  you should rebuild your file descriptor sets and
              retry.

       EINVAL Occurs if nfds is negative or an invalid value is  specified  in
              utimeout or ntimeout.

       ENOMEM Internal memory allocation failure.


NOTES
       Generally  speaking,  all  operating systems that support sockets, also
       support select. Some people consider  select  to  be  an  esoteric  and
       rarely  used  function. Indeed, many types of programs become extremely
       complicated without it. select can be used to solve many problems in  a
       portable  and  efficient  way  that naive programmers try to solve with
       threads, forking, IPCs, signals, memory sharing and other  dirty  meth-
       ods. pselect is a newer function that is less commonly used.

       The  poll(2) system call has the same functionality as select, but with
       less subtle behavior. It is less portable than select.


CONFORMING TO
       4.4BSD (the select function first appeared in 4.2BSD).  Generally  por-
       table to/from non-BSD systems supporting clones of the BSD socket layer
       (including System V variants). However, note that the System V  variant
       typically  sets  the  timeout variable before exit, but the BSD variant
       does not.

       The pselect function is defined in IEEE  Std  1003.1g-2000  (POSIX.1g).
       It  is  found  in glibc2.1 and later. Glibc2.0 has a function with this
       name, that however does not take a sigmask parameter.


SEE ALSO
       accept(2), connect(2), ioctl(2), poll(2), read(2), recv(2),  select(2),
       send(2),  sigaddset(3),  sigdelset(3),  sigemptyset(3),  sigfillset(3),
       sigismember(3), sigprocmask(2), write(2)


----------------------------------------------------------------------------
104 setitimer      - set an interval timer
----------------------------------------------------------------------------
  which:   dd   ?
  ivalue:  dd   ?
  ovalue:  dd   ?
  ;
  mov eax,104
  mov ebx,which
  mov ecx,ivalue
  mov edx,ovalue
  int 80h

       The system provides each  process  with  three  interval  timers,  each
       decrementing in a distinct time domain.  When any timer expires, a sig-
       nal is sent to the process, and the timer (potentially) restarts.

       ITIMER_REAL    decrements in real time, and delivers SIGALRM upon expi-
                      ration.

       ITIMER_VIRTUAL decrements  only  when  the  process  is  executing, and
                      delivers SIGVTALRM upon expiration.

       ITIMER_PROF    decrements both when the process executes and  when  the
                      system  is  executing on behalf of the process.  Coupled
                      with ITIMER_VIRTUAL, this timer is usually used to  pro-
                      file  the time spent by the application in user and ker-
                      nel space.  SIGPROF is delivered upon expiration.

       Timer values are defined by the following structures:
            struct itimerval {
                struct timeval it_interval; /* next value */
                struct timeval it_value;    /* current value */
            };
            struct timeval {
                long tv_sec;                /* seconds */
                long tv_usec;               /* microseconds */
            };

       The function
       setitimer sets the indicated timer to the value in value.  If ovalue is
       nonzero, the old value of the timer is stored there.

       Timers decrement from it_value to zero, generate a signal, and reset to
       it_interval.   A  timer  which  is set to zero (it_value is zero or the
       timer expires and it_interval is zero) stops.

       Both tv_sec and tv_usec are significant in determining the duration  of
       a timer.

       Timers  will  never  expire before the requested time, instead expiring
       some short, constant time afterwards, dependent  on  the  system  timer
       resolution  (currently 10ms).  Upon expiration, a signal will be gener-
       ated and the timer reset.  If the timer expires while  the  process  is
       active (always true for ITIMER_VIRT) the signal will be delivered imme-
       diately when generated.  Otherwise the delivery will  be  offset  by  a
       small time dependent on the system loading.


RETURN VALUE
       On  success,  zero is returned.  On error, -1 is returned, and errno is
       set appropriately.

ERRORS
       EFAULT value or ovalue are not valid pointers.

       EINVAL which is not one of ITIMER_REAL, ITIMER_VIRT, or ITIMER_PROF.

SEE ALSO
       gettimeofday(2), sigaction(2), signal(2)

BUGS
       Under Linux, the generation and delivery of a signal are distinct,  and
       there each signal is permitted only one outstanding event.  It's there-
       fore conceivable that under pathologically heavy  loading,  ITIMER_REAL
       will  expire  before  the  signal  from  a previous expiration has been
       delivered.  The second signal in such an event will be lost.



----------------------------------------------------------------------------
 79 settimeofday   - set time
----------------------------------------------------------------------------
  mov  eax,079
  mov  ebx,tv
  mov  ecx,tz
  int  80h

       The function settimeofday can set the time as
       well  as a timezone.  The tv argument is a timeval struct, as specified
       in <sys/time.h>:

       struct timeval {
               time_t         tv_sec;        /* seconds */
               suseconds_t    tv_usec;  /* microseconds */
       };

       and gives the number of seconds and microseconds since the  Epoch  (see
       time(2)).  The tz argument is a timezone :

       struct timezone {
               int  tz_minuteswest; /* minutes W of Greenwich */
               int  tz_dsttime;     /* type of dst correction */
       };

       The  use  of  the timezone struct is obsolete; the tz_dsttime field has
       never been used under Linux - it has not been and will not be supported
       by  libc or glibc.  Each and every occurrence of this field in the ker-
       nel source (other than the declaration) is a bug. Thus,  the  following
       is purely of historic interest.

       The  field  tz_dsttime  contains  a symbolic constant (values are given
       below) that indicates in which part of the year Daylight Saving Time is
       in  force.  (Note:  its value is constant throughout the year - it does
       not indicate that DST is in force, it just selects an algorithm.)   The
       daylight saving time algorithms defined are as follows :

        DST_NONE     /* not on dst */
        DST_USA      /* USA style dst */
        DST_AUST     /* Australian style dst */
        DST_WET      /* Western European dst */
        DST_MET      /* Middle European dst */
        DST_EET      /* Eastern European dst */
        DST_CAN      /* Canada */
        DST_GB       /* Great Britain and Eire */
        DST_RUM      /* Rumania */
        DST_TUR      /* Turkey */
        DST_AUSTALT  /* Australian style with shift in 1986 */

       Of  course  it turned out that the period in which Daylight Saving Time
       is in force cannot be given by a simple  algorithm,  one  per  country;
       indeed, this period is determined by unpredictable political decisions.
       So this method of representing time zones  has  been  abandoned.  Under
       Linux, in a call to settimeofday the tz_dsttime field should be zero.

       Under Linux there is some peculiar `warp clock' semantics associated to
       the settimeofday system call if on the very first call (after  booting)
       that  has  a  non-NULL  tz  argument,  the  tv argument is NULL and the
       tz_minuteswest field is nonzero. In such a case it is assumed that  the
       CMOS  clock is on local time, and that it has to be incremented by this
       amount to get UTC system time.  No doubt it is a bad idea to  use  this
       feature.

       The following macros are defined to operate on a struct timeval :
       #define       timerisset(tvp)\
               ((tvp)->tv_sec || (tvp)->tv_usec)
       #define       timercmp(tvp, uvp, cmp)\
               ((tvp)->tv_sec cmp (uvp)->tv_sec ||\
               (tvp)->tv_sec == (uvp)->tv_sec &&\
               (tvp)->tv_usec cmp (uvp)->tv_usec)
       #define       timerclear(tvp)\
               ((tvp)->tv_sec = (tvp)->tv_usec = 0)

       If  either  tv or tz is null, the corresponding structure is not set or
       returned.

       Only the super user may use settimeofday.

RETURN VALUE
       settimeofday return 0 for success, or -1  for  failure
       (in which case errno is set appropriately).

ERRORS
       EPERM  settimeofday is called by someone other than the superuser.

       EINVAL Timezone (or something else) is invalid.

       EFAULT One of tv or tz pointed outside your accessible address space.

SEE ALSO
       date(1), adjtimex(2), time(2), ctime(3), ftime(3)


----------------------------------------------------------------------------
 25 stime          - set time
----------------------------------------------------------------------------
  mov  eax,025
  mov  ebx,tptr
  int  80h

       stime sets the system's idea of the time and date.  Time, pointed to by
       t, is measured in seconds from 00:00:00 GMT January 1,  1970.   stime()
       may only be executed by the super user.

RETURN VALUE
       On  success,  zero is returned.  On error, -1 is returned, and errno is
       set appropriately.

ERRORS
       EPERM  The caller is not the super-user.

       EFAULT Error in getting information from user space.

SEE ALSO
       date(1), settimeofday(2)


----------------------------------------------------------------------------
 13 time           - get time in seconds
----------------------------------------------------------------------------
  mov  eax,013
  mov  ebx,tloc
  int  80h

       time  returns the time since the Epoch (00:00:00 UTC, January 1, 1970),
       measured in seconds.

       If t is non-NULL, the return value is also stored in the memory pointed
       to by t.

RETURN VALUE
       On  success,  the value of time in seconds since the Epoch is returned.
       On error, ((time_t)-1) is returned, and errno is set appropriately.

ERRORS
       EFAULT t points outside your accessible address space.

NOTES
       POSIX.1 defines seconds since the Epoch as a value to be interpreted as
       the number of seconds between a specified time and the Epoch, according
       to a formula for conversion from UTC equivalent to  conversion  on  the
       nave  basis that leap seconds are ignored and all years divisible by 4
       are leap years.  This value is not the same as  the  actual  number  of
       seconds  between  the  time  and the Epoch, because of leap seconds and
       because clocks are not required to be synchronised to a standard refer-
       ence.   The  intention  is that the interpretation of seconds since the
       Epoch values be consistent; see  POSIX.1  Annex  B  2.2.2  for  further
       rationale.

SEE ALSO
       date(1),  gettimeofday(2)


----------------------------------------------------------------------------
 43 times          - get process times
----------------------------------------------------------------------------
  mov  eax,043
  mov  ebx,buf
  int  80h

       The times() function stores the current process times in the struct tms
       that buf points to.  The struct tms is as defined in <sys/times.h>:

       struct tms {
              clock_t tms_utime;  /* user time */
              clock_t tms_stime;  /* system time */
              clock_t tms_cutime; /* user time of children */
              clock_t tms_cstime; /* system time of children */
       };

       The tms_utime field contains the CPU time spent executing  instructions
       of  the  calling  process.   The  tms_stime field contains the CPU time
       spent in the system while executing tasks  on  behalf  of  the  calling
       process.   The  tms_cutime  field contains the sum of the tms_utime and
       tms_cutime  values  for  all  waited-for  terminated   children.    The
       tms_cstime  field contains the sum of the tms_stime and tms_cstime val-
       ues for all waited-for terminated children.

       Times for terminated children (and their descendants) is  added  in  at
       the  moment wait(2) or waitpid(2) returns their process ID. In particu-
       lar, times of grandchildren that the children  did  not  wait  for  are
       never seen.

       All times reported are in clock ticks.

RETURN VALUE
       The  function times returns the number of clock ticks that have elapsed
       since an arbitrary point in the past.  For  Linux  this  point  is  the
       moment  the system was booted.  This return value may overflow the pos-
       sible range of type clock_t.  On error, (clock_t) -1 is  returned,  and
       errno is set appropriately.

NOTES
       The number of clock ticks per second can be obtained using
              sysconf(_SC_CLK_TCK);
       In  POSIX-1996 the symbol CLK_TCK (defined in <time.h>) is mentioned as
       obsolescent. It is obsolete now.

       On Linux, if the disposition of SIGCHLD is  set  to  SIG_IGN  then  the
       times   of  terminated  children  are  automatically  included  in  the
       tms_cstime and tms_cutime fields, although POSIX 1003.1-2001 says  that
       this should only happen if the calling process wait()s on its children.

       Note that clock(3) returns values of type clock_t that are not measured
       in clock ticks but in CLOCKS_PER_SEC.

SEE ALSO
       time(1), getrusage(2), wait(2), clock(3), sysconf(3)


----------------------------------------------------------------------------
114 wait4          - wait for process termination, BSD style
----------------------------------------------------------------------------
  mov  eax,114
  mov  ebx,pid
  mov  ecx,status
  mov  edx,rusage_struc         ;pointer to struc


       The wait4 function suspends execution of the current  process  until  a
       child as specified by the pid argument has exited, or until a signal is
       delivered whose action is to terminate the current process or to call a
       signal  handling  function.  If a child as requested by pid has already
       exited by the time of the call  (a  so-called  "zombie"  process),  the
       function  returns  immediately.  Any system resources used by the child
       are freed.

       The value of pid can be one of:

       < -1   which means to wait for any child process whose process group ID
              is equal to the absolute value of pid.

       -1     which means to wait for any child process; this is equivalent to
              calling wait3.

       0      which means to wait for any child process whose process group ID
              is equal to that of the calling process.

       > 0    which  means  to wait for the child whose process ID is equal to
              the value of pid.

       The value of options is a bitwise OR of zero or more of  the  following
       constants:

       WNOHANG
              which  means  to  return  immediately if no child is there to be
              waited for.

       WUNTRACED
              which means to also return for children which are  stopped,  and
              whose status has not been reported.

       If  status  is not NULL, wait3 or wait4 store status information in the
       location pointed to by status.

       This status can be evaluated with the following  macros  (these  macros
       take  the  stat  buffer (an int) as an argument -- not a pointer to the
       buffer!):

       WIFEXITED(status)
              is non-zero if the child exited normally.

       WEXITSTATUS(status)
              evaluates to the least significant eight bits of the return code
              of  the  child  which terminated, which may have been set as the
              argument to a call to exit() or as the  argument  for  a  return
              statement in the main program.  This macro can only be evaluated
              if WIFEXITED returned non-zero.

       WIFSIGNALED(status)
              returns true if the child process exited  because  of  a  signal
              which was not caught.

       WTERMSIG(status)
              returns  the  number of the signal that caused the child process
              to terminate. This macro can only be  evaluated  if  WIFSIGNALED
              returned non-zero.

       WIFSTOPPED(status)
              returns  true  if  the  child process which caused the return is
              currently stopped; this is only possible if the  call  was  done
              using WUNTRACED.

       WSTOPSIG(status)
              returns the number of the signal which caused the child to stop.
              This  macro  can  only  be  evaluated  if  WIFSTOPPED   returned
              non-zero.

       If rusage is not NULL, the struct rusage as defined in <sys/resource.h>
       it  points  to  will  be  filled  with  accounting  information.    See
       getrusage(2) for details.

RETURN VALUE
       The  process  ID of the child which exited, -1 on error (in particular,
       when no unwaited-for child processes of the specified  kind  exist)  or
       zero if WNOHANG was used and no child was available yet.  In the latter
       two cases errno will be set appropriately.

ERRORS
       ECHILD No unwaited-for child process as specified does exist.

       EINTR  if WNOHANG was not set and an unblocked signal or a SIGCHLD  was
              caught.

       EINVAL Invalid value for options given for wait4.

SEE ALSO
       signal(2), getrusage(2), wait(2), signal(7)



----------------------------------------------------------------------------
  7 waitpid        - wait for process termination  
----------------------------------------------------------------------------
  mov  eax,7
  mov  ebx,pid
  mov  ecx,status       ;null or pointer or buffer to hold status
  mov  edx,options      ;null or WNOHANG, WUNTRACED


       The waitpid function suspends execution of the current process until  a
       child as specified by the pid argument has exited, or until a signal is
       delivered whose action is to terminate the current process or to call a
       signal  handling  function.  If a child as requested by pid has already
       exited by the time of the call  (a  so-called  "zombie"  process),  the
       function  returns  immediately.  Any system resources used by the child
       are freed.

       The value of pid can be one of:

       < -1   which means to wait for any child process whose process group ID
              is equal to the absolute value of pid.

       -1     which  means to wait for any child process; this is the same be-
              haviour which wait exhibits.

       0      which means to wait for any child process whose process group ID
              is equal to that of the calling process.

       > 0    which  means  to wait for the child whose process ID is equal to
              the value of pid.

       The value of options is an OR of zero or more  of  the  following  con-
       stants:

       WNOHANG
              which means to return immediately if no child has exited.

       WUNTRACED
              which  means  to also return for children which are stopped (but
              not traced), and whose status has not been reported.  Status for
              traced  children which are stopped is provided also without this
              option.

       (For Linux-only options, see below.)

       If status is not NULL, wait or waitpid store status information in  the
       location pointed to by status.

       This  status  can  be evaluated with the following macros (these macros
       take the stat buffer (an int) as an argument -- not a  pointer  to  the
       buffer!):

       WIFEXITED(status)
              returns true if the child terminated normally, that is, by call-
              ing exit() or _exit(), or by returning from main().

       WEXITSTATUS(status)
              evaluates to the least significant eight bits of the return code
              of  the  child  which terminated, which may have been set as the
              argument to a call to exit() or _exit() or as the argument for a
              return  statement  in  the main program.  This macro can only be
              evaluated if WIFEXITED returned true.

       WIFSIGNALED(status)
              returns true if the child process terminated because of a signal
              which was not caught.

       WTERMSIG(status)
              returns  the  number of the signal that caused the child process
              to terminate. This macro can only be  evaluated  if  WIFSIGNALED
              returned non-zero.

       WIFSTOPPED(status)
              returns  true  if  the  child process which caused the return is
              currently stopped; this is only possible if the  call  was  done
              using   WUNTRACED  or  when  the  child  is  being  traced  (see
              ptrace(2)).

       WSTOPSIG(status)
              returns the number of the signal which caused the child to stop.
              This   macro  can  only  be  evaluated  if  WIFSTOPPED  returned
              non-zero.

       Some versions of Unix (e.g. Linux, Solaris, but not  AIX,  SunOS)  also
       define  a  macro  WCOREDUMP(status)  to  test whether the child process
       dumped core. Only use this enclosed in #ifdef WCOREDUMP ... #endif.

RETURN VALUE
       The process ID of the child which exited, or zero if WNOHANG  was  used
       and  no child was available, or -1 on error (in which case errno is set
       to an appropriate value).

ERRORS
       ECHILD if the process specified in pid does not exist or is not a child
              of the calling process.  (This can happen for one's own child if
              the action for SIGCHLD is set to SIG_IGN.  See  also  the  LINUX
              NOTES section about threads.)

       EINVAL if the options argument was invalid.

       EINTR  if  WNOHANG was not set and an unblocked signal or a SIGCHLD was
              caught.

NOTES
       The Single Unix Specification describes a flag SA_NOCLDWAIT  (not  sup-
       ported under Linux) such that if either this flag is set, or the action
       for SIGCHLD is set to SIG_IGN then children that  exit  do  not  become
       zombies and a call to wait() or waitpid() will block until all children
       have exited, and then fail with errno set to ECHILD.

       The original POSIX standard left the behaviour of  setting  SIGCHLD  to
       SIG_IGN  unspecified.   Later  standards,  including  SUSv2  and  POSIX
       1003.1-2001 specify the behaviour just described as  an  XSI-compliance
       option.   Linux  does  not conform to the second of the two points just
       described: if a wait() or waitpid() call is made while SIGCHLD is being
       ignored, the call behaves just as though SIGCHLD were not being igored,
       that is, the call blocks until  the  next  child  terminates  and  then
       returns the PID and status of that child.

LINUX NOTES
       In  the  Linux kernel, a kernel-scheduled thread is not a distinct con-
       struct from a process. Instead, a thread is simply a  process  that  is
       created  using  the  Linux-unique  clone(2) system call; other routines
       such as the  portable  pthread_create(3)  call  are  implemented  using
       clone(2).   Before  Linux  2.4,  a  thread was just a special case of a
       process, and as a consequence one thread could not wait on the children
       of  another  thread,  even  when  the latter belongs to the same thread
       group.  However, POSIX prescribes such functionality, and  since  Linux
       2.4  a  thread  can,  and  by  default  will, wait on children of other
       threads in the same thread group.

       The following Linux-specific options are for use with children  created
       using clone(2).

       __WCLONE
              Wait  for "clone" children only.  If omitted then wait for "non-
              clone" children only.  (A "clone" child is one which delivers no
              signal, or a signal other than SIGCHLD to its parent upon termi-
              nation.)  This option is ignored if __WALL is also specified.

       __WALL (Since Linux 2.4) Wait for  all  children,  regardless  of  type
              ("clone" or "non-clone").

       __WNOTHREAD
              (Since  Linux  2.4) Do not wait for children of other threads in
              the same thread group. This was the default before Linux 2.4.

SEE ALSO
       clone(2), ptrace(2), signal(2), wait4(2), pthread_create(3), signal(7)

