| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License, Version 1.0 only |
| * (the "License"). You may not use this file except in compliance |
| * with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright (c) 1994-1999 by Sun Microsystems, Inc. |
| * All rights reserved. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| /* |
| * This file contains interfaces that are wrappers over the basic |
| * /proc ioctls |
| */ |
| |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <sys/uio.h> |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/fault.h> |
| #include <sys/procfs.h> |
| |
| #include "prb_proc_int.h" |
| #include "dbg.h" |
| |
| /* |
| * Declarations |
| */ |
| |
| #define PROCFORMAT "/proc/%d" |
| |
| static prb_status_t |
| prb_proc_open_general(pid_t pid, prb_proc_ctl_t **proc_pp, int oflg); |
| |
| /* |
| * prb_proc_open_general() - function to open the process file |
| * system entry for the supplied process. Opens with different |
| * options based on the 'oflg'. |
| * Returns a pointer to an opaque structure that contains the fd |
| * needed for /proc control. |
| */ |
| |
| prb_status_t |
| prb_proc_open_general(pid_t pid, prb_proc_ctl_t **proc_pp, int oflg) |
| { |
| prb_proc_ctl_t *proc_p; |
| char path[MAXPATHLEN]; |
| int retval; |
| |
| (void) sprintf(path, PROCFORMAT, (int)pid); |
| |
| DBG_TNF_PROBE_1(prb_proc_open_1, "libtnfctl", "sunw%verbosity 2", |
| tnf_string, opening_proc_on, path); |
| |
| retval = open(path, oflg); |
| if (retval == -1) { |
| DBG((void) fprintf(stderr, |
| "proc_open: open of \"%s\" failed: %s\n", |
| path, strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| /* allocate proc_p and zero fill */ |
| proc_p = calloc(1, sizeof (*proc_p)); |
| if (proc_p == NULL) |
| return (PRB_STATUS_ALLOCFAIL); |
| proc_p->procfd = retval; |
| proc_p->pid = pid; |
| *proc_pp = proc_p; |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_open() - a wrapper which opens the process file system |
| * entry for the supplied process. Returns a pointer to an opaque |
| * structure that contains the fd needed for /proc control. |
| */ |
| |
| prb_status_t |
| prb_proc_open(pid_t pid, prb_proc_ctl_t **proc_pp) |
| { |
| |
| return (prb_proc_open_general(pid, |
| proc_pp, O_RDWR | O_EXCL)); |
| |
| } |
| |
| /* |
| * prb_proc_reopen() - re-opens the process, mainly for setuid/setgid files. |
| * Read the last section of /proc man page for details. |
| * re-open should not use O_EXCL flag. |
| */ |
| |
| prb_status_t |
| prb_proc_reopen(pid_t pid, prb_proc_ctl_t **proc_pp) |
| { |
| |
| return (prb_proc_open_general(pid, |
| proc_pp, O_RDWR)); |
| |
| } |
| |
| /* |
| * prob_proc_close() - close the proc fd and free the memory taken up |
| * by proc_p |
| */ |
| prb_status_t |
| prb_proc_close(prb_proc_ctl_t *proc_p) |
| { |
| DBG_TNF_PROBE_0(prb_proc_close_1, "libtnfctl", "sunw%verbosity 2"); |
| |
| if (proc_p == NULL) |
| return (PRB_STATUS_OK); |
| |
| if (close(proc_p->procfd) == -1) { |
| DBG((void) fprintf(stderr, |
| "proc_close: close failed: %s\n", strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| free(proc_p); |
| return (PRB_STATUS_OK); |
| } |
| |
| /* |
| * prb_proc_pid_get() - gets the pid of the proc |
| */ |
| pid_t |
| prb_proc_pid_get(prb_proc_ctl_t *proc_p) |
| { |
| return (proc_p->pid); |
| } |
| |
| /* |
| * prb_proc_stop() - stops the target process |
| */ |
| prb_status_t |
| prb_proc_stop(prb_proc_ctl_t *proc_p) |
| { |
| int retval; |
| |
| DBG_TNF_PROBE_0(prb_proc_stop_1, "libtnfctl", |
| "sunw%verbosity 2; sunw%debug 'stopping the target process'"); |
| |
| again: |
| retval = ioctl(proc_p->procfd, PIOCSTOP, NULL); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again; |
| DBG((void) fprintf(stderr, |
| "prb_proc_stop: PIOCSTOP failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_prstop() - runs and stops the process, used to clear a target |
| * process out of a system call state. |
| */ |
| prb_status_t |
| prb_proc_prstop(prb_proc_ctl_t *proc_p) |
| { |
| int procfd; |
| int retval; |
| prrun_t prrun; |
| prstatus_t prstat; |
| |
| DBG_TNF_PROBE_0(prb_proc_prstop_1, "libtnfctl", |
| "sunw%verbosity 2; sunw%debug 'stepping the target process'"); |
| |
| procfd = proc_p->procfd; |
| (void) memset((char *)&prrun, 0, sizeof (prrun)); |
| (void) memset((char *)&prstat, 0, sizeof (prstat)); |
| |
| again1: |
| prrun.pr_flags = PRSTOP; |
| retval = ioctl(procfd, PIOCRUN, &prrun); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again1; |
| DBG((void) fprintf(stderr, |
| "prb_proc_prstop: PIOCRUN failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| again2: |
| retval = ioctl(procfd, PIOCWSTOP, &prstat); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again2; |
| DBG((void) fprintf(stderr, |
| "prb_proc_prstop: PIOCWSTOP failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| /* |
| * if we didn't stop because we requested it (eg. if there was a |
| * signal in the target ?), we might need to try again |
| */ |
| if (prstat.pr_why != PR_REQUESTED) |
| goto again1; |
| |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_state() - returns the status pf the process |
| */ |
| prb_status_t |
| prb_proc_state(prb_proc_ctl_t *proc_p, prb_proc_state_t *state_p) |
| { |
| int procfd; |
| int retval; |
| prstatus_t prstatus; |
| |
| DBG_TNF_PROBE_0(prb_proc_state_1, "libtnfctl", |
| "sunw%verbosity 2; sunw%debug 'getting the status'"); |
| |
| procfd = proc_p->procfd; |
| |
| (void) memset(&prstatus, 0, sizeof (prstatus)); |
| |
| again: |
| retval = ioctl(procfd, PIOCSTATUS, &prstatus); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again; |
| DBG((void) fprintf(stderr, |
| "prb_proc_status: PIOCSTATUS failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| state_p->ps_isbptfault = (prstatus.pr_flags & PR_FAULTED && |
| prstatus.pr_what == FLTBPT); |
| state_p->ps_isstopped = ((prstatus.pr_flags & PR_STOPPED) != 0); |
| state_p->ps_isinsys = ((prstatus.pr_flags & PR_ASLEEP) != 0); |
| state_p->ps_isrequested = ((prstatus.pr_why & PR_REQUESTED) != 0); |
| state_p->ps_issysexit = ((prstatus.pr_why & PR_SYSEXIT) != 0); |
| state_p->ps_issysentry = ((prstatus.pr_why & PR_SYSENTRY) != 0); |
| state_p->ps_syscallnum = prstatus.pr_what; |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_wait() - waits for the target process to stop |
| */ |
| prb_status_t |
| prb_proc_wait(prb_proc_ctl_t *proc_p, boolean_t use_sigmask, sigset_t *oldmask) |
| { |
| int procfd; |
| int retval; |
| prstatus_t prstat; |
| sigset_t pendmask; |
| int i, mask_size; |
| boolean_t pending_signal = B_FALSE; |
| |
| DBG_TNF_PROBE_0(prb_proc_wait_1, "libtnfctl", |
| "sunw%verbosity 2;" |
| "sunw%debug 'waiting for the target process to stop'"); |
| |
| procfd = proc_p->procfd; |
| |
| /* |
| * This one of the places where we do not resubmit the ioctl if |
| * if it is terminated by an EINTR (interrupted system call). In |
| * this case, the caller knows best ... |
| */ |
| (void) memset(&prstat, 0, sizeof (prstat)); |
| |
| /* if we blocked signals... */ |
| if (use_sigmask) { |
| if (sigemptyset(&pendmask) == -1) |
| return (prb_status_map(errno)); |
| if (sigpending(&pendmask) == -1) |
| return (prb_status_map(errno)); |
| /* |
| * check if there were any signals pending - |
| * XXXX libc should provide this interface |
| */ |
| mask_size = sizeof (pendmask) / sizeof (pendmask.__sigbits[0]); |
| for (i = 0; i < mask_size; i++) { |
| if (pendmask.__sigbits[i] != 0) |
| pending_signal = B_TRUE; |
| } |
| |
| /* return to original signal mask */ |
| if (sigprocmask(SIG_SETMASK, oldmask, NULL) == -1) |
| return (prb_status_map(errno)); |
| |
| /* if there was a pending signal, don't call PIOCWSTOP ioctl */ |
| if (pending_signal) |
| return (prb_status_map(EINTR)); |
| |
| /* |
| * XXXX - there is a a race between now and when we call |
| * the PIOCWSTOP ioctl. One solution, is for the user to |
| * call an interface in libtnfctl from their signal handler. |
| * This interface will do a longjmp such that it never |
| * calls the ioctl (the setjmp would be before we restore |
| * the signal mask above) |
| */ |
| } |
| |
| retval = ioctl(procfd, PIOCWSTOP, &prstat); |
| |
| DBG_TNF_PROBE_2(prb_proc_wait_2, "libtnfctl", "sunw%verbosity 2;", |
| tnf_opaque, pc, prstat.pr_reg[R_PC], |
| tnf_opaque, instr, prstat.pr_instr); |
| |
| if (retval == -1) { |
| #ifdef DEBUG |
| if (errno != EINTR && errno != ENOENT) |
| (void) fprintf(stderr, |
| "prb_proc_wait: PIOCWSTOP failed: %s\n", |
| strerror(errno)); |
| |
| #endif |
| return (prb_status_map(errno)); |
| } |
| |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_cont() - continues the target process |
| */ |
| prb_status_t |
| prb_proc_cont(prb_proc_ctl_t *proc_p) |
| { |
| int procfd; |
| int retval; |
| prrun_t prrun; |
| |
| DBG_TNF_PROBE_0(prb_proc_cont_1, "libtnfctl", |
| "sunw%verbosity 2; sunw%debug 'starting the target process'"); |
| procfd = proc_p->procfd; |
| |
| (void) memset((char *)&prrun, 0, sizeof (prrun)); |
| |
| again: |
| prrun.pr_flags = PRCFAULT; |
| retval = ioctl(procfd, PIOCRUN, &prrun); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again; |
| DBG((void) fprintf(stderr, |
| "prb_proc_cont: PIOCRUN failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_istepbpt() - step the target process one instruction |
| * |
| * CAUTION!!!! - this routine is specialized to only be able to single |
| * step over the breakpoint location. |
| */ |
| prb_status_t |
| prb_proc_istepbpt(prb_proc_ctl_t *proc_p) |
| { |
| int procfd; |
| int retval; |
| prrun_t run; |
| fltset_t faults; |
| prstatus_t prstat; |
| |
| DBG_TNF_PROBE_0(prb_proc_istepbpt_1, "libtnfctl", |
| "sunw%verbosity 2; " |
| "sunw%debug 'single stepping over breakpoint'"); |
| |
| procfd = proc_p->procfd; |
| |
| (void) memset((char *)&run, 0, sizeof (run)); |
| |
| /* add trace fault to the list of current traced faults */ |
| again1: |
| retval = ioctl(procfd, PIOCGFAULT, &faults); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again1; |
| DBG((void) fprintf(stderr, |
| "prb_proc_istepbpt: PIOCGFAULT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| praddset(&faults, FLTTRACE); |
| |
| /* issue the run command with the single-step option */ |
| run.pr_flags = PRCFAULT | PRSFAULT | PRSTEP; |
| run.pr_fault = faults; |
| |
| /* load the location of the breakpoint */ |
| run.pr_vaddr = (caddr_t)proc_p->bptaddr; |
| run.pr_flags |= PRSVADDR; |
| |
| again2: |
| retval = ioctl(procfd, PIOCRUN, &run); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again2; |
| DBG((void) fprintf(stderr, |
| "prb_proc_istepbpt: PIOCRUN failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| again3: |
| retval = ioctl(procfd, PIOCWSTOP, &prstat); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again3; |
| DBG((void) fprintf(stderr, |
| "prb_proc_istepbpt: PIOCWSTOP failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| |
| DBG_TNF_PROBE_2(prb_proc_istepbpt_2, "libtnfctl", "sunw%verbosity 2;", |
| tnf_opaque, pc, prstat.pr_reg[R_PC], |
| tnf_opaque, instr, prstat.pr_instr); |
| |
| |
| /* clear any current faults */ |
| again4: |
| retval = ioctl(procfd, PIOCCFAULT, NULL); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again4; |
| DBG((void) fprintf(stderr, |
| "prb_proc_clrbptflt: PIOCCFAULT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| /* remove the trace fault from the current traced faults */ |
| prdelset(&faults, FLTTRACE); |
| again5: |
| retval = ioctl(procfd, PIOCSFAULT, &faults); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again5; |
| DBG((void) fprintf(stderr, |
| "prb_proc_istepbpt: PIOCSFAULT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_clrbptflt() - clear an encountered breakpoint fault |
| */ |
| prb_status_t |
| prb_proc_clrbptflt(prb_proc_ctl_t *proc_p) |
| { |
| int retval; |
| int procfd; |
| |
| DBG_TNF_PROBE_0(prb_proc_clrbptflt_1, "libtnfctl", "sunw%verbosity 2"); |
| |
| procfd = proc_p->procfd; |
| |
| /* clear any current faults */ |
| again: |
| retval = ioctl(procfd, PIOCCFAULT, NULL); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again; |
| DBG((void) fprintf(stderr, |
| "prb_proc_clrbptflt: PIOCCFAULT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_tracebpt() - sets the bpt tracing state. |
| */ |
| prb_status_t |
| prb_proc_tracebpt(prb_proc_ctl_t *proc_p, boolean_t bpt) |
| { |
| int procfd; |
| int retval; |
| fltset_t faults; |
| |
| DBG_TNF_PROBE_1(prb_proc_tracebpt_1, "libtnfctl", "sunw%verbosity 2;", |
| tnf_string, bpt_state, (bpt) ? "enabled" : "disabled"); |
| |
| procfd = proc_p->procfd; |
| /* get the current set of traced faults */ |
| again1: |
| retval = ioctl(procfd, PIOCGFAULT, &faults); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again1; |
| DBG((void) fprintf(stderr, |
| "prb_proc_tracebpt: PIOCGFAULT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| /* set or clear the breakpoint flag */ |
| if (bpt) |
| praddset(&faults, FLTBPT); |
| else |
| prdelset(&faults, FLTBPT); |
| |
| /* write the fault set back */ |
| again2: |
| retval = ioctl(procfd, PIOCSFAULT, &faults); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again2; |
| DBG((void) fprintf(stderr, |
| "prb_proc_tracebpt: PIOCSFAULT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| } |
| |
| /* Note - the following 3 functions should be combined */ |
| |
| /* |
| * prb_proc_setrlc() - sets or clears the run-on-last-close flag. |
| */ |
| prb_status_t |
| prb_proc_setrlc(prb_proc_ctl_t *proc_p, boolean_t rlc) |
| { |
| int procfd; |
| long mode; |
| int retval; |
| |
| DBG_TNF_PROBE_1(prb_proc_setrlc_1, "libtnfctl", "sunw%verbosity 2;", |
| tnf_string, run_on_last_close, (rlc) ? "setting" : "clearing"); |
| |
| procfd = proc_p->procfd; |
| mode = PR_RLC; |
| |
| if (rlc) { |
| again1: |
| retval = ioctl(procfd, PIOCSET, &mode); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again1; |
| DBG((void) fprintf(stderr, |
| "prb_proc_setrlc: PIOCSET failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| } else { |
| again2: |
| retval = ioctl(procfd, PIOCRESET, &mode); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again2; |
| DBG((void) fprintf(stderr, |
| "prb_proc_setrlc: PIOCRESET failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| } |
| |
| return (PRB_STATUS_OK); |
| |
| |
| } /* end prb_proc_setrlc */ |
| |
| |
| /* |
| * prb_proc_setklc() - sets or clears the kill-on-last-close flag. |
| */ |
| prb_status_t |
| prb_proc_setklc(prb_proc_ctl_t *proc_p, boolean_t klc) |
| { |
| int procfd; |
| long mode; |
| int retval; |
| |
| DBG_TNF_PROBE_1(prb_proc_setklc_1, "libtnfctl", "sunw%verbosity 2;", |
| tnf_string, kill_on_last_close, (klc) ? "setting" : "clearing"); |
| |
| procfd = proc_p->procfd; |
| mode = PR_KLC; |
| |
| if (klc) { |
| again1: |
| retval = ioctl(procfd, PIOCSET, &mode); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again1; |
| DBG((void) fprintf(stderr, |
| "prb_proc_setklc: PIOCSET failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| } else { |
| again2: |
| retval = ioctl(procfd, PIOCRESET, &mode); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again2; |
| DBG((void) fprintf(stderr, |
| "prb_proc_setklc: PIOCRESET failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| } |
| |
| return (PRB_STATUS_OK); |
| |
| } /* end prb_proc_setklc */ |
| |
| /* |
| * prb_proc_setfork() - sets or clears the inherit-on-fork flag |
| */ |
| prb_status_t |
| prb_proc_setfork(prb_proc_ctl_t *proc_p, boolean_t inhfork) |
| { |
| int procfd; |
| long mode; |
| int retval; |
| |
| DBG_TNF_PROBE_1(prb_proc_setfork_1, "libtnfctl", "sunw%verbosity 2;", |
| tnf_string, kill_on_last_close, |
| (inhfork) ? "setting" : "clearing"); |
| |
| procfd = proc_p->procfd; |
| mode = PR_FORK; |
| |
| if (inhfork) { |
| again1: |
| retval = ioctl(procfd, PIOCSET, &mode); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again1; |
| DBG((void) fprintf(stderr, |
| "prb_proc_setfork: PIOCSET failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| } else { |
| again2: |
| retval = ioctl(procfd, PIOCRESET, &mode); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again2; |
| DBG((void) fprintf(stderr, |
| "prb_proc_setfork: PIOCRESET failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| } |
| |
| return (PRB_STATUS_OK); |
| |
| } /* end prb_proc_setfork */ |
| |
| /* |
| * prb_proc_exit() - if op is PRB_SYS_ALL, sets up the target process to stop |
| * on exit from all system calls. If op is PRB_SYS_NONE, sets up the target |
| * process so that it will not stop on exit from any system call. |
| * PRB_SYS_ADD and PRB_SYS_DEL adds or deletes a particular system call from |
| * the mask of "interested" system calls respectively. This function can be |
| * called multiple times to build up the mask. |
| */ |
| prb_status_t |
| prb_proc_exit(prb_proc_ctl_t *proc_p, |
| uint_t syscall, |
| prb_syscall_op_t op) |
| { |
| int procfd; |
| int retval; |
| sysset_t sysmask; |
| |
| DBG_TNF_PROBE_0(prb_proc_exit_1, "libtnfctl", |
| "sunw%verbosity 2; " |
| "sunw%debug 'setting up target to stop on exit of syscall'"); |
| |
| procfd = proc_p->procfd; |
| |
| switch (op) { |
| case PRB_SYS_ALL: |
| prfillset(&sysmask); |
| break; |
| case PRB_SYS_NONE: |
| premptyset(&sysmask); |
| break; |
| case PRB_SYS_ADD: |
| again1: |
| retval = ioctl(procfd, PIOCGEXIT, &sysmask); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again1; |
| DBG((void) fprintf(stderr, |
| "prb_proc_exit: PIOCGEXIT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| praddset(&sysmask, syscall); |
| break; |
| case PRB_SYS_DEL: |
| again2: |
| retval = ioctl(procfd, PIOCGEXIT, &sysmask); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again2; |
| DBG((void) fprintf(stderr, |
| "prb_proc_exit: PIOCGEXIT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| prdelset(&sysmask, syscall); |
| break; |
| default: |
| DBG((void) fprintf(stderr, "prb_proc_exit: bad input arg\n")); |
| return (PRB_STATUS_BADARG); |
| } |
| again3: |
| retval = ioctl(procfd, PIOCSEXIT, &sysmask); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again3; |
| DBG((void) fprintf(stderr, |
| "prb_proc_exit: PIOCSEXIT failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| |
| } /* end prb_proc_exit */ |
| |
| /* |
| * prb_proc_entry() - if op is PRB_SYS_ALL, sets up the target process to |
| * stop on entry from all system calls. If op is PRB_SYS_NONE, sets up the |
| * target process so that it will not stop on entry from any system call. |
| * PRB_SYS_ADD and PRB_SYS_DEL adds or deletes a particular system call from |
| * the mask of "interested" system calls respectively. This function can be |
| * called multiple times to build up the mask. |
| */ |
| prb_status_t |
| prb_proc_entry(prb_proc_ctl_t *proc_p, |
| uint_t syscall, |
| prb_syscall_op_t op) |
| { |
| int procfd; |
| int retval; |
| sysset_t sysmask; |
| |
| DBG_TNF_PROBE_0(prb_proc_entry_1, "libtnfctl", |
| "sunw%verbosity 2; " |
| "sunw%debug 'setting up target to stop on entry of syscall'"); |
| |
| procfd = proc_p->procfd; |
| |
| switch (op) { |
| case PRB_SYS_ALL: |
| prfillset(&sysmask); |
| break; |
| case PRB_SYS_NONE: |
| premptyset(&sysmask); |
| break; |
| case PRB_SYS_ADD: |
| again1: |
| retval = ioctl(procfd, PIOCGENTRY, &sysmask); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again1; |
| DBG((void) fprintf(stderr, |
| "prb_proc_entry: PIOCGENTRY failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| praddset(&sysmask, syscall); |
| break; |
| case PRB_SYS_DEL: |
| again2: |
| retval = ioctl(procfd, PIOCGENTRY, &sysmask); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again2; |
| DBG((void) fprintf(stderr, |
| "prb_proc_entry: PIOCGENTRY failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| prdelset(&sysmask, syscall); |
| break; |
| default: |
| DBG((void) fprintf(stderr, "prb_proc_entry: bad input arg\n")); |
| return (PRB_STATUS_BADARG); |
| } |
| again3: |
| retval = ioctl(procfd, PIOCSENTRY, &sysmask); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again3; |
| DBG((void) fprintf(stderr, |
| "prb_proc_entry: PIOCSENTRY failed: %s\n", |
| strerror(errno))); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| } |
| |
| /* |
| * prb_proc_read() - reads a block of memory from a processes address space. |
| */ |
| prb_status_t |
| prb_proc_read(prb_proc_ctl_t *proc_p, uintptr_t addr, void *buf, size_t size) |
| { |
| int procfd; |
| ssize_t sz; |
| off_t offset; |
| |
| DBG_TNF_PROBE_2(prb_proc_read_1, "libtnfctl", "sunw%verbosity 3;", |
| tnf_long, num_bytes, size, |
| tnf_opaque, from_address, addr); |
| |
| procfd = proc_p->procfd; |
| offset = lseek(procfd, (off_t)addr, SEEK_SET); |
| if (offset != (off_t)addr) { |
| DBG(perror("prb_proc_read: lseek failed")); |
| return (prb_status_map(errno)); |
| } |
| sz = read(procfd, buf, size); |
| if (sz != size) { |
| DBG(perror("prb_proc_read: read failed")); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_write() - writes a block of memory from a processes address |
| * space. |
| */ |
| prb_status_t |
| prb_proc_write(prb_proc_ctl_t *proc_p, uintptr_t addr, void *buf, size_t size) |
| { |
| int procfd; |
| ssize_t sz; |
| off_t offset; |
| |
| DBG_TNF_PROBE_2(prb_proc_write_1, "libtnfctl", "sunw%verbosity 3;", |
| tnf_long, num_bytes, size, |
| tnf_opaque, to_address, addr); |
| |
| procfd = proc_p->procfd; |
| offset = lseek(procfd, (off_t)addr, SEEK_SET); |
| if (offset != (off_t)addr) { |
| DBG(perror("prb_proc_write: lseek failed")); |
| return (prb_status_map(errno)); |
| } |
| sz = write(procfd, buf, size); |
| if (sz != size) { |
| DBG(perror("prb_proc_write: write failed")); |
| return (prb_status_map(errno)); |
| } |
| return (PRB_STATUS_OK); |
| } |
| |
| |
| /* |
| * prb_proc_readstr() - dereferences a string in the target |
| * NOTE: There is a similar routine called _tnfctl_readstr_targ() |
| * used by tnfctl layer. It would be better if there was only |
| * one of these functions defined. |
| */ |
| |
| #define BUFSZ 256 |
| |
| prb_status_t |
| prb_proc_readstr(prb_proc_ctl_t *proc_p, uintptr_t addr, const char **outstr_pp) |
| { |
| prb_status_t prbstat; |
| int bufsz = BUFSZ; |
| char buffer[BUFSZ + 1]; |
| offset_t offset; |
| char *ptr, *orig_ptr; |
| |
| *outstr_pp = NULL; |
| offset = 0; |
| |
| /* allocate an inital return buffer */ |
| ptr = (char *)malloc(BUFSZ); |
| if (!ptr) { |
| DBG((void) fprintf(stderr, |
| "prb_proc_readstr: malloc failed\n")); |
| return (PRB_STATUS_ALLOCFAIL); |
| } |
| /*LINTED constant in conditional context*/ |
| while (1) { |
| int i; |
| |
| /* read a chunk into our buffer */ |
| prbstat = prb_proc_read(proc_p, addr + offset, buffer, bufsz); |
| if (prbstat) { |
| |
| /* |
| * if we get into trouble with a large read, try again |
| * with a single byte. Subsequent failure is real ... |
| */ |
| if (bufsz > 1) { |
| bufsz = 1; |
| continue; |
| } |
| |
| DBG((void) fprintf(stderr, |
| "prb_proc_readstr: prb_proc_read failed: %s\n", |
| prb_status_str(prbstat))); |
| free(ptr); |
| return (prbstat); |
| } |
| /* copy the chracters into the return buffer */ |
| for (i = 0; i < bufsz; i++) { |
| char c = buffer[i]; |
| |
| ptr[offset + i] = c; |
| if (c == '\0') { |
| /* hooray! we saw the end of the string */ |
| *outstr_pp = ptr; |
| return (PRB_STATUS_OK); |
| } |
| } |
| |
| /* bummer, need to grab another bufsz characters */ |
| offset += bufsz; |
| orig_ptr = ptr; |
| ptr = (char *)realloc(ptr, offset + bufsz); |
| if (!ptr) { |
| free(orig_ptr); |
| DBG((void) fprintf(stderr, |
| "prb_proc_readstr: realloc failed\n")); |
| return (PRB_STATUS_ALLOCFAIL); |
| } |
| } |
| |
| #if defined(lint) |
| return (PRB_STATUS_OK); |
| #endif |
| } |
| |
| prb_status_t |
| prb_proc_get_r0_r1(prb_proc_ctl_t *proc_p, prgreg_t *r0, prgreg_t *r1) |
| { |
| int retval; |
| int procfd; |
| prstatus_t prstatus; |
| |
| procfd = proc_p->procfd; |
| again: |
| retval = ioctl(procfd, PIOCSTATUS, &prstatus); |
| if (retval == -1) { |
| if (errno == EINTR) |
| goto again; |
| return (prb_status_map(errno)); |
| } |
| |
| /* |
| * Use R_Rn register definitions for some uniformity |
| * sparc: define R_R0 R_O0 |
| * define R_R1 R_O1 |
| * x86: define R_R0 EAX |
| * define R_R1 EDX |
| */ |
| *r0 = prstatus.pr_reg[R_R0]; |
| *r1 = prstatus.pr_reg[R_R1]; |
| DBG((void) fprintf |
| (stderr, "prb_proc_get_r0_r1: R_R0 = %d, R_R1 = %d\n", *r0, *r1)); |
| return (PRB_STATUS_OK); |
| } |