home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / misc / volume02 / trapfile < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  48.5 KB

  1. From mipos3!intelca!oliveb!ames!necntc!ncoast!allbery Thu Apr 14 18:00:09 PDT 1988
  2. Article 346 of comp.sources.misc:
  3. Path: td2cad!mipos3!intelca!oliveb!ames!necntc!ncoast!allbery
  4. From: aj@zyx.UUCP (Arndt Jonasson)
  5. Newsgroups: comp.sources.misc
  6. Subject: v02i094: trapfile - a file-opening logger
  7. Message-ID: <2458@zyx.UUCP>
  8. Date: 10 Apr 88 00:10:55 GMT
  9. Sender: allbery@ncoast.UUCP
  10. Reply-To: aj@zyx.UUCP (Arndt Jonasson)
  11. Organization: ZYX Sweden AB, Stockholm, Sweden
  12. Lines: 2130
  13. Approved: allbery@ncoast.UUCP
  14.  
  15. comp.sources.misc: Volume 2, Issue 94
  16. Submitted-By: "Arndt Jonasson" <aj@zyx.UUCP>
  17. Archive-Name: trapfile
  18.  
  19. [The "options" package was posted to comp.unix.wizards a week or so ago,
  20. there may be more documentation for it there.
  21.  
  22. WARNING:  This program assumes BSD system calls and is otherwise machine and
  23. OS dependent -- suffice it to say that it uses ptrace (if you don't know what
  24. that means, you probably don't want to touch this program) to trap system
  25. calls.  ++bsa]
  26.  
  27. # This is a shell archive.  Remove anything before this line,
  28. # then unpack it by saving it in a file and typing "sh file".
  29. #
  30. # Wrapped by arndt at lynx on Sun Apr 10 02:07:41 1988
  31. #
  32. # This archive contains:
  33. #    README        Makefile.BSD    Makefile.hpux    options.c    
  34. #    options.h    trapfile.1    trapfile.c    
  35. #
  36.  
  37. LANG=""; export LANG
  38.  
  39. echo x - README
  40. cat >README <<'@EOF'
  41. This packet contains the source for the 'trapfile' program, which runs
  42. a program as a subprocess while reporting to the user any calls the
  43. subprocess makes to the system calls 'open' and 'close'. To parse its
  44. command line arguments, 'trapfile' uses my 'options' library, which
  45. can be used by itself and is portable (whereas 'trapfile' is not).
  46.  
  47. trapfile.c        source for trapfile
  48. trapfile.1        manual page for trapfile (use nroff -man)
  49. options.h        include file for options parsing
  50. options.c        source code for options parser
  51. Makefile.BSD        makefile for 4.2BSD on Vax
  52. Makefile.hpux        makefile for HP-UX s300 and s800
  53.  
  54. No manual page for 'options' yet, sorry. Look at how 'trapfile' uses
  55. it.
  56.  
  57. Copy the appropriate makefile to Makefile and just type 'make'.  If
  58. your system is not either HP-UX or 4.2BSD, you are on your own. Try
  59. anyway, and if you succeed, I would be happy if you mailed your
  60. changes to me. 4.3BSD just might work directly, and there should be
  61. no very difficult changes for Sun-3.
  62.  
  63. me = Arndt Jonasson, aj@zyx.SE (uucp:  ...!uunet!mcvax!enea!zyx!aj)
  64. @EOF
  65.  
  66. chmod 664 README
  67.  
  68. echo x - Makefile.BSD
  69. cat >Makefile.BSD <<'@EOF'
  70. trapfile:    options.a trapfile.o
  71.         cc $(CFLAGS) -o trapfile trapfile.o options.a -ltermcap
  72.  
  73. trapfile.o:    options.h trapfile.c
  74.         cc $(CFLAGS) -c -DBSD trapfile.c
  75.  
  76. options.o:    options.h options.c
  77.         cc $(CFLAGS) -c -Dstrchr=index options.c
  78.  
  79. options.a:    options.o
  80.         ar r options.a options.o
  81.         ranlib options.a
  82. @EOF
  83.  
  84. chmod 664 Makefile.BSD
  85.  
  86. echo x - Makefile.hpux
  87. cat >Makefile.hpux <<'@EOF'
  88. trapfile:    trapfile.o options.a
  89.         cc $(CFLAGS) -o trapfile trapfile.o options.a -lcurses
  90.  
  91. trapfile.o:    trapfile.c options.h
  92.         cc $(CFLAGS) -c trapfile.c
  93.  
  94. options.o:    options.h options.c
  95.         cc $(CFLAGS) -c options.c
  96.  
  97. options.a:    options.o
  98.         ar r options.a options.o
  99. @EOF
  100.  
  101. chmod 664 Makefile.hpux
  102.  
  103. echo x - options.c
  104. cat >options.c <<'@EOF'
  105. /*
  106.   Prerelease 0.2 of the command line option parser, 28 Mar 88.
  107.   options.c
  108.  
  109.   Copyright Arndt Jonasson, 1988. Please send enhancements and corrections
  110.   back to the author.
  111.  
  112.   Author: Arndt Jonasson, Zyx Sweden
  113.   aj@zyx.SE    (...<backbone>!mcvax!enea!zyx!aj)
  114.  
  115.   No manual page yet.
  116. */
  117.  
  118. #include "options.h"
  119.  
  120. extern void exit ();        /* '#define void' if your compiler doesn't */
  121.                 /* have it.*/
  122.  
  123. char *O_programname = NULL;
  124.  
  125. static char *usage_string = NULL;
  126. static int option_error;
  127.  
  128. static fatal (msg)
  129. char *msg;
  130. {
  131.    fprintf (stderr, "option parsing failure: %s\n", msg);
  132.    exit (1);
  133. }
  134.  
  135. static char *basename (str)
  136. char *str;
  137. {
  138.    char *p1, *p2;
  139.    extern char *strchr ();
  140.  
  141.    p2 = str;
  142.    while ((p1 = strchr (p2, '/')) != NULL)
  143.    {
  144.       if (p1[1] == '\0')
  145.       {
  146.      p1[0] = '\0';
  147.      return p2;
  148.       }
  149.       p2 = p1 + 1;
  150.    }
  151.    return p2;
  152. }
  153.  
  154. static char *match (prefix, str)
  155. char *prefix, *str;
  156. {
  157.    int n = strlen (prefix);
  158.  
  159.    if (strncmp (prefix, str, n) == 0)
  160.       return str + n;
  161.    else
  162.       return NULL;
  163. }
  164.  
  165. O_usage ()
  166. {
  167.    if (usage_string != NULL)
  168.    {
  169.       if (O_programname == NULL)
  170.      fprintf (stderr, "valid options: %s\n", usage_string);
  171.       else
  172.      fprintf (stderr, "usage: %s %s\n", O_programname, usage_string);
  173.    }
  174.    else
  175.    {
  176.       if (O_programname != NULL)
  177.      fprintf (stderr, "%s: ", O_programname);
  178.       fprintf (stderr, "invalid usage\n");
  179.    }
  180.  
  181.    exit (1);
  182. }
  183.  
  184. int O_atoi (str)
  185. char *str;
  186. {
  187.    char c;
  188.    int base = 10;
  189.    int res = 0;
  190.    int negative = 1;
  191.    
  192. /*
  193.   No check for overflow.
  194. */
  195.  
  196.    c = str[0];
  197.    
  198.    if (*str == '-')
  199.    {
  200.       negative = -1;
  201.       str++;
  202.    }
  203.  
  204.    if (*str == '0')
  205.    {
  206.       if (*++str == 'x')
  207.       {
  208.      str++;
  209.      base = 16;
  210.       }
  211.       else
  212.      base = 8;
  213.    }
  214.    
  215.    if (*str == '\0' && (negative != -1 || base != 8)) /* kludgie */
  216.    {
  217.       option_error = 1;
  218.       return 0;
  219.    }
  220.  
  221.    for (;;)
  222.    {
  223.       switch (c = *str++)
  224.       {
  225.        case '8':
  226.        case '9':
  227.      if (base == 8)
  228.      {
  229.         option_error = 1;
  230.         return 0;
  231.      }
  232.        case '0':
  233.        case '1':
  234.        case '2':
  235.        case '3':
  236.        case '4':
  237.        case '5':
  238.        case '6':
  239.        case '7':
  240.      res = base * res + c - '0';
  241.      break;
  242.        case 'a':
  243.        case 'b':
  244.        case 'c':
  245.        case 'd':
  246.        case 'e':
  247.        case 'f':
  248.      if (base == 16)
  249.         res = base * res + c - 'a' + 10;
  250.      else
  251.      {
  252.         option_error = 1;
  253.         return 0;
  254.      }
  255.      break;
  256.        case '\0':
  257.      return (negative * res);
  258.      /* NOTREACHED */
  259.      break;
  260.        default:
  261.      option_error = 1;
  262.      return 0;
  263.       }
  264.    }
  265. }
  266.  
  267. double O_atof (s)
  268. char *s;
  269. {
  270. /*
  271.   No error-checking currently.
  272. */
  273.    extern double atof ();
  274.  
  275.    return atof (s);
  276. }
  277.  
  278. char *O_strid (s)
  279. char *s;
  280. {
  281.    return s;
  282. }
  283.  
  284. char O_chrid (s)
  285. char *s;
  286. {
  287.    return s[0];
  288. }
  289.  
  290. static get_it (p, arg, c)
  291. Option *p;
  292. char *arg;
  293. char c;
  294. {
  295.    option_error = 0;
  296.  
  297.    switch ((p->flags & 037))
  298.    {
  299.     case O_INT:
  300.       *p->ip = (*p->ipf) (arg);
  301.       if (option_error)
  302.       {
  303.      fprintf (stderr,
  304.           "Invalid integer '%s' given to the -%c option\n", arg, c);
  305.      O_usage ();
  306.       }
  307.       break;
  308.     case O_STR:
  309.       *p->cp = (*p->cpf) (arg);
  310.       break;
  311.     case O_DBL:
  312.       *p->dp = (*p->dpf) (arg);
  313.       if (option_error)
  314.       {
  315.      fprintf (stderr,
  316.       "Invalid floating-point number '%s' given to the -%c option\n", arg, c);
  317.      O_usage ();
  318.       }
  319.       break;
  320.     case O_CHR:
  321.       *p->tp = (*p->tpf) (arg);
  322.       break;
  323.     case O_MUL:
  324.       (*p->table_p)[p->data++] = arg;
  325.       break;
  326.     default:
  327.       fatal ("invalid option type");
  328.    }
  329. }
  330.  
  331. int O_parse_options (desc, argc, argv)
  332. Option *desc;
  333. int argc;
  334. char **argv;
  335. {
  336.    static char *empty_table[] = {NULL};
  337.    int first_char, multiple_error, multiple_add, hyphen_is_arg;
  338.    int min, max, i, remaining;
  339.    Option *p, *default_p = NULL;
  340.    char *cp, *dir, *arg;
  341.    char c;
  342.  
  343.    multiple_error = 1;
  344.    multiple_add = 0;
  345.    hyphen_is_arg = 1;
  346.    min = 0;
  347.    max = -1;
  348.  
  349.    for (p = desc; p->flags != O_END; p++)
  350.    {
  351.       p->data = 0;
  352.  
  353.       if (p->flags == O_DIR)
  354.       {
  355.      dir = p->directive;
  356.      if (cp = match ("usage: ", dir))
  357.         usage_string = cp;
  358.      else if (cp = match ("multiple: ", dir))
  359.      {
  360.         multiple_error = (strcmp ("error", cp) == 0);
  361.         multiple_add = (strcmp ("add", cp) == 0);
  362.      }
  363.      else if (cp = match ("remaining: ", dir))
  364.      {
  365.         int n;
  366.         char dummy;
  367.  
  368.         n = sscanf (cp, "%d%c%d", &min, &dummy, &max);
  369.         if (n == 1)
  370.            max = min;
  371.         else if (n == 2)
  372.            max = -1;
  373.      }
  374.      else
  375.         fatal ("unknown option directive");
  376.       }
  377.       else if (p->flags == O_MUL)
  378.      *p->table_p = (char **) malloc (argc * sizeof (char *));
  379.       else if (p->flags == O_INT && p->c == '\0')
  380.       {
  381.      p->c = -1;
  382.      default_p = p;
  383.       }
  384.       else if (p->flags == O_FLG && p->c == '\0')
  385.      hyphen_is_arg = 0;
  386.  
  387.       if (p->flags == O_FLG)
  388.      *p->ip = 0;
  389.    }
  390.  
  391.    O_programname = basename (argv[0]);
  392.  
  393.    for (i = 1; i < argc; i++)
  394.    {
  395.       arg = argv[i];
  396.       if (arg[0] != '-' || (arg[1] == '\0' && hyphen_is_arg))
  397.      break;
  398.  
  399.       first_char = 1;
  400.  
  401.       while ((c = *++arg) != '\0' || first_char)
  402.       {
  403.      for (p = desc; p->flags != O_END; p++)
  404.         if (p->c == c)
  405.         {
  406.            if (p->flags & 040)
  407.            {
  408.           fprintf (stderr, "The -%c option was given twice\n", c);
  409.           O_usage ();
  410.            }
  411.  
  412.            if (multiple_error && p->flags != O_MUL)
  413.           p->flags |= 040;
  414.  
  415.            if ((p->flags & 037) == O_FLG)
  416.            {
  417.  
  418.           if (multiple_add)
  419.              *(p->ip) += 1;
  420.           else
  421.              *(p->ip) = 1;
  422.           if (c == '\0')
  423.              goto next_argument;
  424.           else
  425.              goto next_character;
  426.            }
  427.  
  428.            arg += 1;
  429.            if (arg[0] == '\0')
  430.            {
  431.           if (++i == argc)
  432.           {
  433.              fprintf (stderr,
  434.                   "The -%c option requires an argument\n", c);
  435.              O_usage ();
  436.           }
  437.           arg = argv[i];
  438.            }
  439.            get_it (p, arg, c);
  440.            goto next_argument;
  441.         }
  442.      if ((p = default_p) != NULL && first_char)
  443.      {
  444.         *p->ip = (*p->ipf) (arg);
  445.         if (!option_error)
  446.            goto next_argument;
  447.      }
  448.      fprintf (stderr, "There is no -%c option\n", c);
  449.      O_usage ();
  450.  
  451.      /* NOTREACHED*/
  452.  
  453.        next_character:
  454.      first_char = 0;
  455.       }
  456.     next_argument: ;
  457.    }
  458.  
  459.    for (p = desc; p->flags != O_END; p++)
  460.       if ((p->flags & 037) == O_MUL)
  461.       {
  462.      if (p->data == 0)
  463.         (*p->table_p = empty_table);
  464.      else
  465.      {
  466.         (*p->table_p)[p->data++] = NULL;
  467.         *p->table_p = (char **) realloc (*p->table_p,
  468.                          p->data * sizeof (char *));
  469.      }
  470.       }
  471.  
  472.    remaining = argc - i;
  473.    if (remaining < min || max != -1 && remaining > max)
  474.    {
  475.       if (max == -1)
  476.      fprintf (stderr, "At least %d non-option argument%s required\n",
  477.           min, min == 1 ? " is" : "s are");
  478.       else if (max == 0)
  479.      fprintf (stderr, "No non-option arguments are allowed\n");
  480.       else if (min == max)
  481.      fprintf (stderr, "Exactly %d non-option argument%s required\n",
  482.           min, min == 1 ? " is" : "s are");
  483.       else
  484.      fprintf (stderr, "%d to %d non-option arguments are required\n",
  485.           min, max);
  486.       O_usage ();
  487.    }
  488.    return i;
  489. }
  490. @EOF
  491.  
  492. chmod 664 options.c
  493.  
  494. echo x - options.h
  495. cat >options.h <<'@EOF'
  496. /*
  497.   Prerelease 0.2 of the command line option parser, 28 Mar 88.
  498.   options.h
  499.  
  500.   Copyright Arndt Jonasson, 1988. Please send enhancements and corrections
  501.   back to the author.
  502.  
  503.   Author: Arndt Jonasson, Zyx Sweden
  504.   aj@zyx.SE    (...<backbone>!mcvax!enea!zyx!aj)
  505.  
  506.   No manual page yet.
  507. */
  508.  
  509. #include <stdio.h>
  510.  
  511. typedef struct
  512. {
  513.    char c;
  514.    int flags;
  515.    char **cp;
  516.    char *(*cpf) ();
  517.    int *ip;
  518.    int (*ipf) ();
  519.    double *dp;
  520.    double (*dpf) ();
  521.    char *tp;
  522.    char (*tpf) ();
  523.    char ***table_p;
  524.    char *directive;
  525.    int data;
  526. } Option;
  527.  
  528. extern int O_atoi ();
  529. extern double O_atof ();
  530. extern char O_chrid ();
  531.  
  532. extern char *O_strid ();
  533. extern char *O_programname;
  534.  
  535. #define O_FLG    0
  536. #define O_INT    1
  537. #define O_STR    2
  538. #define O_DBL    3
  539. #define O_END    4
  540. #define O_DIR    5
  541. #define O_CHR    6
  542. #define O_MUL    7
  543.  
  544.  
  545. #define O_flg(c, ip)    {c, O_FLG, 0, 0, &ip, 0, 0, 0, 0, 0, 0, 0}
  546. #define O_int(c, ip)    {c, O_INT, 0, 0, &ip, O_atoi, 0, 0, 0, 0, 0, 0}
  547. #define O_str(c, cp)    {c, O_STR, &cp, O_strid, 0, 0, 0, 0, 0, 0, 0, 0}
  548. #define O_dbl(c, dp)    {c, O_DBL, 0, 0, 0, 0, &dp, O_atof, 0, 0, 0, 0}
  549. #define O_end        {-1, O_END, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  550. #define O_directive(str) {-1, O_DIR, 0, 0, 0, 0, 0, 0, 0, 0, 0, str}
  551. #define O_chr(c, cp)    {c, O_CHR, 0, 0, 0, 0, 0, 0, &cp, O_chrid, 0, 0}
  552. #define O_mul(c, cpp)    {c, O_MUL, 0, 0, 0, 0, 0, 0, 0, 0, &cpp, 0}
  553.  
  554. #define O_Empty            '\0'
  555.  
  556. #define usage()            O_usage ()
  557. @EOF
  558.  
  559. chmod 664 options.h
  560.  
  561. echo x - trapfile.1
  562. cat >trapfile.1 <<'@EOF'
  563. .TH TRAPFILE 1 local ""
  564. .SH NAME
  565. trapfile - trap all file opening calls in a program
  566. .SH SYNOPSIS
  567. .B trapfile
  568. [options] command [args ...]
  569. .SH DESCRIPTION
  570. .I trapfile
  571. runs an application program in an inferior fork, trapping all calls
  572. the application makes to the system calls
  573. .I open
  574. and
  575. .IR close ,
  576. and reporting them on standard error. The report consists of the name
  577. of the system call and its arguments. If the call was successful, the return
  578. value is shown (except for
  579. .IR close,
  580. which always returns 0 when successful), otherwise an error message,
  581. as defined by
  582. .IR perror (3).
  583. In order to let subprocesses of the application run correctly,
  584. .I trapfile
  585. must also trap calls to
  586. .I fork
  587. (and
  588. .IR vfork ,
  589. if it exists). Thus, these system calls will be reported too.
  590. .PP
  591. On some systems, for complicated reasons, it doesn't work to single-step a
  592. call to
  593. .I fork
  594. (or
  595. .IR vfork ).
  596. Instead
  597. .I trapfile
  598. lets the subprocess run free for a short while. When this happens, only
  599. the call itself will be reported, not the result (whether successful
  600. or not).
  601. .PP 
  602. The following options exist:
  603. .TP 11
  604. .BR -l
  605. Don't run the program; only print out all system calls in it to standard
  606. output, and the
  607. locations they would be in if the program were run.
  608. .TP
  609. .BR -s
  610. Report signals sent to the application as well as system calls.
  611. .TP
  612. .BI -w " time"
  613. When the application running under
  614. .I trapfile
  615. spawns a child of its own,
  616. .I trapfile
  617. can no longer write breakpoints into the address space of the application,
  618. since the system will have made the core image shared and thus non-writable
  619. (unless the application is of the old non-shared type, see
  620. .IR ld (1).
  621. In this situation,
  622. .I trapfile
  623. reports why it is waiting, and by default sleeps until the child of
  624. the application has done an exec or exited, at which time the application's
  625. address space will be writable again. If the
  626. .B \-w
  627. option is used,
  628. .I time
  629. indicates how many seconds
  630. .I trapfile
  631. is to wait before continuing to run. In this case, no more system call
  632. reports will be
  633. issued, since the application is now running free. Signal reporting, if enabled
  634. with the
  635. .B \-s
  636. option, will still be done.
  637. .TP
  638. .B -i
  639. Turn off interrupts for
  640. .IR trapfile .
  641. Normally, interrupt, hangup and quit signals kill both
  642. .I trapfile
  643. and its subprocess.
  644. .TP
  645. .B -d
  646. When
  647. .I trapfile
  648. looks for system calls in the program file, it normally only searches
  649. the code area (text segment) for system calls. This is sufficient for
  650. the majority of all programs. With the
  651. .B \-d
  652. option, the entire file is searched.
  653. .TP
  654. .B -r
  655. Display the reports given by
  656. .I trapfile
  657. in reverse video on the terminal, if possible.
  658. .TP
  659. .BI -T " term"
  660. Use the terminal definition for
  661. .IR term ,
  662. instead of the one given by the environment variable TERM. This is only
  663. useful together with the
  664. .B \-r
  665. option.
  666. .TP
  667. .BI -o " logfile"
  668. Outputs the reports to the file
  669. .I logfile
  670. instead of standard error.
  671. .SH WARNING
  672. Since
  673. .I trapfile
  674. makes very heavy use of the SIGTRAP signal, the application is not likely
  675. to run correctly if it uses that signal for any purpose.
  676. .SH ENVIRONMENT VARIABLES
  677. .TP 11
  678. TERM
  679. The name of the terminal database entry. Used for obtaining the escape
  680. sequences for reverse video when using the
  681. .B \-r
  682. option, unless overridden by the
  683. .B \-T
  684. option.
  685. .TP
  686. TMPDIR
  687. If
  688. .I trapfile
  689. doesn't have write access to the application program file,
  690. it has to make a temporary copy; otherwise it won't be able to set any
  691. breakpoints in the program image. The temporary copy is by default put in the
  692. directory
  693. .BR /tmp ,
  694. unless overridden by the TMPDIR environment variable.
  695. Setting TMPDIR only works in systems that have the
  696. .B tempnam(3)
  697. library routine.
  698. .SH FILES
  699. .TP 11
  700. /tmp/trapfXXXXXX
  701. Temporary writable copy of the application.
  702. .SH SYSTEM DEPENDENCIES
  703. .I trapfile
  704. currently works for HP-UX s300 (MC68020), HP-UX s800 (HP Precision
  705. Architecture) and 4.2BSD (Vax-11/750). Due to the small granularity in
  706. the Vax (system calls instructions are only two bytes and may start at
  707. any address), the possibility of finding what looks like a system
  708. call but isn't, and thus trapping the wrong location, is larger on a Vax.
  709.  
  710. Of these three systems, only the HP-UX s300 can single-step calls to
  711. .IR fork .
  712. .SH SEE ALSO
  713. adb(1), ptrace(2), perror(3), tempnam(3).
  714. .SH BUGS
  715. .I trapfile
  716. doesn't look at the PATH environment variable when opening the application
  717. file. Thus, the full pathname of the application must be given.
  718. .PP
  719. In a program containing several instances of one of the trapped system
  720. calls (\fIopen\fR,
  721. .IR close ,
  722. .I fork
  723. or
  724. .IR vfork ),
  725. only the first occurrence will be trapped; the others are ignored.
  726. .PP
  727. If the application receives a signal during a
  728. .I fork
  729. call on a system where single-stepping a
  730. .I fork
  731. can't be done, the newly created child process may not run correctly.
  732. .SH DIAGNOSTICS
  733. "couldn't remove temporary file XXXXXX"
  734. .IP
  735. The temporary copy of the application program couldn't be removed.
  736. See the discussion of TMPDIR.
  737. .PP
  738. "child killed by signal NNN"
  739. .br
  740. "child killed by signal NNN (core dumped)"
  741. .IP
  742. The application was killed by a signal, and possibly a core dump was
  743. produced.
  744. .PP
  745. "TERM not set; can't use reverse video"
  746. .br
  747. "terminal type XXX not known; can't use reverse video"
  748. .br
  749. "terminal type XXX doesn't support reverse video"
  750. .IP
  751. The
  752. .B \-r
  753. option was requested, but reverse video can't be used, for
  754. the named reason. The program will ignore the
  755. .B \-r
  756. option.
  757. .PP
  758. "the application is setuid and may not run correctly"
  759. .IP
  760. Since traced programs do not honour the setuid/setgid bit, a
  761. program that needs a special user identity to run correctly may
  762. fail when run under
  763. .IR trapfile .
  764. .PP
  765. "couldn't exec XXX"
  766. .IP
  767. The application exists, but couldn't be started.
  768. .PP
  769. "couldn't open XXX for writing, using stderr"
  770. .IP
  771. The logfile given with the
  772. .B \-o
  773. option couldn't be opened.
  774. .PP
  775. "warning, multiple occurrences of trap NN"
  776. .IP
  777. Only the first of several occurrences of a system call will be 
  778. trapped; the rest will be ignored. This can cause file openings
  779. not to be reported, or worse, a child process of the application
  780. to terminate prematurely.
  781. .PP
  782. "Waiting for child's child to exit or exec"
  783. .br
  784. "No more system calls will be made"
  785. .IP
  786. See the discussion of the
  787. .B \-w
  788. option.
  789. .PP
  790. These messages are followed by a standard error printout if relevant (as
  791. printed by
  792. .IR perror (3).
  793. .PP
  794. Messages containing the word
  795. .I fatal
  796. indicate some internal error in
  797. .IR trapfile ,
  798. caused either by a bug, or by external circumstances preventing
  799. .I trapfile
  800. from continuing.
  801. .SH AUTHOR
  802. Arndt Jonasson
  803. @EOF
  804.  
  805. chmod 664 trapfile.1
  806.  
  807. echo x - trapfile.c
  808. cat >trapfile.c <<'@EOF'
  809. /*
  810.   Author: Arndt Jonasson, Zyx Sweden AB
  811.   Mail address: aj@zyx.SE    (uucp: <backbone>!mcvax!enea!zyx!aj)
  812.   Date: March 20, 1988
  813.   Version 1.0
  814.  
  815.   This program arose from a question in the news group comp.unix.questions
  816.   (or was it comp.unix.wizards?) - someone wanted to know whether the
  817.   functionality of TOPS-20's "Set Trap File-Openings" command existed in
  818.   Unix. The answer is "well, yes, in a way" and this program is the result.
  819.  
  820.   It is very dependent on the 'ptrace(2)' system call and knows a few
  821.   system- and processor-dependent things about what a system call looks
  822.   like.
  823.  
  824.   Assuming that 'ptrace(2)' exists and that the registers can be accessed,
  825.   it shouldn't be too hard to port.
  826. */
  827.  
  828. /*
  829.   Bad style:
  830.  
  831.   We use goto.
  832.  
  833.   We deliberately use a preprocessor definition with no () around it,
  834.   but I won't fix that - it's too convenient. Just watch out for it.
  835.  
  836.   We assume sizeof(int) = 4.
  837. */
  838.  
  839. /*
  840.   All the different preprocessor symbols are quite intertangled; e.g.
  841.   BSD currently means not only 4.2BSD Unix, but the Vax processor.
  842.   hp9000s300 implies the MC68020 processor. It's too soon to untangle
  843.   this until this program has been ported to more systems.
  844.  
  845.   Currently used preprocessor symbols:
  846.  
  847.   hpux                HP-UX system.
  848.   hp9000s300, hp9000s800    Imply hpux.
  849.   BSD                4.2BSD.
  850.  
  851.   BSD currently implies Vax processor. hp9000s800 implies HP PA, and
  852.   hp9000s300 implies MC68020.
  853.  
  854.   BSD and hpux are mutually exclusive. So are hp9000s300 and hp9000s800.
  855. */
  856.  
  857. /*
  858.   What does a system call look like?
  859.  
  860.   General assumptions: system calls have a uniform calling sequence on
  861.   any given machine, with the arguments in known places. When a system
  862.   call returns, either the result is in a certain register, or an
  863.   error value defined by <errno.h> is in that same register. There
  864.   exists an instruction that will generate a SIGTRAP for the process
  865.   executing it.
  866.  
  867.   ==================
  868.   HP-UX s300, 68020:
  869.   ==================
  870.  
  871.   MOVQ        &0x5,%d0    0x7005
  872. or
  873.   MOV.W        &0xA5,%d0    0x303c
  874.                 0xA5
  875. or
  876.   MOV.L        &0xA5,%d0    0x203c
  877.                   0x0
  878.                 0xA5
  879. followed by
  880.   TRAP        &0        0x4e40
  881.  
  882.   Top of stack is return pc, then come the arguments.
  883.   If the call failed, 0x10000 in the status flag word is set, and errno is
  884.   in d0. If the call was successful, the result is typically in d0.
  885.  
  886.   The trap is transformed into a 'trap &1' (0x4e41).
  887.  
  888.   =================
  889.   HP-UX s800, HPPA:
  890.   =================
  891.   
  892.   ldil        -40000000,r1
  893.   ble        4(sr7,r1)    0xe420e008
  894.   ldo        0xA5(r0),r22    0x341600e0
  895.  
  896.   Arguments are passed in arg0, arg1, etc.
  897.   If the call failed, r22 is 1 with errno in ret0. Otherwise r22 is 0,
  898.   and the return value is in ret0. The 'ble' jumps into execute-only
  899.   kernel space, and there the real trap instruction lies. Can't put
  900.   a breakpoint there, of course.
  901.  
  902.   The ble is supplanted with a 'break 4,8' (0x10004).
  903.  
  904.   ============
  905.   4.2BSD, Vax:
  906.   ============
  907.  
  908.   chmk        $4        0x04bc
  909. OR
  910.   chmk        $42        0x8fbc
  911.                 0x42
  912.  
  913.   Arguments are passed on the stack, but the system call is typically
  914.   called via the 'calls' instruction that pushes five additional
  915.   words on the stack before the return pc. We have to adjust the sp
  916.   accordingly. Return value in R0.
  917.  
  918.   The chmk is replaced by a 'bpt' (0x803).
  919. */
  920.  
  921. #include "options.h"        /* My option parser. */
  922.  
  923. #include <sys/types.h>
  924. #include <sys/stat.h>
  925. #include <fcntl.h>
  926. #include <stdio.h>
  927. #include <signal.h>
  928.  
  929. #include    <machine/reg.h>
  930. #ifdef hpux
  931. # define PS    16        /* These two are not defined in reg.h */
  932. # define PC    16 + 2        /* WARNING: BIG KLUDGE (but how convenient) */
  933. #endif
  934.  
  935. #ifdef hpux
  936. # include <sys/ptrace.h>
  937. #else
  938. # define    PT_SETTRC    0
  939. # define    PT_RIUSER    1
  940. # define    PT_RDUSER    2
  941. # define    PT_RUAREA    3
  942. # define    PT_WIUSER    4
  943. # define    PT_WDUSER    5
  944. # define    PT_WUAREA    6
  945. # define    PT_CONTIN    7
  946. # define    PT_EXIT        8
  947. # define    PT_SINGLE    9
  948. #endif
  949.  
  950. /*
  951.   System call numbers. Some systems define them in an include file, some
  952.   don't. The numbers are the same on all the systems where this program
  953.   currently runs.
  954. */
  955.  
  956. #ifndef hpux
  957. # define SYS_FORK    0x2
  958. # define SYS_OPEN    0x5
  959. # define SYS_CLOSE    0x6
  960. #endif
  961.  
  962. #ifndef hp9000s300
  963. # define SYS_VFORK    0x42
  964. #endif
  965.  
  966. #include <sys/param.h>
  967. #include <sys/dir.h>
  968.  
  969. #ifdef hp9000s800
  970. # include <filehdr.h>
  971. # include <aouthdr.h>
  972. # include <spacehdr.h>
  973. # include <scnhdr.h>
  974. #else
  975. # ifdef BSD
  976. #  include <a.out.h>
  977. # endif
  978. # include <sys/user.h>
  979. #endif
  980.  
  981. /*
  982.   Unix system incompatibility.
  983. */
  984.  
  985. #ifndef BSD
  986. # include <string.h>
  987. #else
  988. # include <strings.h>
  989. # define strchr        index
  990. #endif
  991.  
  992. #include <errno.h>
  993. extern char *sys_errlist[];    /* And pray tell me, why aren't */
  994. extern int sys_nerr;        /* these defined in <errno.h>? */
  995. extern void perror ();
  996. #ifdef BSD
  997. extern int errno;        /* sigh ... */
  998. #endif
  999.  
  1000. extern int tgetent ();        /* For obtaining information from the */
  1001. extern char *tgetstr ();    /* terminfo or termcap database. */
  1002.  
  1003. extern void _exit (), exit ();
  1004. extern unsigned long sleep ();
  1005. extern unsigned short geteuid (), getegid ();
  1006. extern char *getenv ();
  1007.  
  1008. char *signame[] =
  1009. {
  1010.    "NAL 0",
  1011.    "HUP", "INT", "QUIT", "ILL", "TRAP",
  1012.    "IOT", "EMT", "FPE", "KILL", "BUS",
  1013.    "SEGV", "SYS", "PIPE", "ALRM", "TERM",
  1014. #ifdef BSD
  1015.    "URG", "STOP", "TSTP", "CONT", "CHLD",
  1016.    "TTIN", "TTOU", "IO", "XCPU", "XFSZ",
  1017.    "VTALRM", "PROF"
  1018. #else
  1019.    "USR1", "USR2", "CLD", "PWR", "VTALRM",
  1020.    "PROF", "IO", "WINDOW", "STOP", "TSTP",
  1021.    "CONT", "TTIN", "TTOU", "URG"
  1022. #endif
  1023. };
  1024.  
  1025. /*
  1026.   Approximate - it's only important that the number be large enough.
  1027. */
  1028.  
  1029. #ifdef BSD
  1030. # define SYS_n    153
  1031. #else
  1032. # define SYS_n    239
  1033. #endif
  1034.  
  1035. typedef struct
  1036. {
  1037.    int nr;            /* The entry's index in the 'calls' array. */
  1038.    enum {Absent, Present, Bpt} type; /* Status of this system call. */
  1039.    int old_word;        /* Old contents before breakpoint was set. */
  1040.    int new_word;        /* Breakpoint instruction. */
  1041.    int address;            /* Address in subprocess's address space. */
  1042. }
  1043. system_call;
  1044. system_call calls[SYS_n];    /* Array of all system calls in the */
  1045.                 /* application, indexed by trap number. */
  1046.  
  1047. #define Check(d, msg)    if (!(d)) fatal (msg, 1)
  1048.  
  1049. #define with_reverse_video(x)        do { \
  1050.                          fputs (rev_on, log); \
  1051.                          x \
  1052.                          fputs (rev_off, log); \
  1053.                        } while (0)
  1054.  
  1055. char *tempfile = NULL;        /* If application executable isn't writable, */
  1056.                 /* this is the name of the temporary file. */
  1057. char *term = NULL;        /* Terminal type, set by the -T option. */
  1058. FILE *log;            /* Name of logfile, set by the -o option. */
  1059. char *rev_on, *rev_off;        /* Control sequences to turn on/off reverse */
  1060.                 /* video on the terminal. Set to "" when no */
  1061.                 /* such capability is available or desired. */
  1062. int report_signals = 0;        /* If non-zero, report signals. Set by the */
  1063.                 /* -s option. */
  1064. int sigm = 0;            /* Mask of pending signals. */
  1065.  
  1066.  
  1067. /*
  1068.   Return to our superior, with the given exit status. The log file is closed
  1069.   and the temporary copy removed, if one was made.
  1070. */
  1071.  
  1072. cleanup_and_exit (status)
  1073. int status;
  1074. {
  1075.    int s;
  1076.  
  1077.    fclose (log);
  1078.  
  1079.    if (tempfile != NULL)
  1080.    {
  1081.       s = unlink (tempfile);
  1082.       if (s == -1 && errno != ENOENT)
  1083.      fprintf (stderr, "%s: couldn't remove temporary file %s\n",
  1084.           O_programname, tempfile);
  1085.    }
  1086.    exit (status);
  1087. }
  1088.  
  1089. /*
  1090.   Report a fatal error on stderr. If 'perr' is set, 'errno' contains useful
  1091.   information, so print it using 'perror'. Then exit with status 1.
  1092. */
  1093.  
  1094. fatal (msg, perr)
  1095. char *msg;
  1096. int perr;
  1097. {
  1098.    fprintf (stderr, "%s: fatal - %s", O_programname, msg);
  1099.    if (perr)
  1100.    {
  1101.       fprintf (stderr, " - ");
  1102.       perror ("");
  1103.    }
  1104.    else
  1105.       fprintf (stderr, "\n");
  1106.    cleanup_and_exit (1);
  1107.  
  1108. /*
  1109.   From a pathname, extract the last component.
  1110. */
  1111.  
  1112. char *basename (str)
  1113. char *str;
  1114. {
  1115.    char *p1, *p2;
  1116.  
  1117.    p2 = str;
  1118.    while ((p1 = strchr (p2, '/')) != NULL)
  1119.       p2 = p1 + 1;
  1120.    return p2;
  1121. }
  1122.  
  1123. /* 
  1124.   If 'sig' is non-zero, it is a pending signal for the subprocess
  1125.   'pid'. Report it to the user if desired. Then continue the subprocess,
  1126.   giving it the signal.
  1127. */
  1128.  
  1129. report_signal_and_continue (pid, sig)
  1130. int pid, sig;
  1131. {
  1132.    int s;
  1133.  
  1134.    if (report_signals && sig > 0)
  1135.       with_reverse_video
  1136.      ({
  1137.         fprintf (log, "SIG%s\n", signame[sig]);
  1138.      });
  1139.  
  1140.    s = ptrace (PT_CONTIN, pid, 1, sig);
  1141.    Check (s != -1, "couldn't continue subprocess after signal");
  1142. }
  1143.  
  1144. /*
  1145.   Let the subprocess 'pid' run until it either exits or encounters the signal
  1146.   'sig'. The value -1 for 'sig' means return on all signals. The return
  1147.   value is the signal that the subprocess received, unless it was a SIGTRAP,
  1148.   in which case the return value is 0.
  1149. */
  1150.  
  1151. int do_wait (pid, sig)
  1152. int pid, sig;
  1153. {
  1154.    int s, status, lo, hi;
  1155.  
  1156. /*
  1157.   o If 'wait' returns some other pid than the one we expect, try again.
  1158.     When using pipes in sh, such things can happen.
  1159.   o If the child exited normally, so do we, with the same exit status.
  1160. */
  1161.  
  1162.    for (;;)
  1163.    {
  1164.       s = wait (&status);
  1165.  
  1166.       if (s == -1)
  1167.       {
  1168.      if (errno == EINTR)
  1169.         continue;
  1170.      else
  1171.         fatal ("wait failed", 1);
  1172.       }
  1173.  
  1174.       if (s != pid)
  1175.      continue;
  1176.  
  1177.       lo = (status) & 0377;
  1178.       hi = ((status) >> 8) & 0377;
  1179.  
  1180.       if (lo == 0177)
  1181.       {
  1182.      if (hi == SIGTRAP)
  1183.         return 0;
  1184.  
  1185.      if (sig == -1)
  1186.      {
  1187.         sigm |= (1 << hi);
  1188.         return hi;
  1189.      }
  1190.  
  1191.      report_signal_and_continue (pid, hi);
  1192.      continue;
  1193.       }
  1194.  
  1195.       if (lo == 0)
  1196.      cleanup_and_exit (hi);
  1197.  
  1198.       if (hi == 0)
  1199.       {
  1200.      fprintf (stderr, "%s: child killed by signal %d%s\n",
  1201.           O_programname,
  1202.           lo & 0177,
  1203.           (lo & 0200) ? "(core dumped)" : "");
  1204.      cleanup_and_exit (lo);
  1205.       }
  1206.    }
  1207. }
  1208.  
  1209. /*
  1210.   Read a 16-bit chunk from the file 'f'. Depending on the processor, the
  1211.   first byte will be either the low or the high byte.
  1212. */
  1213.  
  1214. unsigned int get_chunk (f)
  1215. FILE *f;
  1216. {
  1217.    static int first = 1;
  1218.    static int saved;
  1219.  
  1220.    if (!first)
  1221.    {
  1222.       first = 1;
  1223. #ifdef BSD
  1224.       return (saved >> 16) & 0xffff;
  1225. #else
  1226.       return (saved & 0xffff);
  1227. #endif
  1228.    }
  1229.    else
  1230.    {
  1231.       first = 0;
  1232.       saved = getw (f);
  1233. #ifdef BSD
  1234.       return (saved & 0xffff);
  1235. #else
  1236.       return (saved >> 16) & 0xffff;
  1237. #endif
  1238.    }
  1239. }
  1240.  
  1241. /*
  1242.   Given an address in the application's code area, the system call at that
  1243.   address is returned, or NULL if it wasn't found.
  1244. */
  1245.  
  1246. system_call *find_system_call (adr)
  1247. int adr;
  1248. {
  1249.    int i;
  1250.  
  1251.    for (i = 0; i < SYS_n; i++)
  1252.       if (calls[i].address == adr)
  1253.      return &calls[i];
  1254.  
  1255.    return NULL;
  1256. }
  1257.  
  1258. /*
  1259.   Obtain the sequences for turning reverse video on and off. If that fails,
  1260.   they are set to the empty string.
  1261. */
  1262.  
  1263. setup_standout_strings (on, off)
  1264. char **on, **off;
  1265. {
  1266.    int s;
  1267.    char *r_on, *r_off;
  1268.    static char termbuf[1024], databuf[1024];
  1269.    char *scratch = databuf;
  1270.  
  1271.    *on = *off = "";
  1272.  
  1273.    if (term == NULL)
  1274.       term = getenv ("TERM");
  1275.    if (term == NULL)
  1276.    {
  1277.       fprintf (stderr, "%s: TERM not set; can't use reverse video\n",
  1278.            O_programname);
  1279.       return;
  1280.    }
  1281.    s = tgetent (termbuf, term);
  1282.    if (s != 1)
  1283.    {
  1284.       fprintf (stderr,
  1285.            "%s: terminal type \"%s\" not known; can't use reverse video\n",
  1286.            O_programname, term);
  1287.       return;
  1288.    }
  1289.    r_on = tgetstr ("so", &scratch);
  1290.    r_off = tgetstr ("se", &scratch);
  1291.    if (r_on == 0 || r_off == 0)
  1292.    {
  1293.       fprintf (stderr,
  1294.            "%s: terminal type \"%s\" doesn't support reverse video\n",
  1295.            O_programname, term);
  1296.       return;
  1297.    }
  1298.    *on = r_on;
  1299.    *off = r_off;
  1300. }
  1301.  
  1302. /*
  1303.   If the application is setuid or setgid to something other than the current
  1304.   user's id, give a warning.
  1305. */
  1306.  
  1307. warn_if_setuid (file)
  1308. char *file;
  1309. {
  1310.    int s;
  1311.    struct stat b;
  1312.  
  1313.    s = stat (file, &b);
  1314.    Check (s == 0, "couldn't stat program file");
  1315.    if (((b.st_mode & S_ISUID) && geteuid () != b.st_uid)
  1316.        ||
  1317.        ((b.st_mode & S_ISGID) && getegid () != b.st_gid))
  1318.    {
  1319.       fprintf (stderr,
  1320.            "%s: the application is set%cid and may not work correctly\n",
  1321.            O_programname, (b.st_mode & S_ISUID) ? 'u' : 'g');
  1322.    }
  1323. }
  1324.  
  1325. /*
  1326.   Like 'perror', but to an arbitrary file stream and without the newline.
  1327. */
  1328.  
  1329. print_error (f, err)
  1330. FILE *f;
  1331. int err;
  1332. {
  1333.    if (err < 0 || err > sys_nerr)
  1334.       fprintf (f, " Error %d", err);
  1335.    else
  1336.       fprintf (f, " %s", sys_errlist[err]);
  1337. }
  1338.  
  1339. /* 
  1340.   Report the result of a system call. 'print_p' tells whether to print the
  1341.   result if the call succeeded (useless in the case of 'close', for instance).
  1342.   'flags' is some value that says whether the system call succeeded or not.
  1343.   Mostly it is the processor's status register. What bits in it are relevant
  1344.   is processor-specific.
  1345. */
  1346.  
  1347. print_result (f, val, flags, print_p)
  1348. FILE *f;
  1349. int val, flags, print_p;
  1350. {
  1351. #ifdef hp9000s800
  1352.    if (flags != 0)
  1353. #endif
  1354. #ifdef BSD
  1355.    if (flags & 1)
  1356. #endif
  1357. #ifdef hp9000s300
  1358.    if (flags & 0x10000)
  1359. #endif
  1360.       print_error (f, val);
  1361.    else if (print_p)
  1362.       fprintf (f, " = %d", val);
  1363.  
  1364.    fprintf (f, "\n");
  1365. }
  1366.  
  1367. /*
  1368.   The nice 'tempnam' function doesn't exist in 4.2BSD, so we use what
  1369.   we have, i.e. 'mktemp'.
  1370. */
  1371.  
  1372. char *tempfile_name ()
  1373. {
  1374.    char *s;
  1375.  
  1376. #ifndef BSD
  1377.    extern char *tempnam ();
  1378.    s = tempnam ("/tmp", "trapf");
  1379. #else
  1380.    extern char *mktemp ();
  1381.    s = mktemp ("/tmp/trapfXXXXXX");
  1382. #endif
  1383.    return s;
  1384. }
  1385.  
  1386. /*
  1387.   Start the application in an inferior fork (the fork will already have been
  1388.   done). 'f' is the logfile - if not stderr, the application shouldn't see
  1389.   it.
  1390. */
  1391.  
  1392. do_exec (f, file, argv)
  1393. FILE *f;
  1394. char *file;
  1395. char **argv;
  1396. {
  1397.    if (f != stderr)
  1398.       close (fileno (f));
  1399.  
  1400.    (void) signal (SIGINT, SIG_DFL);
  1401.    (void) signal (SIGQUIT, SIG_DFL);
  1402.    argv[0] = basename (argv[0]);
  1403.    (void) execv (file, argv);
  1404.    fatal ("couldn't exec program", 1);
  1405. }
  1406.  
  1407. /*
  1408.   Obtain the base of the register area in the u area of the application.
  1409.   Apparently, this changes when the application is running, so it has
  1410.   to be done each time the application has been continued or single-stepped.
  1411.  
  1412.   In HP-UX s300, the register area lies somewhat after the user area proper,
  1413.   and is accessed in the same way. All we need to know is the address of that
  1414.   area, and the address of the start of the user area, so we can give a
  1415.   relative address to 'ptrace'. That relative address is given by
  1416.   <machine/reg.h>, but not for pc and ps. ps is immediately after SP, and
  1417.   since it is only two bytes, pc can't be defined as an index into the
  1418.   u.u_ar0 array.
  1419.  
  1420.   In 4.2BSD Vax, the above remains valid, except that pc and pc are indeed
  1421.   defined in reg.h.
  1422.  
  1423.   In HP-UX s800, the register area lies somewhere totally different, and is
  1424.   not accessed through the user area. There is a special request P_RUREGS
  1425.   for reading the registers.
  1426. */
  1427.  
  1428. #ifndef hp9000s800
  1429. int get_reg_base (pid)
  1430. {
  1431.    int base, minus;
  1432.    struct user u;
  1433.  
  1434. #define u_offset(a)        ((char *) a - (char *) &u)
  1435.  
  1436.    base = ptrace (PT_RUAREA, pid, u_offset (&u.u_ar0), 0);
  1437.    Check (base != -1, "couldn't get user area register block pointer");
  1438.    minus = ptrace (PT_RUAREA, pid, u_offset (&u.u_ap), 0) - u_offset (u.u_arg);
  1439.    Check (minus != -1, "couldn't get user area u_ap");
  1440.  
  1441.    return base - minus;
  1442. }
  1443. #endif
  1444.  
  1445. /*
  1446.   Read a structure from a file into memory, complain if it failed.
  1447. */
  1448.  
  1449. #define slurp(f, x)    if (fread (&(x), sizeof (x), 1, f) != 1) \
  1450.                    fatal ("slurp", 1)
  1451.  
  1452. /*
  1453.   From the file stream 'f', open to an executable file, obtain the offset
  1454.   of the code in the file, the offset of the code in the address space when
  1455.   the program is run, and the size of the code area.
  1456. */
  1457.  
  1458. get_offsets (f, file, code, size)
  1459. int *file, *code, *size;
  1460. FILE *f;
  1461. {
  1462. #ifdef hp9000s800
  1463.  
  1464. /*
  1465.   In s800, the code always starts at 0x800 in the process's address space.
  1466.   The start of code in the file can be obtained by examination of the
  1467.   header.
  1468. */
  1469.  
  1470.    struct header hdr;
  1471.    struct som_exec_auxhdr auxhdr;
  1472.    int n, i;
  1473.     
  1474.    slurp (f, hdr);
  1475.  
  1476.    fseek (f, hdr.aux_header_location, 0);
  1477.    slurp (f, auxhdr);
  1478.    if (auxhdr.som_auxhdr.type != HPUX_AUX_ID)
  1479.       fatal ("not right header type", 0);
  1480.  
  1481.    *file = auxhdr.exec_tfile;
  1482.    *code = 0x800;
  1483.    *size = auxhdr.exec_tsize;
  1484. #else
  1485.  
  1486. /*
  1487.   In s300 and BSD, the code always starts at 0 in the process's address space.
  1488.   Depending on the kind of executable, the start of the code in the
  1489.   file is different.
  1490. */
  1491.  
  1492.    struct exec hdr;
  1493.  
  1494. #ifdef hpux
  1495. # define N_TXTOFF    TEXT_OFFSET
  1496. #endif
  1497.  
  1498.    slurp (f, hdr);
  1499.  
  1500.    if (N_BADMAG (hdr))
  1501.       fatal ("unknown magic number", 0);
  1502.  
  1503.    *file = N_TXTOFF (hdr);
  1504.    *code = 0;
  1505.    *size = hdr.a_text;
  1506. #endif
  1507. }
  1508.  
  1509. /*
  1510.   Return index of lowest set bit in *maskp. Return 0 if *maskp is 0. Lowest
  1511.   bit in *maskp will never be set, since that would mean signal 0. If that
  1512.   signal occurs, something is seriously wrong.
  1513. */
  1514.  
  1515. int pick_first_signal (maskp)
  1516. unsigned int *maskp;
  1517. {
  1518.    unsigned int m = *maskp;
  1519.    int i = 0;
  1520.  
  1521.    if (m == 0)
  1522.       return 0;
  1523.  
  1524.    for (;;)
  1525.    {
  1526.       i += 1;
  1527.       m >>= 1;
  1528.       if (m & 1)
  1529.       {
  1530.      *maskp ^= (1 << i);
  1531.      return i;
  1532.       }
  1533.    }
  1534. }
  1535.  
  1536. main (argc, argv)
  1537. int argc;
  1538. char **argv;
  1539. {
  1540. #ifdef hp9000s800
  1541.    struct save_state state;
  1542. # define s_offset(a)    ((char *) &state.a - (char *) &state)
  1543. #endif
  1544.  
  1545.    int sig;
  1546.    int address;
  1547.    char *string_arg;
  1548.    char **command;
  1549.    system_call *p;
  1550. #ifndef hp9000s800
  1551.    int reg_base;
  1552.    int sp;
  1553. #endif
  1554.    int trapno;
  1555.    int pid, i;
  1556.    FILE *f;
  1557.    int *ip;
  1558.    int bytes;
  1559.    unsigned int w1, w2;
  1560.    int val, arg1, arg2, arg3, res;
  1561.    int s;
  1562.    int pc, flags;
  1563.    int c_offset, f_offset, c_size;
  1564.    char *file;
  1565.    int n;
  1566.  
  1567.    static int ignore_signals = 0, reverse_video = 0, look_only = 0;
  1568.    static int search_data = 0;
  1569.    static char *logname = NULL;
  1570.    static int wait_for_child = -1;
  1571.  
  1572.    static Option desc[] = {
  1573.       O_flg ('d', search_data),
  1574.       O_flg ('i', ignore_signals),
  1575.       O_flg ('s', report_signals),
  1576.       O_flg ('r', reverse_video),
  1577.       O_flg ('l', look_only),
  1578.       O_str ('o', logname),
  1579.       O_str ('T', term),
  1580.       O_int ('w', wait_for_child),
  1581.       O_directive ("remaining: 1-infinity"),
  1582.       O_directive
  1583.        ("usage: [-isrld] [-T term] [-o logfile] [-w time] command [args ...]"),
  1584.       O_end,
  1585.    };
  1586.  
  1587.    n = O_parse_options (desc, argc, argv);
  1588.  
  1589.    command = argv + n;
  1590.  
  1591.    file = command[0];
  1592.  
  1593. /*
  1594.   Set up the log file.
  1595. */
  1596.  
  1597.    if (logname != NULL)
  1598.    {
  1599.       log = fopen (logname, "w");
  1600.       if (log == NULL)
  1601.       {
  1602.      fprintf (stderr,
  1603.           "%s: couldn't open %s for writing, using stderr\n",
  1604.           O_programname, logname);
  1605.      log = stderr;
  1606.       }
  1607.    }
  1608.    else
  1609.       log = stderr;
  1610.  
  1611. /*
  1612.   Set up reverse video.
  1613. */
  1614.  
  1615.    if (reverse_video)
  1616.       setup_standout_strings (&rev_on, &rev_off);
  1617.  
  1618. /*
  1619.   If we are going to run the application, it has to be writable, otherwise
  1620.   no breakpoint can be set. So make a temporary copy if the application
  1621.   isn't writable.
  1622. */
  1623.  
  1624.    if (!look_only)
  1625.    {
  1626.       warn_if_setuid (file);
  1627.  
  1628.       s = open (file, O_WRONLY, 0); /* Try to open for writing. */
  1629.       if (s == -1)        /* Couldn't, so make a writable copy. */
  1630.       {
  1631.      char buf[1024];
  1632.  
  1633.      tempfile = tempfile_name ();
  1634.      sprintf (buf, "cp %s %s && chmod u+w %s", file, tempfile, tempfile);
  1635.      s = system (buf);
  1636.      if (s != 0)
  1637.         fatal ("couldn't make a temporary copy", 0);
  1638.      file = tempfile;
  1639.       }
  1640.       else
  1641.      (void) close (s);
  1642.    }
  1643.  
  1644.    f = fopen (file, "r");
  1645.    Check (f != 0, "couldn't open the program file");
  1646.  
  1647. /*
  1648.   Get pointers to where the code starts in the file, and in core.
  1649.   Set the file pointer so that we start reading the first code
  1650.   instruction from the file.
  1651. */
  1652.  
  1653.    get_offsets (f, &f_offset, &c_offset, &c_size);
  1654.    s = fseek (f, (long) f_offset, 0);
  1655.    Check (s != -1, "couldn't fseek the program file");
  1656.  
  1657. /*
  1658.   Initialize the array of system calls.
  1659. */
  1660.  
  1661.    for (i = 0; i < SYS_n; i++)
  1662.    {
  1663.       calls[i].type = Absent;
  1664.       calls[i].nr = i;
  1665.    }
  1666.  
  1667. /*
  1668.   Read through the executable file, with the core address 'bytes' starting
  1669.   at the address where the code will be loaded when run.
  1670. */
  1671.  
  1672.    w1 = 0;
  1673.    bytes = c_offset;
  1674.  
  1675.    for (;;)
  1676.    {
  1677.       if (!search_data && bytes > c_offset + c_size)
  1678.      break;
  1679.  
  1680.       bytes += 2;
  1681.  
  1682.       w2 = get_chunk (f);
  1683.       if (feof (f))
  1684.      break;
  1685.  
  1686.       trapno = -1;
  1687.  
  1688. #ifdef hp9000s300
  1689.       if (((w1 & ~0xff) == 0x7000 || (w1 & ~0xff) == 0x0)
  1690.       &&
  1691.       w2 == 0x4e40)
  1692.       {
  1693.      trapno = (w1 & 0xff);
  1694.      address = bytes - 2;
  1695.       }
  1696. #endif
  1697. #ifdef hp9000s800
  1698.       if (w1 == 0xe420 && w2 == 0xe008)
  1699.       {
  1700.      bytes += 2;
  1701.      w1 = get_chunk (f);
  1702.      bytes += 2;
  1703.      w2 = get_chunk (f);
  1704.  
  1705.      if (w1 == 0x3416)
  1706.      {
  1707.         trapno = (w2 >> 1);
  1708.         address = bytes - 8;
  1709.      }
  1710.       }
  1711. #endif
  1712. #ifdef BSD
  1713.       if ((w2 & 0xff) == 0xbc)
  1714.       {
  1715.      unsigned int saved_w1 = w1;
  1716.  
  1717.      trapno = (w2 >> 8) & 0xff;
  1718.      address = bytes - 2;
  1719.      if (trapno == 0x8f)
  1720.      {
  1721.         bytes += 2;
  1722.         w2 = get_chunk (f);
  1723.         trapno = w2;
  1724.      }
  1725.      else if (trapno == 0 || trapno > 0x3f)
  1726.         trapno = -1;
  1727.  
  1728.      bytes += 2;
  1729.      w2 = get_chunk (f);
  1730.      if (trapno != -1
  1731.          && (w2 & 0xff) != 0x1f /* all except exit, getppid and geteuid */
  1732.          && (w2 & 0xff) != 0x1e /* vfork and wait3 */
  1733.          && (saved_w1 != 0)) /* all except brk, sbrk, ptrace */
  1734.        trapno = -2;
  1735.       }
  1736.                 /* Instructions may start on odd addresses */
  1737.                 /* on a Vax. */
  1738.       if (trapno == -1)
  1739.       {
  1740.      unsigned int saved_w1 = w1;
  1741.  
  1742.      if ((w1 & 0xff00) == 0xbc00)
  1743.      {
  1744.         trapno = (w2 & 0xff);
  1745.         address = bytes - 3;
  1746.         if (trapno == 0x8f)
  1747.         {
  1748.            w1 = w2;
  1749.            bytes += 2;
  1750.            w2 = get_chunk (f);
  1751.            trapno = ((w2 & 0xff) << 8) + ((w1 >> 8) & 0xff);
  1752.         }
  1753.         else if (trapno == 0 || trapno > 0x3f)
  1754.            trapno = -1;
  1755.  
  1756.         if ((saved_w1 & 0xff) != 0
  1757.         && (w2 & 0xff00) != 0x1e00
  1758.         && (w2 & 0xff00) != 0x1f00)
  1759.           trapno = -1;
  1760.      }
  1761.       }
  1762. #endif
  1763.       if (trapno > 0 && trapno < SYS_n)
  1764.       {
  1765.      if (calls[trapno].type == Present && !look_only
  1766.         && (i == SYS_OPEN
  1767.         || i == SYS_CLOSE
  1768.         || i == SYS_FORK
  1769.         || i == SYS_VFORK))
  1770.      {
  1771.         fprintf (stderr, "%s: warning, multiple occurrences of trap %#x\n",
  1772.              O_programname, trapno);
  1773.      }
  1774.      calls[trapno].address = address;
  1775.      calls[trapno].type = Present;
  1776.      if (look_only)
  1777.         printf ("system call %#x at %#x\n", trapno, address);
  1778.       }
  1779.       w1 = w2;
  1780.    }
  1781.    fclose (f);
  1782.  
  1783.    if (look_only)        /* If only listing system calls, we are */
  1784.       exit (0);            /* done now. */
  1785.  
  1786.    fflush (stdout);        /* Cause child's stdio buffers to start */
  1787.    fflush (stdout);        /* out clean, in case the child wants to */
  1788.    fflush (stderr);        /* report an error. */
  1789.  
  1790.    if (ignore_signals)        /* Ignore signals, if the user told us to. */
  1791.    {
  1792.       (void) signal (SIGHUP, SIG_IGN);
  1793.       (void) signal (SIGINT, SIG_IGN);
  1794.       (void) signal (SIGQUIT, SIG_IGN);
  1795.    }
  1796.  
  1797.    pid = fork ();        /* vfork won't do. Bug in hpux. */
  1798.    Check (pid != -1, "couldn't fork");
  1799.  
  1800.    if (pid == 0)
  1801.    {
  1802.       (void) ptrace (PT_SETTRC, 0, 0, 0); /* Shouldn't fail */
  1803.       do_exec (log, file, command);
  1804.       /* NOTREACHED */
  1805.    }
  1806.  
  1807.    (void) do_wait (pid, SIGTRAP);
  1808.                 /* Wait for the 'exec' to happen, */
  1809.    for (i = 0; i < SYS_n; i++)    /* then start putting in our breakpoints. */
  1810.    {
  1811.       if (i != SYS_OPEN
  1812.       && i != SYS_CLOSE
  1813.       && i != SYS_FORK
  1814.       && i != SYS_VFORK)
  1815.      continue;
  1816.  
  1817.       if (calls[i].type == Present)
  1818.       {
  1819.      s = ptrace (PT_RIUSER, pid, calls[i].address, 0);
  1820.      Check (s != -1, "couldn't read trap word");
  1821.      calls[i].old_word = s;
  1822.  
  1823. #ifdef BSD
  1824.      calls[i].new_word = (s & 0xffff0000) | 0x803;
  1825. #endif
  1826. #ifdef hp9000s300
  1827.      calls[i].new_word = 0x4e410000 + (s & 0xffff);
  1828. #endif
  1829. #ifdef hp9000s800
  1830.      calls[i].new_word = 0x10004;
  1831. #endif
  1832.  
  1833.      calls[i].type = Bpt;
  1834.  
  1835.      s = ptrace (PT_WIUSER, pid, calls[i].address, calls[i].new_word);
  1836.      Check (s != -1, "couldn't write new trap word");
  1837.       }
  1838.    }
  1839.  
  1840. /*
  1841.   The rest of the 'main' function is within this loop. The basic flow
  1842.   of control is:
  1843.  
  1844.   1) Subprocess is in the stopped state.
  1845.   2) Continue it, passing along any pending signal that occurred during the
  1846.   last loop.
  1847.   3) Wait until it hits another breakpoint (which will cause a SIGTRAP signal
  1848.   and stop the subprocess).
  1849.   4) Find what system call is at the location of the breakpoint.
  1850.   5) If none (spurious signal), just go back to step 1.
  1851.   6) Find the arguments to the system call.
  1852.   7) Put back the real system call.
  1853.   8) Single-step the system call.
  1854.   9) Get the result of the system call (including error status).
  1855.   10) Report the whole system call.
  1856.   11) Put back the breakpoint and come back to step 1.
  1857. */
  1858.  
  1859.    for (;;)
  1860.    {
  1861. /* 1 */
  1862.       report_signal_and_continue (pid, pick_first_signal (&sigm));
  1863. /* 2 */
  1864.       (void) do_wait (pid, SIGTRAP);
  1865.  
  1866. /* 3 */
  1867. #ifdef hp9000s800
  1868.       pc = ptrace (PT_RUREGS, pid, s_offset (ss_pcoq_head), 0);
  1869.       pc &= ~3;            /* remove privilege level */
  1870. #else
  1871.       reg_base = get_reg_base (pid);
  1872.       pc = ptrace (PT_RUAREA, pid, reg_base + 4*PC, 0);
  1873. # ifdef hp9000s300
  1874.       pc -= 2;
  1875. # endif
  1876. #endif
  1877.  
  1878. /* 4 */
  1879.       p = find_system_call (pc);
  1880.  
  1881. /* 5 */
  1882.       if (p == 0)
  1883.      continue;        /* Spurious SIGTRAP, just ignore it. */
  1884.  
  1885. /* 6 */
  1886. #ifdef hp9000s800
  1887.       arg1 = ptrace (PT_RUREGS, pid, s_offset (ss_arg0), 0);
  1888.       arg2 = ptrace (PT_RUREGS, pid, s_offset (ss_arg1), 0);
  1889.       arg3 = ptrace (PT_RUREGS, pid, s_offset (ss_arg2), 0);
  1890. #else
  1891.       reg_base = get_reg_base (pid); /* unnecessary, really, since it was */
  1892.                      /* done above. */
  1893.       sp = ptrace (PT_RUAREA, pid, reg_base + 4*SP, 0);
  1894. # ifdef BSD
  1895.       sp += 4*5;        /* adjustment for junk on stack */
  1896. # endif
  1897.       arg1 = ptrace (PT_RDUSER, pid, sp+4, 0);
  1898.       arg2 = ptrace (PT_RDUSER, pid, sp+8, 0);
  1899.       arg3 = ptrace (PT_RDUSER, pid, sp+12, 0);
  1900. #endif
  1901.  
  1902.       if (p->nr == SYS_OPEN)
  1903.       {
  1904.      static char buf[1024];
  1905.      int adjusted_arg1 = arg1 & ~3;
  1906.      int offset = (arg1 & 3);
  1907. #ifdef BSD
  1908.      int mask = (0x01010101 >> (32 - 8 * offset));
  1909. #else
  1910.      int mask = (0x01010101 >> (8 * offset)) ^ 0x01010101;
  1911. #endif
  1912.  
  1913.      ip = (int *) buf;
  1914.      for (i = 0; i < 100; i++)
  1915.      {
  1916.         val = ptrace (PT_RDUSER, pid, adjusted_arg1, 0);
  1917.         Check (val != -1, "couldn't read string from subprocess");
  1918.  
  1919. /*
  1920.   Not all systems allow address arguments to 'ptrace' to be odd, so we
  1921.   do this to get things right. The Vax does allow it, but it's easier to
  1922.   special-case 'mask' than the whole loop.
  1923. */
  1924.  
  1925.         if (i == 0)
  1926.            val |= mask;
  1927.         *ip++ = val;
  1928.         if (((val & 0xff000000) == 0)
  1929.         | ((val & 0x00ff0000) == 0)
  1930.         | ((val & 0x0000ff00) == 0)
  1931.         | ((val & 0x000000ff) == 0))
  1932.            break;
  1933.         adjusted_arg1 += 4;
  1934.      }
  1935.      *ip = 0;
  1936.      string_arg = buf + offset;
  1937.       }
  1938.  
  1939. /*
  1940.   Put back the true system call in order to run it.
  1941.  
  1942.   If the call is a kind of fork, we have to remove all the breakpoints;
  1943.   otherwise the child process would die on SIGTRAP as soon as it tried
  1944.   to use one of the system calls that we have put breakpoints in.
  1945. */
  1946.  
  1947. /* 7 */
  1948.       if (p->nr == SYS_FORK || p->nr == SYS_VFORK)
  1949.       {
  1950.      for (i = 0; i < SYS_n; i++)
  1951.         if (calls[i].type == Bpt)
  1952.         {
  1953.            s = ptrace (PT_WIUSER,
  1954.                pid, calls[i].address, calls[i].old_word);
  1955.            Check (s != -1, "couldn't write back old trap words");
  1956.         }
  1957.       }
  1958.       else
  1959.       {
  1960.      s = ptrace (PT_WIUSER, pid, p->address, p->old_word);
  1961.      Check (s != -1, "couldn't write back old trap word");
  1962.       }
  1963.  
  1964. /*
  1965.   Now run the system call by single-stepping it.
  1966.  
  1967.   Some systems lose when the 'fork' or 'vfork' system call is
  1968.   single-stepped over; the child immediately gets a SIGTRAP. The only
  1969.   solution to this problem is to continue the process instead of
  1970.   single-stepping it, then stopping it as soon as possible with a
  1971.   'kill' call. After this, we jump down to the label 'put_back',
  1972.   and put back all our breakpoints. We may miss some system calls that
  1973.   we should have reported in this way, but that can't be helped.
  1974. */
  1975.  
  1976. /* 8 */
  1977. #if defined(hp9000s800) | defined(BSD)
  1978.  
  1979. /*
  1980.   To avoid anomalies (such as reporting the fork call after the child
  1981.   has printed things on our terminal), we do the reporting here already.
  1982.   Unfortunately, we can't report the pid of the child process.
  1983. */
  1984.  
  1985.       if (p->nr == SYS_FORK || p->nr == SYS_VFORK)
  1986.       {
  1987.      with_reverse_video
  1988.         ({
  1989.            fprintf (log, "%sfork()\n", p->nr == SYS_FORK ? "" : "v");
  1990.         });
  1991.  
  1992. /*
  1993.   There is a slight chance here that a signal is stopping the process just
  1994.   before the system call is done. In that case, the result won't show up
  1995.   correctly, and in the case of fork, the child process loses. The only
  1996.   way to detect that is to look at the pc. We don't do that yet.
  1997. */
  1998.  
  1999.      s = ptrace (PT_CONTIN, pid, pc, 0);
  2000.      kill (pid, SIGTRAP);
  2001.      sig = do_wait (pid, -1);
  2002.      goto put_back;
  2003.       }
  2004. #endif
  2005.  
  2006. /*
  2007.   On some systems, the correct data don't show up in the registers until
  2008.   we have single-stepped more than once.
  2009. */
  2010.  
  2011. #ifdef hp9000s800
  2012. # define EXTRA_STEPS    2    /* Once for the instruction after the jump */
  2013.                 /* (yes, this is a RISC), once for the */
  2014.                 /* actual trap somewhere in the protected */
  2015.                 /* kernel code. */
  2016. #endif
  2017. #ifdef hp9000s300
  2018. # define EXTRA_STEPS    1    /* Scheduling anomaly, perhaps? */
  2019. #endif
  2020. #ifdef BSD
  2021. # define EXTRA_STEPS    0    /* No problem. */
  2022. #endif
  2023.  
  2024.       for (i = 0; i < 1 + EXTRA_STEPS;)
  2025.       {
  2026.      s = ptrace (PT_SINGLE, pid, pc, 0);
  2027.      Check (s != -1, "couldn't singlestep 1");
  2028.      pc = 1;
  2029.      sig = do_wait (pid, -1);
  2030.      if (sig == 0)
  2031.         i += 1;
  2032.       }
  2033.  
  2034. /*
  2035.   Pick up the return value of the system call from the right register, and
  2036.   the flag word that says whether the call succeeded. This flag word will
  2037.   be decoded by the function 'print_result'.
  2038. */
  2039.  
  2040. /* 9 */
  2041. #ifdef hp9000s800
  2042.       flags = ptrace (PT_RUREGS, pid, s_offset (ss_gr22), 0);
  2043.       res = ptrace (PT_RUREGS, pid, s_offset (ss_ret0), 0);
  2044. #else
  2045.       reg_base = get_reg_base (pid);
  2046.       flags = ptrace (PT_RUAREA, pid, reg_base + 4*PS, 0);
  2047.       res = ptrace (PT_RUAREA, pid, reg_base + 4*R0, 0);
  2048. #endif
  2049.  
  2050. /*
  2051.   Now the system call is complete; report it along with its arguments,
  2052.   and whether it succeeded or failed.
  2053. */
  2054.  
  2055. /* 10 */
  2056.       with_reverse_video
  2057.      ({
  2058.         if (p->nr == SYS_OPEN)
  2059.         {
  2060.            if ((arg2 & O_CREAT) != 0)
  2061.           fprintf (log, "open(\"%s\", %#x, %#o)",
  2062.                string_arg, arg2, arg3);
  2063.            else
  2064.           fprintf (log, "open(\"%s\", %#x)", string_arg, arg2);
  2065.            print_result (log, res, flags, 1);
  2066.         }
  2067.         else if (p->nr == SYS_CLOSE)
  2068.         {
  2069.            fprintf (log, "close(%d)", arg1);
  2070.            print_result (log, res, flags, 0);
  2071.         }
  2072.         else if (p->nr == SYS_FORK || p->nr == SYS_VFORK)
  2073.         {
  2074.            fprintf (log, "%sfork()", p->nr == SYS_FORK ? "" : "v");
  2075.            print_result (log, res, flags, 1);
  2076.         }
  2077.      });
  2078.  
  2079.     put_back:
  2080.  
  2081. /*
  2082.   If we previously removed all our breakpoints before single-stepping a
  2083.   fork or vfork, now put them back again.
  2084.  
  2085.   If the child hasn't had time to exec or exit, there are now two processes
  2086.   sharing the same code area, and the system consequently prohibits us from
  2087.   writing into it. Wait either until we can write, or until the user-supplied
  2088.   timeout expires.
  2089.  
  2090.   If not fork or vfork, just put back the one system call that was called.
  2091. */
  2092.  
  2093. /* 11 */
  2094.       if (p->nr == SYS_FORK || p->nr == SYS_VFORK)
  2095.       {
  2096.      int waiting = 0;
  2097.  
  2098.        retry:
  2099.      for (i = 0; i < SYS_n; i++)
  2100.         if (calls[i].type == Bpt)
  2101.         {
  2102.            s = ptrace (PT_WIUSER,
  2103.                pid, calls[i].address, calls[i].new_word);
  2104.            if (s == -1)
  2105.            {
  2106.           if (wait_for_child == waiting)
  2107.           {
  2108.              with_reverse_video
  2109.             ({
  2110.                fprintf (log,
  2111.                    "No more system call reports will be made\n");
  2112.             });
  2113.              break;
  2114.           }
  2115.  
  2116.           if (waiting == 0)
  2117.              with_reverse_video
  2118.             ({
  2119.                fprintf (log,
  2120.                   "Waiting for child's child to exit or exec\n");
  2121.             });
  2122.  
  2123.           sleep (1);
  2124.           waiting += 1;
  2125.           goto retry;
  2126.            }
  2127.         }
  2128.       }
  2129.       else
  2130.       {
  2131.      s = ptrace (PT_WIUSER, pid, p->address, p->new_word);
  2132.      Check (s != -1, "couldn't write new trap word");
  2133.       }
  2134.    }
  2135. }
  2136. @EOF
  2137.  
  2138. chmod 664 trapfile.c
  2139.  
  2140. exit 0
  2141. -- 
  2142. Arndt Jonasson, ZYX Sweden AB, Styrmansgatan 6, 114 54 Stockholm, Sweden
  2143. email address:     aj@zyx.SE    or    <backbone>!mcvax!enea!zyx!aj
  2144.  
  2145.  
  2146.