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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * ptrace(2) interface built on top of proc(4).
32 #pragma weak _ptrace = ptrace
42 #include <sys/types.h>
45 #include <sys/siginfo.h>
46 #include <sys/fault.h>
47 #include <sys/syscall.h>
52 * mtlib.h must precede thread.h
59 static mutex_t pt_lock
= DEFAULTMUTEX
;
67 typedef struct cstatus
{
68 struct cstatus
*next
; /* linked list */
69 pid_t pid
; /* process-id */
70 int asfd
; /* /proc/<pid>/as */
71 int ctlfd
; /* /proc/<pid>/ctl */
72 int statusfd
; /* /proc/<pid>/status */
73 int flags
; /* see below */
74 pstatus_t pstatus
; /* from /proc/<pid>/status */
75 user_t user
; /* manufactured u-block */
79 #define CS_SETREGS 0x01 /* set registers on run */
80 #define CS_PSARGS 0x02 /* u_psargs[] has been fetched */
81 #define CS_SIGNAL 0x04 /* u_signal[] has been fetched */
83 #define NULLCP ((cstatus_t *)0)
85 static cstatus_t
*childp
= NULLCP
;
87 /* fake u-block offsets */
88 #define UP ((user_t *)NULL)
89 #define U_REG ((int)(&UP->u_reg[0]))
90 #define U_AR0 ((int)(&UP->u_ar0))
91 #define U_PSARGS ((int)(&UP->u_psargs[0]))
92 #define U_SIGNAL ((int)(&UP->u_signal[0]))
93 #define U_CODE ((int)(&UP->u_code))
94 #define U_ADDR ((int)(&UP->u_addr))
95 #define U_END ((int)sizeof (user_t))
96 #define REGADDR 0xffff0000 /* arbitrary kernel address for u_ar0 */
98 /* external routines defined in this module */
99 extern int ptrace(int, pid_t
, int, int);
100 /* static routines defined in this module */
101 static cstatus_t
*FindProc(pid_t
);
102 static void CheckAllProcs(void);
103 static int Dupfd(int, int);
104 static void MakeProcName(char *, pid_t
);
105 static int OpenProc(cstatus_t
*);
106 static void CloseProc(cstatus_t
*);
107 static cstatus_t
*GrabProc(pid_t
);
108 static void ReleaseProc(cstatus_t
*);
109 static int ProcUpdate(cstatus_t
*);
110 static void MakeUser(cstatus_t
*);
111 static void GetPsargs(cstatus_t
*);
112 static void GetSignal(cstatus_t
*);
119 static char name
[20];
122 case 0: return ("PTRACE_TRACEME");
123 case 1: return ("PTRACE_PEEKTEXT");
124 case 2: return ("PTRACE_PEEKDATA");
125 case 3: return ("PTRACE_PEEKUSER");
126 case 4: return ("PTRACE_POKETEXT");
127 case 5: return ("PTRACE_POKEDATA");
128 case 6: return ("PTRACE_POKEUSER");
129 case 7: return ("PTRACE_CONT");
130 case 8: return ("PTRACE_KILL");
131 case 9: return ("PTRACE_SINGLESTEP");
133 (void) sprintf(name
, "%d", request
);
139 ptrace(int request
, pid_t pid
, int addr
, int data
)
156 fprintf(stderr
, " ptrace(%s, 0x%X, 0x%X, 0x%X)\n",
157 map(request
), pid
, addr
, data
);
160 (void) mutex_lock(&pt_lock
);
162 if (request
== 0) { /* PTRACE_TRACEME, executed by traced process */
164 * Set stop-on-all-signals and nothing else.
165 * Turn off inherit-on-fork flag (grandchildren run away).
166 * Set ptrace-compatible flag.
168 char procname
[64]; /* /proc/<pid>/ctl */
171 MakeProcName(procname
, getpid());
172 (void) strcat(procname
, "/ctl");
173 if ((fd
= open(procname
, O_WRONLY
, 0)) < 0)
176 prfillset(&ctl
.arg
.signals
);
177 if (write(fd
, (char *)&ctl
, sizeof (long)+sizeof (sigset_t
))
178 != sizeof (long)+sizeof (sigset_t
))
181 premptyset(&ctl
.arg
.faults
);
182 if (write(fd
, (char *)&ctl
, sizeof (long)+sizeof (fltset_t
))
183 != sizeof (long)+sizeof (fltset_t
))
186 premptyset(&ctl
.arg
.syscalls
);
187 if (write(fd
, (char *)&ctl
, sizeof (long)+sizeof (sysset_t
))
188 != sizeof (long)+sizeof (sysset_t
))
191 premptyset(&ctl
.arg
.syscalls
);
192 if (write(fd
, (char *)&ctl
, sizeof (long)+sizeof (sysset_t
))
193 != sizeof (long)+sizeof (sysset_t
))
196 ctl
.arg
.flags
= PR_FORK
;
197 if (write(fd
, (char *)&ctl
, sizeof (long)+sizeof (long))
198 != sizeof (long)+sizeof (long))
201 ctl
.arg
.flags
= PR_PTRACE
;
202 if (write(fd
, (char *)&ctl
, sizeof (long)+sizeof (long))
203 != sizeof (long)+sizeof (long))
208 (void) mutex_unlock(&pt_lock
);
215 /* find the cstatus structure corresponding to pid */
216 if ((cp
= GrabProc(pid
)) == NULLCP
)
220 if (!(ps
->pr_flags
& PR_ISTOP
)) {
221 if (ProcUpdate(cp
) != 0) {
225 if (!(ps
->pr_flags
& PR_ISTOP
))
230 * Process the request.
234 case 1: /* PTRACE_PEEKTEXT */
235 case 2: /* PTRACE_PEEKDATA */
238 if (pread(cp
->asfd
, (char *)&data
, sizeof (data
), (off_t
)addr
)
240 (void) mutex_unlock(&pt_lock
);
245 case 3: /* PTRACE_PEEKUSER */
249 if (xaddr
>= REGADDR
&& xaddr
< REGADDR
+sizeof (gregset_t
))
250 xaddr
-= REGADDR
-U_REG
;
251 if (xaddr
>= U_PSARGS
&& xaddr
< U_PSARGS
+sizeof (UP
->u_psargs
))
253 if (xaddr
>= U_SIGNAL
&& xaddr
< U_SIGNAL
+sizeof (UP
->u_signal
))
255 if ((int)xaddr
>= 0 && xaddr
< U_END
) {
256 /* LINTED pointer alignment */
257 data
= *((int *)((caddr_t
)(&cp
->user
) + xaddr
));
258 (void) mutex_unlock(&pt_lock
);
263 case 4: /* PTRACE_POKETEXT */
264 case 5: /* PTRACE_POKEDATA */
268 if (xaddr
>= (unsigned)cp
->user
.u_reg
[REG_SP
] &&
269 xaddr
< (unsigned)cp
->user
.u_reg
[REG_SP
]+16*sizeof (int))
270 cp
->flags
|= CS_SETREGS
;
271 if (pwrite(cp
->asfd
, (char *)&data
, sizeof (data
), (off_t
)addr
)
273 (void) mutex_unlock(&pt_lock
);
278 case 6: /* PTRACE_POKEUSER */
282 if (xaddr
>= REGADDR
&& xaddr
< REGADDR
+sizeof (gregset_t
))
283 xaddr
-= REGADDR
-U_REG
;
284 if ((int)xaddr
>= U_REG
&& xaddr
< U_REG
+sizeof (gregset_t
)) {
285 int rx
= (xaddr
-U_REG
)/sizeof (greg_t
);
287 data
= (cp
->user
.u_reg
[REG_PS
] &
288 ~PSL_USERMASK
) | (data
& PSL_USERMASK
);
289 else if (rx
== REG_SP
|| rx
== REG_PC
|| rx
== REG_nPC
)
291 cp
->user
.u_reg
[rx
] = data
;
292 cp
->flags
|= CS_SETREGS
;
293 (void) mutex_unlock(&pt_lock
);
298 case 7: /* PTRACE_CONT */
299 case 9: /* PTRACE_SINGLESTEP */
303 if (cp
->flags
& CS_SETREGS
) {
307 ps
->pr_lwp
.pr_reg
[R_PSR
] = cp
->user
.u_reg
[REG_PSR
];
308 ps
->pr_lwp
.pr_reg
[R_PC
] = cp
->user
.u_reg
[REG_PC
];
309 ps
->pr_lwp
.pr_reg
[R_nPC
] = cp
->user
.u_reg
[REG_nPC
];
310 ps
->pr_lwp
.pr_reg
[R_Y
] = cp
->user
.u_reg
[REG_Y
];
311 ps
->pr_lwp
.pr_reg
[R_G1
] = cp
->user
.u_reg
[REG_G1
];
312 ps
->pr_lwp
.pr_reg
[R_G2
] = cp
->user
.u_reg
[REG_G2
];
313 ps
->pr_lwp
.pr_reg
[R_G3
] = cp
->user
.u_reg
[REG_G3
];
314 ps
->pr_lwp
.pr_reg
[R_G4
] = cp
->user
.u_reg
[REG_G4
];
315 ps
->pr_lwp
.pr_reg
[R_G5
] = cp
->user
.u_reg
[REG_G5
];
316 ps
->pr_lwp
.pr_reg
[R_G6
] = cp
->user
.u_reg
[REG_G6
];
317 ps
->pr_lwp
.pr_reg
[R_G7
] = cp
->user
.u_reg
[REG_G7
];
318 ps
->pr_lwp
.pr_reg
[R_O0
] = cp
->user
.u_reg
[REG_O0
];
319 ps
->pr_lwp
.pr_reg
[R_O1
] = cp
->user
.u_reg
[REG_O1
];
320 ps
->pr_lwp
.pr_reg
[R_O2
] = cp
->user
.u_reg
[REG_O2
];
321 ps
->pr_lwp
.pr_reg
[R_O3
] = cp
->user
.u_reg
[REG_O3
];
322 ps
->pr_lwp
.pr_reg
[R_O4
] = cp
->user
.u_reg
[REG_O4
];
323 ps
->pr_lwp
.pr_reg
[R_O5
] = cp
->user
.u_reg
[REG_O5
];
324 ps
->pr_lwp
.pr_reg
[R_O6
] = cp
->user
.u_reg
[REG_O6
];
325 ps
->pr_lwp
.pr_reg
[R_O7
] = cp
->user
.u_reg
[REG_O7
];
326 (void) pread(cp
->asfd
, (char *)&ps
->pr_lwp
.pr_reg
[R_L0
],
327 16*sizeof (int), (off_t
)cp
->user
.u_reg
[REG_SP
]);
329 iov
[0].iov_base
= (caddr_t
)&cmd
;
330 iov
[0].iov_len
= sizeof (long);
331 iov
[1].iov_base
= (caddr_t
)&ps
->pr_lwp
.pr_reg
[0];
332 iov
[1].iov_len
= sizeof (ps
->pr_lwp
.pr_reg
);
333 if (writev(cp
->ctlfd
, iov
, 2) < 0)
336 if (addr
!= 1 && /* new virtual address */
337 (addr
& ~03) != cp
->user
.u_reg
[REG_PC
]) {
338 runctl
[0] = PCSVADDR
;
339 runctl
[1] = (addr
& ~03);
340 if (write(cp
->ctlfd
, (char *)runctl
, 2*sizeof (long))
344 /* make data the current signal */
345 if (data
!= 0 && data
!= ps
->pr_lwp
.pr_cursig
) {
346 (void) memset((char *)&ctl
.arg
.siginfo
, 0,
348 ctl
.arg
.siginfo
.si_signo
= data
;
350 if (write(cp
->ctlfd
, (char *)&ctl
,
351 sizeof (long)+sizeof (siginfo_t
))
352 != sizeof (long)+sizeof (siginfo_t
))
360 runctl
[2] = (request
== 9)? PRSTEP
: 0;
361 if (write(cp
->ctlfd
, (char *)runctl
, 3*sizeof (long))
362 != 3*sizeof (long)) {
363 if (errno
== ENOENT
) {
364 /* current signal must have killed it */
366 (void) mutex_unlock(&pt_lock
);
371 (void) memset((char *)ps
, 0, sizeof (pstatus_t
));
373 (void) mutex_unlock(&pt_lock
);
377 case 8: /* PTRACE_KILL */
379 (void) memset((char *)&ctl
.arg
.siginfo
, 0, sizeof (siginfo_t
));
380 ctl
.arg
.siginfo
.si_signo
= SIGKILL
;
382 (void) write(cp
->ctlfd
, (char *)&ctl
,
383 sizeof (long)+sizeof (siginfo_t
));
384 (void) kill(pid
, SIGKILL
);
386 (void) mutex_unlock(&pt_lock
);
394 if (errno
== EAGAIN
) {
395 if (OpenProc(cp
) == 0)
401 (void) mutex_unlock(&pt_lock
);
405 (void) mutex_unlock(&pt_lock
);
410 * Find the cstatus structure corresponding to pid.
417 for (cp
= childp
; cp
!= NULLCP
; cp
= cp
->next
)
425 * Check every proc for existence, release those that are gone.
426 * Be careful about the linked list; ReleaseProc() changes it.
431 cstatus_t
*cp
= childp
;
433 while (cp
!= NULLCP
) {
434 cstatus_t
*next
= cp
->next
;
436 if (ProcUpdate(cp
) != 0)
443 * Utility for OpenProc().
446 Dupfd(int fd
, int dfd
)
449 * Make sure fd not one of 0, 1, or 2 to avoid stdio interference.
450 * Also, if dfd is greater than 2, dup fd to be exactly dfd.
452 if (dfd
> 2 || (0 <= fd
&& fd
<= 2)) {
453 if (dfd
> 2 && fd
!= dfd
)
458 dfd
= fcntl(fd
, F_DUPFD
, (intptr_t)dfd
);
464 * Mark filedescriptor close-on-exec.
465 * Should also be close-on-return-from-fork-in-child.
467 (void) fcntl(fd
, F_SETFD
, (intptr_t)1);
472 * Construct the /proc directory name: "/proc/<pid>"
473 * The name buffer passed by the caller must be large enough.
476 MakeProcName(char *procname
, pid_t pid
)
478 (void) sprintf(procname
, "/proc/%d", (int)pid
);
482 * Open/reopen the /proc/<pid> files.
485 OpenProc(cstatus_t
*cp
)
487 char procname
[64]; /* /proc/nnnnn/fname */
492 MakeProcName(procname
, cp
->pid
);
493 fname
= procname
+ strlen(procname
);
496 * Use exclusive-open only if this is the first open.
498 omode
= (cp
->asfd
> 0)? O_RDWR
: (O_RDWR
|O_EXCL
);
499 (void) strcpy(fname
, "/as");
500 if ((fd
= open(procname
, omode
, 0)) < 0 ||
501 (cp
->asfd
= Dupfd(fd
, cp
->asfd
)) < 0)
504 (void) strcpy(fname
, "/ctl");
505 if ((fd
= open(procname
, O_WRONLY
, 0)) < 0 ||
506 (cp
->ctlfd
= Dupfd(fd
, cp
->ctlfd
)) < 0)
509 (void) strcpy(fname
, "/status");
510 if ((fd
= open(procname
, O_RDONLY
, 0)) < 0 ||
511 (cp
->statusfd
= Dupfd(fd
, cp
->statusfd
)) < 0)
522 * Close the /proc/<pid> files.
525 CloseProc(cstatus_t
*cp
)
528 (void) close(cp
->asfd
);
530 (void) close(cp
->ctlfd
);
531 if (cp
->statusfd
> 0)
532 (void) close(cp
->statusfd
);
539 * Take control of a child process.
551 if ((cp
= FindProc(pid
)) != NULLCP
) /* already grabbed */
554 CheckAllProcs(); /* clean up before grabbing new process */
556 cp
= (cstatus_t
*)malloc(sizeof (cstatus_t
));
559 (void) memset((char *)cp
, 0, sizeof (cstatus_t
));
563 while (OpenProc(cp
) == 0) {
568 if (pread(cp
->statusfd
, (char *)&cp
->pstatus
,
569 sizeof (cp
->pstatus
), (off_t
)0) == sizeof (cp
->pstatus
) &&
570 cp
->pstatus
.pr_ppid
== ppid
&&
571 (cp
->pstatus
.pr_flags
& PR_PTRACE
) &&
572 write(cp
->ctlfd
, (char *)ctl
, 2*sizeof (long))
573 == 2*sizeof (long)) {
589 * Close the /proc/<pid> file, if open.
590 * Deallocate the memory used by the cstatus_t structure.
593 ReleaseProc(cstatus_t
*cp
)
602 for (pcp
= childp
; pcp
!= NULLCP
; pcp
= pcp
->next
) {
603 if (pcp
->next
== cp
) {
604 pcp
->next
= cp
->next
;
614 * Update process information from /proc.
615 * Return 0 on success, -1 on failure.
618 ProcUpdate(cstatus_t
*cp
)
620 pstatus_t
*ps
= &cp
->pstatus
;
622 if (cp
->flags
& CS_SETREGS
) {
626 ps
->pr_lwp
.pr_reg
[R_PSR
] = cp
->user
.u_reg
[REG_PSR
];
627 ps
->pr_lwp
.pr_reg
[R_PC
] = cp
->user
.u_reg
[REG_PC
];
628 ps
->pr_lwp
.pr_reg
[R_nPC
] = cp
->user
.u_reg
[REG_nPC
];
629 ps
->pr_lwp
.pr_reg
[R_Y
] = cp
->user
.u_reg
[REG_Y
];
630 ps
->pr_lwp
.pr_reg
[R_G1
] = cp
->user
.u_reg
[REG_G1
];
631 ps
->pr_lwp
.pr_reg
[R_G2
] = cp
->user
.u_reg
[REG_G2
];
632 ps
->pr_lwp
.pr_reg
[R_G3
] = cp
->user
.u_reg
[REG_G3
];
633 ps
->pr_lwp
.pr_reg
[R_G4
] = cp
->user
.u_reg
[REG_G4
];
634 ps
->pr_lwp
.pr_reg
[R_G5
] = cp
->user
.u_reg
[REG_G5
];
635 ps
->pr_lwp
.pr_reg
[R_G6
] = cp
->user
.u_reg
[REG_G6
];
636 ps
->pr_lwp
.pr_reg
[R_G7
] = cp
->user
.u_reg
[REG_G7
];
637 ps
->pr_lwp
.pr_reg
[R_O0
] = cp
->user
.u_reg
[REG_O0
];
638 ps
->pr_lwp
.pr_reg
[R_O1
] = cp
->user
.u_reg
[REG_O1
];
639 ps
->pr_lwp
.pr_reg
[R_O2
] = cp
->user
.u_reg
[REG_O2
];
640 ps
->pr_lwp
.pr_reg
[R_O3
] = cp
->user
.u_reg
[REG_O3
];
641 ps
->pr_lwp
.pr_reg
[R_O4
] = cp
->user
.u_reg
[REG_O4
];
642 ps
->pr_lwp
.pr_reg
[R_O5
] = cp
->user
.u_reg
[REG_O5
];
643 ps
->pr_lwp
.pr_reg
[R_O6
] = cp
->user
.u_reg
[REG_O6
];
644 ps
->pr_lwp
.pr_reg
[R_O7
] = cp
->user
.u_reg
[REG_O7
];
645 (void) pread(cp
->asfd
, (char *)&ps
->pr_lwp
.pr_reg
[R_L0
],
646 16*sizeof (int), (off_t
)cp
->user
.u_reg
[REG_SP
]);
648 iov
[0].iov_base
= (caddr_t
)&cmd
;
649 iov
[0].iov_len
= sizeof (long);
650 iov
[1].iov_base
= (caddr_t
)&ps
->pr_lwp
.pr_reg
[0];
651 iov
[1].iov_len
= sizeof (ps
->pr_lwp
.pr_reg
);
652 (void) writev(cp
->ctlfd
, iov
, 2);
653 cp
->flags
&= ~CS_SETREGS
;
656 while (pread(cp
->statusfd
, (char *)ps
, sizeof (*ps
), (off_t
)0) < 0) {
657 /* attempt to regain control */
658 if (errno
!= EINTR
&&
659 !(errno
== EAGAIN
&& OpenProc(cp
) == 0))
663 if (ps
->pr_flags
& PR_ISTOP
)
666 (void) memset((char *)ps
, 0, sizeof (pstatus_t
));
672 * Manufacture the contents of the fake u-block.
675 MakeUser(cstatus_t
*cp
)
677 pstatus_t
*ps
= &cp
->pstatus
;
679 cp
->user
.u_reg
[REG_PSR
] = ps
->pr_lwp
.pr_reg
[R_PSR
];
680 cp
->user
.u_reg
[REG_PC
] = ps
->pr_lwp
.pr_reg
[R_PC
];
681 cp
->user
.u_reg
[REG_nPC
] = ps
->pr_lwp
.pr_reg
[R_nPC
];
682 cp
->user
.u_reg
[REG_Y
] = ps
->pr_lwp
.pr_reg
[R_Y
];
683 cp
->user
.u_reg
[REG_G1
] = ps
->pr_lwp
.pr_reg
[R_G1
];
684 cp
->user
.u_reg
[REG_G2
] = ps
->pr_lwp
.pr_reg
[R_G2
];
685 cp
->user
.u_reg
[REG_G3
] = ps
->pr_lwp
.pr_reg
[R_G3
];
686 cp
->user
.u_reg
[REG_G4
] = ps
->pr_lwp
.pr_reg
[R_G4
];
687 cp
->user
.u_reg
[REG_G5
] = ps
->pr_lwp
.pr_reg
[R_G5
];
688 cp
->user
.u_reg
[REG_G6
] = ps
->pr_lwp
.pr_reg
[R_G6
];
689 cp
->user
.u_reg
[REG_G7
] = ps
->pr_lwp
.pr_reg
[R_G7
];
690 cp
->user
.u_reg
[REG_O0
] = ps
->pr_lwp
.pr_reg
[R_O0
];
691 cp
->user
.u_reg
[REG_O1
] = ps
->pr_lwp
.pr_reg
[R_O1
];
692 cp
->user
.u_reg
[REG_O2
] = ps
->pr_lwp
.pr_reg
[R_O2
];
693 cp
->user
.u_reg
[REG_O3
] = ps
->pr_lwp
.pr_reg
[R_O3
];
694 cp
->user
.u_reg
[REG_O4
] = ps
->pr_lwp
.pr_reg
[R_O4
];
695 cp
->user
.u_reg
[REG_O5
] = ps
->pr_lwp
.pr_reg
[R_O5
];
696 cp
->user
.u_reg
[REG_O6
] = ps
->pr_lwp
.pr_reg
[R_O6
];
697 cp
->user
.u_reg
[REG_O7
] = ps
->pr_lwp
.pr_reg
[R_O7
];
698 cp
->user
.u_ar0
= (greg_t
*)REGADDR
;
699 cp
->user
.u_code
= ps
->pr_lwp
.pr_info
.si_code
;
700 cp
->user
.u_addr
= ps
->pr_lwp
.pr_info
.si_addr
;
701 cp
->flags
&= ~(CS_PSARGS
|CS_SIGNAL
);
705 * Fetch the contents of u_psargs[].
708 GetPsargs(cstatus_t
*cp
)
710 char procname
[64]; /* /proc/<pid>/psinfo */
713 MakeProcName(procname
, cp
->pid
);
714 (void) strcat(procname
, "/psinfo");
715 if ((fd
= open(procname
, O_RDONLY
, 0)) < 0) {
716 (void) memset(cp
->user
.u_psargs
, 0, PSARGSZ
);
719 (void) pread(fd
, cp
->user
.u_psargs
, PSARGSZ
,
720 (off_t
)((psinfo_t
*)0)->pr_psargs
);
723 cp
->flags
|= CS_PSARGS
;
727 * Fetch the contents of u_signal[].
730 GetSignal(cstatus_t
*cp
)
732 char procname
[64]; /* /proc/<pid>/sigact */
734 struct sigaction action
[MAXSIG
];
737 MakeProcName(procname
, cp
->pid
);
738 (void) strcat(procname
, "/sigact");
739 (void) memset((char *)action
, 0, sizeof (action
));
740 if ((fd
= open(procname
, O_RDONLY
, 0)) >= 0) {
741 (void) read(fd
, (char *)action
, sizeof (action
));
744 for (i
= 0; i
< MAXSIG
; i
++)
745 cp
->user
.u_signal
[i
] = action
[i
].sa_handler
;
746 cp
->flags
|= CS_SIGNAL
;