home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume2 / trapfile < prev    next >
Internet Message Format  |  1991-08-07  |  49KB

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