xref: /titanic_52/usr/src/lib/libtnfctl/prb_proc.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1994-1999 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains interfaces that are wrappers over the basic
31  * /proc ioctls
32  */
33 
34 #include <unistd.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <sys/uio.h>
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/fault.h>
44 #include <sys/procfs.h>
45 
46 #include "prb_proc_int.h"
47 #include "dbg.h"
48 
49 /*
50  * Declarations
51  */
52 
53 #define	PROCFORMAT	"/proc/%d"
54 
55 static prb_status_t
56 prb_proc_open_general(pid_t pid, prb_proc_ctl_t **proc_pp, int oflg);
57 
58 /*
59  * prb_proc_open_general() - function to open the process file
60  * system entry for the supplied process. Opens with different
61  * options based on the 'oflg'.
62  * Returns a pointer to an opaque structure that contains the fd
63  * needed for /proc control.
64  */
65 
66 prb_status_t
67 prb_proc_open_general(pid_t pid, prb_proc_ctl_t **proc_pp, int oflg)
68 {
69 	prb_proc_ctl_t		*proc_p;
70 	char			path[MAXPATHLEN];
71 	int			retval;
72 
73 	(void) sprintf(path, PROCFORMAT, (int)pid);
74 
75 	DBG_TNF_PROBE_1(prb_proc_open_1, "libtnfctl", "sunw%verbosity 2",
76 		tnf_string, opening_proc_on, path);
77 
78 	retval = open(path, oflg);
79 	if (retval == -1) {
80 		DBG((void) fprintf(stderr,
81 			"proc_open: open of \"%s\" failed: %s\n",
82 			path, strerror(errno)));
83 		return (prb_status_map(errno));
84 	}
85 	/* allocate proc_p and zero fill */
86 	proc_p = calloc(1, sizeof (*proc_p));
87 	if (proc_p == NULL)
88 		return (PRB_STATUS_ALLOCFAIL);
89 	proc_p->procfd = retval;
90 	proc_p->pid = pid;
91 	*proc_pp = proc_p;
92 	return (PRB_STATUS_OK);
93 }
94 
95 
96 /*
97  * prb_proc_open() - a wrapper which opens the process file system
98  * entry for the supplied process.  Returns a pointer to an opaque
99  * structure that contains the fd needed for /proc control.
100  */
101 
102 prb_status_t
103 prb_proc_open(pid_t pid, prb_proc_ctl_t **proc_pp)
104 {
105 
106 	return (prb_proc_open_general(pid,
107 				proc_pp, O_RDWR | O_EXCL));
108 
109 }
110 
111 /*
112  * prb_proc_reopen() - re-opens the process, mainly for setuid/setgid files.
113  * Read the last section of /proc man page for details.
114  * re-open should not use O_EXCL flag.
115  */
116 
117 prb_status_t
118 prb_proc_reopen(pid_t pid, prb_proc_ctl_t **proc_pp)
119 {
120 
121 	return (prb_proc_open_general(pid,
122 				proc_pp, O_RDWR));
123 
124 }
125 
126 /*
127  * prob_proc_close() - close the proc fd and free the memory taken up
128  *	by proc_p
129  */
130 prb_status_t
131 prb_proc_close(prb_proc_ctl_t *proc_p)
132 {
133 	DBG_TNF_PROBE_0(prb_proc_close_1, "libtnfctl", "sunw%verbosity 2");
134 
135 	if (proc_p == NULL)
136 		return (PRB_STATUS_OK);
137 
138 	if (close(proc_p->procfd) == -1) {
139 		DBG((void) fprintf(stderr,
140 			"proc_close: close failed: %s\n", strerror(errno)));
141 		return (prb_status_map(errno));
142 	}
143 	free(proc_p);
144 	return (PRB_STATUS_OK);
145 }
146 
147 /*
148  * prb_proc_pid_get() - gets the pid of the proc
149  */
150 pid_t
151 prb_proc_pid_get(prb_proc_ctl_t *proc_p)
152 {
153 	return (proc_p->pid);
154 }
155 
156 /*
157  * prb_proc_stop() - stops the target process
158  */
159 prb_status_t
160 prb_proc_stop(prb_proc_ctl_t *proc_p)
161 {
162 	int			 retval;
163 
164 	DBG_TNF_PROBE_0(prb_proc_stop_1, "libtnfctl",
165 		"sunw%verbosity 2; sunw%debug 'stopping the target process'");
166 
167 again:
168 	retval = ioctl(proc_p->procfd, PIOCSTOP, NULL);
169 	if (retval == -1) {
170 		if (errno == EINTR)
171 			goto again;
172 		DBG((void) fprintf(stderr,
173 			"prb_proc_stop: PIOCSTOP failed: %s\n",
174 			strerror(errno)));
175 		return (prb_status_map(errno));
176 	}
177 	return (PRB_STATUS_OK);
178 }
179 
180 
181 /*
182  * prb_proc_prstop() - runs and stops the process, used to clear a target
183  * process out of a system call state.
184  */
185 prb_status_t
186 prb_proc_prstop(prb_proc_ctl_t *proc_p)
187 {
188 	int		procfd;
189 	int		retval;
190 	prrun_t		prrun;
191 	prstatus_t	prstat;
192 
193 	DBG_TNF_PROBE_0(prb_proc_prstop_1, "libtnfctl",
194 		"sunw%verbosity 2; sunw%debug 'stepping the target process'");
195 
196 	procfd = proc_p->procfd;
197 	(void) memset((char *)&prrun, 0, sizeof (prrun));
198 	(void) memset((char *)&prstat, 0, sizeof (prstat));
199 
200 again1:
201 	prrun.pr_flags = PRSTOP;
202 	retval = ioctl(procfd, PIOCRUN, &prrun);
203 	if (retval == -1) {
204 		if (errno == EINTR)
205 			goto again1;
206 		DBG((void) fprintf(stderr,
207 			"prb_proc_prstop: PIOCRUN failed: %s\n",
208 			strerror(errno)));
209 		return (prb_status_map(errno));
210 	}
211 again2:
212 	retval = ioctl(procfd, PIOCWSTOP, &prstat);
213 	if (retval == -1) {
214 		if (errno == EINTR)
215 			goto again2;
216 		DBG((void) fprintf(stderr,
217 			"prb_proc_prstop: PIOCWSTOP failed: %s\n",
218 			strerror(errno)));
219 		return (prb_status_map(errno));
220 	}
221 	/*
222 	 * if we didn't stop because we requested it (eg. if there was a
223 	 * signal in the target ?), we might need to try again
224 	 */
225 	if (prstat.pr_why != PR_REQUESTED)
226 		goto again1;
227 
228 	return (PRB_STATUS_OK);
229 }
230 
231 
232 /*
233  * prb_proc_state() - returns the status pf the process
234  */
235 prb_status_t
236 prb_proc_state(prb_proc_ctl_t *proc_p, prb_proc_state_t *state_p)
237 {
238 	int		procfd;
239 	int		retval;
240 	prstatus_t	prstatus;
241 
242 	DBG_TNF_PROBE_0(prb_proc_state_1, "libtnfctl",
243 		"sunw%verbosity 2; sunw%debug 'getting the status'");
244 
245 	procfd = proc_p->procfd;
246 
247 	(void) memset(&prstatus, 0, sizeof (prstatus));
248 
249 again:
250 	retval = ioctl(procfd, PIOCSTATUS, &prstatus);
251 	if (retval == -1) {
252 		if (errno == EINTR)
253 			goto again;
254 		DBG((void) fprintf(stderr,
255 			"prb_proc_status: PIOCSTATUS failed: %s\n",
256 			strerror(errno)));
257 		return (prb_status_map(errno));
258 	}
259 	state_p->ps_isbptfault = (prstatus.pr_flags & PR_FAULTED &&
260 		prstatus.pr_what == FLTBPT);
261 	state_p->ps_isstopped = ((prstatus.pr_flags & PR_STOPPED) != 0);
262 	state_p->ps_isinsys = ((prstatus.pr_flags & PR_ASLEEP) != 0);
263 	state_p->ps_isrequested = ((prstatus.pr_why & PR_REQUESTED) != 0);
264 	state_p->ps_issysexit = ((prstatus.pr_why & PR_SYSEXIT) != 0);
265 	state_p->ps_issysentry = ((prstatus.pr_why & PR_SYSENTRY) != 0);
266 	state_p->ps_syscallnum = prstatus.pr_what;
267 	return (PRB_STATUS_OK);
268 }
269 
270 
271 /*
272  * prb_proc_wait() - waits for the target process to stop
273  */
274 prb_status_t
275 prb_proc_wait(prb_proc_ctl_t *proc_p, boolean_t use_sigmask, sigset_t *oldmask)
276 {
277 	int		procfd;
278 	int		retval;
279 	prstatus_t	prstat;
280 	sigset_t	pendmask;
281 	int		i, mask_size;
282 	boolean_t	pending_signal = B_FALSE;
283 
284 	DBG_TNF_PROBE_0(prb_proc_wait_1, "libtnfctl",
285 		"sunw%verbosity 2;"
286 		"sunw%debug 'waiting for the target process to stop'");
287 
288 	procfd = proc_p->procfd;
289 
290 	/*
291 	 * This one of the places where we do not resubmit the ioctl if
292 	 * if it is terminated by an EINTR (interrupted system call). In
293 	 * this case, the caller knows best ...
294 	 */
295 	(void) memset(&prstat, 0, sizeof (prstat));
296 
297 	/* if we blocked signals... */
298 	if (use_sigmask) {
299 		if (sigemptyset(&pendmask) == -1)
300 			return (prb_status_map(errno));
301 		if (sigpending(&pendmask) == -1)
302 			return (prb_status_map(errno));
303 		/*
304 		 * check if there were any signals pending -
305 		 * XXXX libc should provide this interface
306 		 */
307 		mask_size = sizeof (pendmask) / sizeof (pendmask.__sigbits[0]);
308 		for (i = 0; i < mask_size; i++) {
309 			if (pendmask.__sigbits[i] != 0)
310 				pending_signal = B_TRUE;
311 		}
312 
313 		/* return to original signal mask */
314 		if (sigprocmask(SIG_SETMASK, oldmask, NULL) == -1)
315 			return (prb_status_map(errno));
316 
317 		/* if there was a pending signal, don't call PIOCWSTOP ioctl */
318 		if (pending_signal)
319 			return (prb_status_map(EINTR));
320 
321 		/*
322 		 * XXXX - there is a a race between now and when we call
323 		 * the PIOCWSTOP ioctl.  One solution, is for the user to
324 		 * call an interface in libtnfctl from their signal handler.
325 		 * This interface will do a longjmp such that it never
326 		 * calls the ioctl (the setjmp would be before we restore
327 		 * the signal mask above)
328 		 */
329 	}
330 
331 	retval = ioctl(procfd, PIOCWSTOP, &prstat);
332 
333 	DBG_TNF_PROBE_2(prb_proc_wait_2, "libtnfctl", "sunw%verbosity 2;",
334 			tnf_opaque, pc, prstat.pr_reg[R_PC],
335 			tnf_opaque, instr, prstat.pr_instr);
336 
337 	if (retval == -1) {
338 #ifdef DEBUG
339 		if (errno != EINTR && errno != ENOENT)
340 			(void) fprintf(stderr,
341 				"prb_proc_wait: PIOCWSTOP failed: %s\n",
342 				strerror(errno));
343 
344 #endif
345 		return (prb_status_map(errno));
346 	}
347 
348 	return (PRB_STATUS_OK);
349 }
350 
351 
352 /*
353  * prb_proc_cont() - continues the target process
354  */
355 prb_status_t
356 prb_proc_cont(prb_proc_ctl_t *proc_p)
357 {
358 	int		procfd;
359 	int		retval;
360 	prrun_t		prrun;
361 
362 	DBG_TNF_PROBE_0(prb_proc_cont_1, "libtnfctl",
363 		"sunw%verbosity 2; sunw%debug 'starting the target process'");
364 	procfd = proc_p->procfd;
365 
366 	(void) memset((char *)&prrun, 0, sizeof (prrun));
367 
368 again:
369 	prrun.pr_flags = PRCFAULT;
370 	retval = ioctl(procfd, PIOCRUN, &prrun);
371 	if (retval == -1) {
372 		if (errno == EINTR)
373 			goto again;
374 		DBG((void) fprintf(stderr,
375 			"prb_proc_cont: PIOCRUN failed: %s\n",
376 			strerror(errno)));
377 		return (prb_status_map(errno));
378 	}
379 	return (PRB_STATUS_OK);
380 }
381 
382 
383 /*
384  * prb_proc_istepbpt() - step the target process one instruction
385  *
386  * CAUTION!!!! - this routine is specialized to only be able to single
387  * step over the breakpoint location.
388  */
389 prb_status_t
390 prb_proc_istepbpt(prb_proc_ctl_t *proc_p)
391 {
392 	int		procfd;
393 	int		retval;
394 	prrun_t		run;
395 	fltset_t	faults;
396 	prstatus_t	prstat;
397 
398 	DBG_TNF_PROBE_0(prb_proc_istepbpt_1, "libtnfctl",
399 		"sunw%verbosity 2; "
400 		"sunw%debug 'single stepping over breakpoint'");
401 
402 	procfd = proc_p->procfd;
403 
404 	(void) memset((char *)&run, 0, sizeof (run));
405 
406 	/* add trace fault to the list of current traced faults */
407 again1:
408 	retval = ioctl(procfd, PIOCGFAULT, &faults);
409 	if (retval == -1) {
410 		if (errno == EINTR)
411 			goto again1;
412 		DBG((void) fprintf(stderr,
413 			"prb_proc_istepbpt: PIOCGFAULT failed: %s\n",
414 			strerror(errno)));
415 		return (prb_status_map(errno));
416 	}
417 	praddset(&faults, FLTTRACE);
418 
419 	/* issue the run command with the single-step option */
420 	run.pr_flags = PRCFAULT | PRSFAULT | PRSTEP;
421 	run.pr_fault = faults;
422 
423 	/* load the location of the breakpoint */
424 	run.pr_vaddr = (caddr_t)proc_p->bptaddr;
425 	run.pr_flags |= PRSVADDR;
426 
427 again2:
428 	retval = ioctl(procfd, PIOCRUN, &run);
429 	if (retval == -1) {
430 		if (errno == EINTR)
431 			goto again2;
432 		DBG((void) fprintf(stderr,
433 			"prb_proc_istepbpt: PIOCRUN failed: %s\n",
434 			strerror(errno)));
435 		return (prb_status_map(errno));
436 	}
437 again3:
438 	retval = ioctl(procfd, PIOCWSTOP, &prstat);
439 	if (retval == -1) {
440 		if (errno == EINTR)
441 			goto again3;
442 		DBG((void) fprintf(stderr,
443 				"prb_proc_istepbpt: PIOCWSTOP failed: %s\n",
444 				strerror(errno)));
445 		return (prb_status_map(errno));
446 	}
447 
448 	DBG_TNF_PROBE_2(prb_proc_istepbpt_2, "libtnfctl", "sunw%verbosity 2;",
449 			tnf_opaque, pc, prstat.pr_reg[R_PC],
450 			tnf_opaque, instr, prstat.pr_instr);
451 
452 
453 	/* clear any current faults */
454 again4:
455 	retval = ioctl(procfd, PIOCCFAULT, NULL);
456 	if (retval == -1) {
457 		if (errno == EINTR)
458 			goto again4;
459 		DBG((void) fprintf(stderr,
460 			"prb_proc_clrbptflt: PIOCCFAULT failed: %s\n",
461 			strerror(errno)));
462 		return (prb_status_map(errno));
463 	}
464 	/* remove the trace fault from the current traced faults */
465 	prdelset(&faults, FLTTRACE);
466 again5:
467 	retval = ioctl(procfd, PIOCSFAULT, &faults);
468 	if (retval == -1) {
469 		if (errno == EINTR)
470 			goto again5;
471 		DBG((void) fprintf(stderr,
472 			"prb_proc_istepbpt: PIOCSFAULT failed: %s\n",
473 			strerror(errno)));
474 		return (prb_status_map(errno));
475 	}
476 	return (PRB_STATUS_OK);
477 }
478 
479 
480 /*
481  * prb_proc_clrbptflt() - clear an encountered breakpoint fault
482  */
483 prb_status_t
484 prb_proc_clrbptflt(prb_proc_ctl_t *proc_p)
485 {
486 	int	retval;
487 	int	procfd;
488 
489 	DBG_TNF_PROBE_0(prb_proc_clrbptflt_1, "libtnfctl", "sunw%verbosity 2");
490 
491 	procfd = proc_p->procfd;
492 
493 	/* clear any current faults */
494 again:
495 	retval = ioctl(procfd, PIOCCFAULT, NULL);
496 	if (retval == -1) {
497 		if (errno == EINTR)
498 			goto again;
499 		DBG((void) fprintf(stderr,
500 			"prb_proc_clrbptflt: PIOCCFAULT failed: %s\n",
501 			strerror(errno)));
502 		return (prb_status_map(errno));
503 	}
504 	return (PRB_STATUS_OK);
505 }
506 
507 
508 /*
509  * prb_proc_tracebpt() - sets the bpt tracing state.
510  */
511 prb_status_t
512 prb_proc_tracebpt(prb_proc_ctl_t *proc_p, boolean_t bpt)
513 {
514 	int		procfd;
515 	int		retval;
516 	fltset_t	faults;
517 
518 	DBG_TNF_PROBE_1(prb_proc_tracebpt_1, "libtnfctl", "sunw%verbosity 2;",
519 		tnf_string, bpt_state, (bpt) ? "enabled" : "disabled");
520 
521 	procfd = proc_p->procfd;
522 	/* get the current set of traced faults */
523 again1:
524 	retval = ioctl(procfd, PIOCGFAULT, &faults);
525 	if (retval == -1) {
526 		if (errno == EINTR)
527 			goto again1;
528 		DBG((void) fprintf(stderr,
529 			"prb_proc_tracebpt: PIOCGFAULT failed: %s\n",
530 			strerror(errno)));
531 		return (prb_status_map(errno));
532 	}
533 	/* set or clear the breakpoint flag */
534 	if (bpt)
535 		praddset(&faults, FLTBPT);
536 	else
537 		prdelset(&faults, FLTBPT);
538 
539 	/* write the fault set back */
540 again2:
541 	retval = ioctl(procfd, PIOCSFAULT, &faults);
542 	if (retval == -1) {
543 		if (errno == EINTR)
544 			goto again2;
545 		DBG((void) fprintf(stderr,
546 			"prb_proc_tracebpt: PIOCSFAULT failed: %s\n",
547 			strerror(errno)));
548 		return (prb_status_map(errno));
549 	}
550 	return (PRB_STATUS_OK);
551 }
552 
553 /* Note - the following 3 functions should be combined */
554 
555 /*
556  * prb_proc_setrlc() - sets or clears the run-on-last-close flag.
557  */
558 prb_status_t
559 prb_proc_setrlc(prb_proc_ctl_t *proc_p, boolean_t rlc)
560 {
561 	int			procfd;
562 	long			mode;
563 	int			retval;
564 
565 	DBG_TNF_PROBE_1(prb_proc_setrlc_1, "libtnfctl", "sunw%verbosity 2;",
566 		tnf_string, run_on_last_close, (rlc) ? "setting" : "clearing");
567 
568 	procfd = proc_p->procfd;
569 	mode = PR_RLC;
570 
571 	if (rlc) {
572 again1:
573 		retval = ioctl(procfd, PIOCSET, &mode);
574 		if (retval == -1) {
575 			if (errno == EINTR)
576 				goto again1;
577 			DBG((void) fprintf(stderr,
578 				"prb_proc_setrlc: PIOCSET failed: %s\n",
579 				strerror(errno)));
580 			return (prb_status_map(errno));
581 		}
582 	} else {
583 again2:
584 		retval = ioctl(procfd, PIOCRESET, &mode);
585 		if (retval == -1) {
586 			if (errno == EINTR)
587 				goto again2;
588 			DBG((void) fprintf(stderr,
589 				"prb_proc_setrlc: PIOCRESET failed: %s\n",
590 				strerror(errno)));
591 			return (prb_status_map(errno));
592 		}
593 	}
594 
595 	return (PRB_STATUS_OK);
596 
597 
598 }				/* end prb_proc_setrlc */
599 
600 
601 /*
602  * prb_proc_setklc() - sets or clears the kill-on-last-close flag.
603  */
604 prb_status_t
605 prb_proc_setklc(prb_proc_ctl_t *proc_p, boolean_t klc)
606 {
607 	int			procfd;
608 	long			mode;
609 	int			retval;
610 
611 	DBG_TNF_PROBE_1(prb_proc_setklc_1, "libtnfctl", "sunw%verbosity 2;",
612 		tnf_string, kill_on_last_close, (klc) ? "setting" : "clearing");
613 
614 	procfd = proc_p->procfd;
615 	mode = PR_KLC;
616 
617 	if (klc) {
618 again1:
619 		retval = ioctl(procfd, PIOCSET, &mode);
620 		if (retval == -1) {
621 			if (errno == EINTR)
622 				goto again1;
623 			DBG((void) fprintf(stderr,
624 				"prb_proc_setklc: PIOCSET failed: %s\n",
625 				strerror(errno)));
626 			return (prb_status_map(errno));
627 		}
628 	} else {
629 again2:
630 		retval = ioctl(procfd, PIOCRESET, &mode);
631 		if (retval == -1) {
632 			if (errno == EINTR)
633 				goto again2;
634 			DBG((void) fprintf(stderr,
635 				"prb_proc_setklc: PIOCRESET failed: %s\n",
636 				strerror(errno)));
637 			return (prb_status_map(errno));
638 		}
639 	}
640 
641 	return (PRB_STATUS_OK);
642 
643 }				/* end prb_proc_setklc */
644 
645 /*
646  * prb_proc_setfork() - sets or clears the inherit-on-fork flag
647  */
648 prb_status_t
649 prb_proc_setfork(prb_proc_ctl_t *proc_p, boolean_t inhfork)
650 {
651 	int			procfd;
652 	long			mode;
653 	int			retval;
654 
655 	DBG_TNF_PROBE_1(prb_proc_setfork_1, "libtnfctl", "sunw%verbosity 2;",
656 		tnf_string, kill_on_last_close,
657 		(inhfork) ? "setting" : "clearing");
658 
659 	procfd = proc_p->procfd;
660 	mode = PR_FORK;
661 
662 	if (inhfork) {
663 again1:
664 		retval = ioctl(procfd, PIOCSET, &mode);
665 		if (retval == -1) {
666 			if (errno == EINTR)
667 				goto again1;
668 			DBG((void) fprintf(stderr,
669 				"prb_proc_setfork: PIOCSET failed: %s\n",
670 				strerror(errno)));
671 			return (prb_status_map(errno));
672 		}
673 	} else {
674 again2:
675 		retval = ioctl(procfd, PIOCRESET, &mode);
676 		if (retval == -1) {
677 			if (errno == EINTR)
678 				goto again2;
679 			DBG((void) fprintf(stderr,
680 				"prb_proc_setfork: PIOCRESET failed: %s\n",
681 				strerror(errno)));
682 			return (prb_status_map(errno));
683 		}
684 	}
685 
686 	return (PRB_STATUS_OK);
687 
688 }				/* end prb_proc_setfork */
689 
690 /*
691  * prb_proc_exit() - if op is PRB_SYS_ALL, sets up the target process to stop
692  * on exit from all system calls.  If op is PRB_SYS_NONE, sets up the target
693  * process so that it will not stop on exit from any system call.
694  * PRB_SYS_ADD and PRB_SYS_DEL adds or deletes a particular system call from
695  * the mask of "interested" system calls respectively. This function can be
696  * called multiple times to build up the mask.
697  */
698 prb_status_t
699 prb_proc_exit(prb_proc_ctl_t *proc_p,
700 	uint_t syscall,
701 	prb_syscall_op_t op)
702 {
703 	int		procfd;
704 	int		retval;
705 	sysset_t	sysmask;
706 
707 	DBG_TNF_PROBE_0(prb_proc_exit_1, "libtnfctl",
708 		"sunw%verbosity 2; "
709 		"sunw%debug 'setting up target to stop on exit of syscall'");
710 
711 	procfd = proc_p->procfd;
712 
713 	switch (op) {
714 	case PRB_SYS_ALL:
715 		prfillset(&sysmask);
716 		break;
717 	case PRB_SYS_NONE:
718 		premptyset(&sysmask);
719 		break;
720 	case PRB_SYS_ADD:
721 again1:
722 		retval = ioctl(procfd, PIOCGEXIT, &sysmask);
723 		if (retval == -1) {
724 			if (errno == EINTR)
725 				goto again1;
726 			DBG((void) fprintf(stderr,
727 				"prb_proc_exit: PIOCGEXIT failed: %s\n",
728 				strerror(errno)));
729 			return (prb_status_map(errno));
730 		}
731 		praddset(&sysmask, syscall);
732 		break;
733 	case PRB_SYS_DEL:
734 again2:
735 		retval = ioctl(procfd, PIOCGEXIT, &sysmask);
736 		if (retval == -1) {
737 			if (errno == EINTR)
738 				goto again2;
739 			DBG((void) fprintf(stderr,
740 				"prb_proc_exit: PIOCGEXIT failed: %s\n",
741 				strerror(errno)));
742 			return (prb_status_map(errno));
743 		}
744 		prdelset(&sysmask, syscall);
745 		break;
746 	default:
747 		DBG((void) fprintf(stderr, "prb_proc_exit: bad input arg\n"));
748 		return (PRB_STATUS_BADARG);
749 	}
750 again3:
751 	retval = ioctl(procfd, PIOCSEXIT, &sysmask);
752 	if (retval == -1) {
753 		if (errno == EINTR)
754 			goto again3;
755 		DBG((void) fprintf(stderr,
756 			"prb_proc_exit: PIOCSEXIT failed: %s\n",
757 			strerror(errno)));
758 		return (prb_status_map(errno));
759 	}
760 	return (PRB_STATUS_OK);
761 
762 }				/* end prb_proc_exit */
763 
764 /*
765  * prb_proc_entry() - if op is PRB_SYS_ALL, sets up the target process to
766  * stop on entry from all system calls.  If op is PRB_SYS_NONE, sets up the
767  * target process so that it will not stop on entry from any system call.
768  * PRB_SYS_ADD and PRB_SYS_DEL adds or deletes a particular system call from
769  * the mask of "interested" system calls respectively. This function can be
770  * called multiple times to build up the mask.
771  */
772 prb_status_t
773 prb_proc_entry(prb_proc_ctl_t *proc_p,
774 	uint_t syscall,
775 	prb_syscall_op_t op)
776 {
777 	int		procfd;
778 	int		retval;
779 	sysset_t	sysmask;
780 
781 	DBG_TNF_PROBE_0(prb_proc_entry_1, "libtnfctl",
782 		"sunw%verbosity 2; "
783 		"sunw%debug 'setting up target to stop on entry of syscall'");
784 
785 	procfd = proc_p->procfd;
786 
787 	switch (op) {
788 	case PRB_SYS_ALL:
789 		prfillset(&sysmask);
790 		break;
791 	case PRB_SYS_NONE:
792 		premptyset(&sysmask);
793 		break;
794 	case PRB_SYS_ADD:
795 again1:
796 		retval = ioctl(procfd, PIOCGENTRY, &sysmask);
797 		if (retval == -1) {
798 			if (errno == EINTR)
799 				goto again1;
800 			DBG((void) fprintf(stderr,
801 				"prb_proc_entry: PIOCGENTRY failed: %s\n",
802 				strerror(errno)));
803 			return (prb_status_map(errno));
804 		}
805 		praddset(&sysmask, syscall);
806 		break;
807 	case PRB_SYS_DEL:
808 again2:
809 		retval = ioctl(procfd, PIOCGENTRY, &sysmask);
810 		if (retval == -1) {
811 			if (errno == EINTR)
812 				goto again2;
813 			DBG((void) fprintf(stderr,
814 				"prb_proc_entry: PIOCGENTRY failed: %s\n",
815 				strerror(errno)));
816 			return (prb_status_map(errno));
817 		}
818 		prdelset(&sysmask, syscall);
819 		break;
820 	default:
821 		DBG((void) fprintf(stderr, "prb_proc_entry: bad input arg\n"));
822 		return (PRB_STATUS_BADARG);
823 	}
824 again3:
825 	retval = ioctl(procfd, PIOCSENTRY, &sysmask);
826 	if (retval == -1) {
827 		if (errno == EINTR)
828 			goto again3;
829 		DBG((void) fprintf(stderr,
830 			"prb_proc_entry: PIOCSENTRY failed: %s\n",
831 			strerror(errno)));
832 		return (prb_status_map(errno));
833 	}
834 	return (PRB_STATUS_OK);
835 }
836 
837 /*
838  * prb_proc_read() - reads a block of memory from a processes address space.
839  */
840 prb_status_t
841 prb_proc_read(prb_proc_ctl_t *proc_p, uintptr_t addr, void *buf, size_t size)
842 {
843 	int		procfd;
844 	ssize_t		sz;
845 	off_t		offset;
846 
847 	DBG_TNF_PROBE_2(prb_proc_read_1, "libtnfctl", "sunw%verbosity 3;",
848 		tnf_long, num_bytes, size,
849 		tnf_opaque, from_address, addr);
850 
851 	procfd = proc_p->procfd;
852 	offset = lseek(procfd, (off_t)addr, SEEK_SET);
853 	if (offset != (off_t)addr) {
854 		DBG(perror("prb_proc_read: lseek failed"));
855 		return (prb_status_map(errno));
856 	}
857 	sz = read(procfd, buf, size);
858 	if (sz != size) {
859 		DBG(perror("prb_proc_read: read failed"));
860 		return (prb_status_map(errno));
861 	}
862 	return (PRB_STATUS_OK);
863 }
864 
865 
866 /*
867  * prb_proc_write() - writes a block of memory from a processes address
868  * space.
869  */
870 prb_status_t
871 prb_proc_write(prb_proc_ctl_t *proc_p, uintptr_t addr, void *buf, size_t size)
872 {
873 	int		procfd;
874 	ssize_t		sz;
875 	off_t		offset;
876 
877 	DBG_TNF_PROBE_2(prb_proc_write_1, "libtnfctl", "sunw%verbosity 3;",
878 		tnf_long, num_bytes, size,
879 		tnf_opaque, to_address, addr);
880 
881 	procfd = proc_p->procfd;
882 	offset = lseek(procfd, (off_t)addr, SEEK_SET);
883 	if (offset != (off_t)addr) {
884 		DBG(perror("prb_proc_write: lseek failed"));
885 		return (prb_status_map(errno));
886 	}
887 	sz = write(procfd, buf, size);
888 	if (sz != size) {
889 		DBG(perror("prb_proc_write: write failed"));
890 		return (prb_status_map(errno));
891 	}
892 	return (PRB_STATUS_OK);
893 }
894 
895 
896 /*
897  * prb_proc_readstr() - dereferences a string in the target
898  * 	NOTE: There is a similar routine called _tnfctl_readstr_targ()
899  *	      used by tnfctl layer.  It would be better if there was only
900  *	      one of these functions defined.
901  */
902 
903 #define	BUFSZ	256
904 
905 prb_status_t
906 prb_proc_readstr(prb_proc_ctl_t *proc_p, uintptr_t addr, const char **outstr_pp)
907 {
908 	prb_status_t	prbstat;
909 	int		bufsz = BUFSZ;
910 	char		buffer[BUFSZ + 1];
911 	offset_t	offset;
912 	char		*ptr, *orig_ptr;
913 
914 	*outstr_pp = NULL;
915 	offset = 0;
916 
917 	/* allocate an inital return buffer */
918 	ptr = (char *)malloc(BUFSZ);
919 	if (!ptr) {
920 		DBG((void) fprintf(stderr,
921 			"prb_proc_readstr: malloc failed\n"));
922 		return (PRB_STATUS_ALLOCFAIL);
923 	}
924 	/*LINTED constant in conditional context*/
925 	while (1) {
926 		int			 i;
927 
928 		/* read a chunk into our buffer */
929 		prbstat = prb_proc_read(proc_p, addr + offset, buffer, bufsz);
930 		if (prbstat) {
931 
932 			/*
933 			 * if we get into trouble with a large read, try again
934 			 * with a single byte.  Subsequent failure is real ...
935 			 */
936 			if (bufsz > 1) {
937 				bufsz = 1;
938 				continue;
939 			}
940 
941 			DBG((void) fprintf(stderr,
942 				"prb_proc_readstr: prb_proc_read failed: %s\n",
943 				prb_status_str(prbstat)));
944 			free(ptr);
945 			return (prbstat);
946 		}
947 		/* copy the chracters into the return buffer */
948 		for (i = 0; i < bufsz; i++) {
949 			char			c = buffer[i];
950 
951 			ptr[offset + i] = c;
952 			if (c == '\0') {
953 				/* hooray! we saw the end of the string */
954 				*outstr_pp = ptr;
955 				return (PRB_STATUS_OK);
956 			}
957 		}
958 
959 		/* bummer, need to grab another bufsz characters */
960 		offset += bufsz;
961 		orig_ptr = ptr;
962 		ptr = (char *)realloc(ptr, offset + bufsz);
963 		if (!ptr) {
964 			free(orig_ptr);
965 			DBG((void) fprintf(stderr,
966 				"prb_proc_readstr: realloc failed\n"));
967 			return (PRB_STATUS_ALLOCFAIL);
968 		}
969 	}
970 
971 #if defined(lint)
972 	return (PRB_STATUS_OK);
973 #endif
974 }
975 
976 prb_status_t
977 prb_proc_get_r0_r1(prb_proc_ctl_t *proc_p, prgreg_t *r0, prgreg_t *r1)
978 {
979 	int retval;
980 	int procfd;
981 	prstatus_t  prstatus;
982 
983 	procfd = proc_p->procfd;
984 again:
985 	retval = ioctl(procfd, PIOCSTATUS, &prstatus);
986 	if (retval == -1) {
987 		if (errno == EINTR)
988 			goto again;
989 		return (prb_status_map(errno));
990 	}
991 
992 	/*
993 	 *  Use R_Rn register definitions for some uniformity
994 	 *	   sparc: 	define R_R0  R_O0
995 	 *			define R_R1  R_O1
996 	 *	   x86:   	define R_R0  EAX
997 	 *			define R_R1  EDX
998 	 */
999 	*r0 = prstatus.pr_reg[R_R0];
1000 	*r1 = prstatus.pr_reg[R_R1];
1001 	DBG((void) fprintf
1002 	    (stderr, "prb_proc_get_r0_r1: R_R0 = %d, R_R1 = %d\n", *r0, *r1));
1003 	return (PRB_STATUS_OK);
1004 }
1005