1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 #include <stdio.h>
  27 #include <stdlib.h>
  28 #include <unistd.h>
  29 #include <sys/uio.h>
  30 #include <fcntl.h>
  31 #include <string.h>
  32 #include <errno.h>
  33 #include <sys/types.h>
  34 #include <sys/signal.h>
  35 #include <sys/fault.h>
  36 #include <sys/syscall.h>
  37 #include <procfs.h>
  38 #include <sys/auxv.h>
  39 #include <libelf.h>
  40 #include <sys/param.h>
  41 #include <sys/machelf.h>
  42 #include <stdarg.h>
  43 
  44 #include <proc_service.h>
  45 
  46 #include "rdb.h"
  47 #include "disasm.h"
  48 #include "gram.h"
  49 
  50 #define PROCSIZE        20
  51 
  52 static void
  53 init_proc()
  54 {
  55         int             pfd;
  56         char            procname[PROCSIZE];
  57         sigset_t        sigset;
  58         fltset_t        fltset;
  59         sysset_t        sysset;
  60         long            oper, pflags;
  61         struct iovec    piov[2];
  62 
  63         /*
  64          * open our own /proc file and set tracing flags
  65          */
  66         (void) snprintf(procname, PROCSIZE, "/proc/%d/ctl", EC_SWORD(getpid()));
  67         if ((pfd = open(procname, O_WRONLY)) < 0) {
  68                 (void) fprintf(stderr, "can't open %s\n", procname);
  69                 exit(1);
  70         }
  71 
  72         /*
  73          * inherit on fork, and kill-on-last-close
  74          */
  75         oper = PCSET;
  76         piov[0].iov_base = (caddr_t)(&oper);
  77         piov[0].iov_len = sizeof (oper);
  78         pflags = PR_FORK;
  79         piov[1].iov_base = (caddr_t)&pflags;
  80         piov[1].iov_len = sizeof (pflags);
  81 
  82         if (writev(pfd, piov, 2) == -1)
  83                 perr("init_proc: PCSET");
  84 
  85         /*
  86          * no signal tracing
  87          */
  88         oper = PCSTRACE;
  89         premptyset(&sigset);
  90         piov[1].iov_base = (caddr_t)&sigset;
  91         piov[1].iov_len = sizeof (sigset);
  92         if (writev(pfd, piov, 2) == -1)
  93                 perr("PCSTRACE");
  94 
  95         /*
  96          * no fault tracing
  97          */
  98         oper = PCSFAULT;
  99         premptyset(&fltset);
 100         piov[1].iov_base = (caddr_t)&fltset;
 101         piov[1].iov_len = sizeof (fltset);
 102         if (writev(pfd, piov, 2) == -1)
 103                 perr("PCSFAULT");
 104 
 105         /*
 106          * no syscall tracing
 107          */
 108         oper = PCSENTRY;
 109         premptyset(&sysset);
 110         piov[1].iov_base = (caddr_t)&sysset;
 111         piov[1].iov_len = sizeof (sysset);
 112         if (writev(pfd, piov, 2) == -1)
 113                 perr("PSENTRY");
 114 
 115         /*
 116          * except exit from exec() or execve()
 117          */
 118         oper = PCSEXIT;
 119         premptyset(&sysset);
 120         praddset(&sysset, SYS_execve);
 121         if (writev(pfd, piov, 2) == -1)
 122                 perr("PCSEXIT");
 123 
 124         (void) close(pfd);
 125 }
 126 
 127 int
 128 main(int argc, char *argv[])
 129 {
 130         int                     pctlfd;
 131         int                     pstatusfd;
 132         char                    procname[PROCSIZE];
 133         char                    *command;
 134         char                    *rdb_commands = NULL;
 135         pid_t                   cpid;
 136         pstatus_t               pstatus;
 137         sysset_t                sysset;
 138         int                     c;
 139         int                     error = 0;
 140         long                    oper;
 141         struct iovec            piov[2];
 142         extern FILE             *yyin;
 143 
 144         command = argv[0];
 145 
 146         while ((c = getopt(argc, argv, "f:")) != EOF)
 147                 switch (c) {
 148                 case 'f':
 149                         rdb_commands = optarg;
 150                         break;
 151                 case '?':
 152                         break;
 153                 }
 154 
 155         if (error || (optind == argc)) {
 156                 (void) printf("usage: %s [-f file] executable "
 157                     "[executable arguments ...]\n", command);
 158                 (void) printf("\t-f     command file\n");
 159                 exit(1);
 160         }
 161 
 162         /*
 163          * set up for tracing the child.
 164          */
 165         init_proc();
 166 
 167         /*
 168          * create a child to fork and exec from.
 169          */
 170         if ((cpid = fork()) == 0) {
 171                 (void) execv(argv[optind], &argv[optind]);
 172                 perr(argv[optind]);
 173         }
 174 
 175         if (cpid == -1) /* fork() failure */
 176                 perr(command);
 177 
 178         /*
 179          * initialize libelf
 180          */
 181         if (elf_version(EV_CURRENT) == EV_NONE) {
 182                 (void) fprintf(stderr, "elf_version() failed: %s\n",
 183                     elf_errmsg(0));
 184                 exit(1);
 185         }
 186 
 187         /*
 188          * initialize librtld_db
 189          */
 190         if (rd_init(RD_VERSION) != RD_OK) {
 191                 (void) fprintf(stderr, "librtld_db::rd_init() failed: version "
 192                     "submitted: %d\n", RD_VERSION);
 193                 exit(1);
 194         }
 195 
 196         /* rd_log(1); */
 197 
 198         /*
 199          * Child should now be waiting after the successful
 200          * exec.
 201          */
 202         (void) snprintf(procname, PROCSIZE, "/proc/%d/ctl", EC_SWORD(cpid));
 203         (void) printf("parent: %d child: %d child procname: %s\n",
 204             EC_SWORD(getpid()), EC_SWORD(cpid), procname);
 205         if ((pctlfd = open(procname, O_WRONLY)) < 0) {
 206                 perror(procname);
 207                 (void) fprintf(stderr, "%s: can't open child %s\n",
 208                     command, procname);
 209                 exit(1);
 210         }
 211 
 212         /*
 213          * wait for child process.
 214          */
 215         oper = PCWSTOP;
 216         piov[0].iov_base = (caddr_t)&oper;
 217         piov[0].iov_len = sizeof (oper);
 218         if (writev(pctlfd, piov, 1) == -1)
 219                 perr("PCWSTOP");
 220 
 221         /*
 222          * open /proc/<cpid>/status
 223          */
 224         (void) snprintf(procname, PROCSIZE, "/proc/%d/status", EC_SWORD(cpid));
 225         if ((pstatusfd = open(procname, O_RDONLY)) == -1)
 226                 perr(procname);
 227 
 228         if (read(pstatusfd, &pstatus, sizeof (pstatus)) == -1)
 229                 perr("status read failed");
 230 
 231         /*
 232          * Make sure that it stopped where we expected.
 233          */
 234         while ((pstatus.pr_lwp.pr_why == PR_SYSEXIT) &&
 235             (pstatus.pr_lwp.pr_what == SYS_execve)) {
 236                 long    pflags = 0;
 237                 if (!(pstatus.pr_lwp.pr_reg[R_PS] & ERRBIT)) {
 238                         /* successfull exec(2) */
 239                         break;
 240                 }
 241 
 242                 oper = PCRUN;
 243                 piov[1].iov_base = (caddr_t)&pflags;
 244                 piov[1].iov_len = sizeof (pflags);
 245                 if (writev(pctlfd, piov, 2) == -1)
 246                         perr("PCRUN1");
 247 
 248                 oper = PCWSTOP;
 249                 if (writev(pctlfd, piov, 1) == -1)
 250                         perr("PCWSTOP");
 251 
 252                 if (read(pstatusfd, &pstatus, sizeof (pstatus)) == -1)
 253                         perr("status read failed");
 254         }
 255 
 256         premptyset(&sysset);
 257         oper = PCSEXIT;
 258         piov[1].iov_base = (caddr_t)&sysset;
 259         piov[1].iov_len = sizeof (sysset);
 260         if (writev(pctlfd, piov, 2) == -1)
 261                 perr("PIOCSEXIT");
 262 
 263         /*
 264          * Did we stop where we expected ?
 265          */
 266         if ((pstatus.pr_lwp.pr_why != PR_SYSEXIT) ||
 267             (pstatus.pr_lwp.pr_what != SYS_execve)) {
 268                 long    pflags = 0;
 269 
 270                 (void) fprintf(stderr, "Didn't catch the exec, why: %d "
 271                     "what: %d\n", pstatus.pr_lwp.pr_why,
 272                     pstatus.pr_lwp.pr_what);
 273 
 274                 oper = PCRUN;
 275                 piov[1].iov_base = (caddr_t)&pflags;
 276                 piov[1].iov_len = sizeof (pflags);
 277                 if (writev(pctlfd, piov, 2) == -1)
 278                         perr("PCRUN2");
 279                 exit(1);
 280         }
 281 
 282         (void) ps_init(pctlfd, pstatusfd, cpid, &proch);
 283 
 284         if (rdb_commands) {
 285                 if ((yyin = fopen(rdb_commands, "r")) == NULL) {
 286                         (void) printf("unable to open %s for input\n",
 287                             rdb_commands);
 288                         perr("fopen");
 289                 }
 290         } else {
 291                 proch.pp_flags |= FLG_PP_PROMPT;
 292                 rdb_prompt();
 293         }
 294         (void) yyparse();
 295 
 296         if (proch.pp_flags & FLG_PP_PACT) {
 297                 long    pflags = PRCFAULT;
 298 
 299                 (void) printf("\ncontinuing the hung process...\n");
 300 
 301                 pctlfd = proch.pp_ctlfd;
 302                 (void) ps_close(&proch);
 303 
 304                 oper = PCRUN;
 305                 piov[1].iov_base = (caddr_t)&pflags;
 306                 piov[1].iov_len = sizeof (pflags);
 307                 if (writev(pctlfd, piov, 2) == -1)
 308                         perr("PCRUN2");
 309                 (void) close(pctlfd);
 310         }
 311 
 312         return (0);
 313 }