diff options
Diffstat (limited to 'gdb/procfs.c')
-rw-r--r-- | gdb/procfs.c | 988 |
1 files changed, 988 insertions, 0 deletions
diff --git a/gdb/procfs.c b/gdb/procfs.c new file mode 100644 index 0000000..a277369 --- /dev/null +++ b/gdb/procfs.c @@ -0,0 +1,988 @@ +/* Machine independent support for SVR4 /proc (process file system) for GDB. + Copyright (C) 1991 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* N O T E S + +For information on the details of using /proc consult section proc(4) +in the UNIX System V Release 4 System Administrator's Reference Manual. + +The general register and floating point register sets are manipulated by +separate ioctl's. This file makes the assumption that if FP0_REGNUM is +defined, then support for the floating point register set is desired, +regardless of whether or not the actual target has floating point hardware. + + */ + + + +#include "param.h" + +#ifdef USE_PROC_FS /* Entire file goes away if not using /proc */ + +#include <stdio.h> +#include <sys/procfs.h> +#include <fcntl.h> +#include <errno.h> + +#include "defs.h" +#include "ansidecl.h" +#include "inferior.h" +#include "target.h" + +#ifndef PROC_NAME_FMT +#define PROC_NAME_FMT "/proc/%d" +#endif + +extern void EXFUN(supply_gregset, (gregset_t *gregsetp)); +extern void EXFUN(fill_gregset, (gregset_t *gresetp, int regno)); + +#if defined (FP0_REGNUM) +extern void EXFUN(supply_fpregset, (fpregset_t *fpregsetp)); +extern void EXFUN(fill_fpregset, (fpregset_t *fpresetp, int regno)); +#endif + +#if 1 /* FIXME: Gross and ugly hack to resolve coredep.c global */ +CORE_ADDR kernel_u_addr; +#endif + +/* All access to the inferior, either one started by gdb or one that has + been attached to, is controlled by an instance of a procinfo structure, + defined below. Since gdb currently only handles one inferior at a time, + the procinfo structure is statically allocated and only one exists at + any given time. */ + +struct procinfo { + int valid; /* Nonzero if pid, fd, & pathname are valid */ + int pid; /* Process ID of inferior */ + int fd; /* File descriptor for /proc entry */ + char *pathname; /* Pathname to /proc entry */ + int was_stopped; /* Nonzero if was stopped prior to attach */ + prrun_t prrun; /* Control state when it is run */ + prstatus_t prstatus; /* Current process status info */ + gregset_t gregset; /* General register set */ + fpregset_t fpregset; /* Floating point register set */ + fltset_t fltset; /* Current traced hardware fault set */ + sigset_t trace; /* Current traced signal set */ + sysset_t exitset; /* Current traced system call exit set */ + sysset_t entryset; /* Current traced system call entry set */ +} pi; + +/* Forward declarations of static functions so we don't have to worry + about ordering within this file. The EXFUN macro may be slightly + misleading. Should probably be called DCLFUN instead, or something + more intuitive, since it can be used for both static and external + definitions. */ + +static void EXFUN(proc_init_failed, (char *why)); +static int EXFUN(open_proc_file, (int pid)); +static void EXFUN(close_proc_file, (void)); +static void EXFUN(unconditionally_kill_inferior, (void)); + +/* + +GLOBAL FUNCTION + + ptrace -- override library version to force errors for /proc version + +SYNOPSIS + + int ptrace (int request, int pid, int arg3, int arg4) + +DESCRIPTION + + When gdb is configured to use /proc, it should not be calling + or otherwise attempting to use ptrace. In order to catch errors + where use of /proc is configured, but some routine is still calling + ptrace, we provide a local version of a function with that name + that does nothing but issue an error message. +*/ + +int +DEFUN(ptrace, (request, pid, arg3, arg4), + int request AND + int pid AND + int arg3 AND + int arg4) +{ + error ("internal error - there is a call to ptrace() somewhere"); + /*NOTREACHED*/ +} + +/* + +GLOBAL FUNCTION + + kill_inferior_fast -- kill inferior while gdb is exiting + +SYNOPSIS + + void kill_inferior_fast (void) + +DESCRIPTION + + This is used when GDB is exiting. It gives less chance of error. + +NOTES + + Don't attempt to kill attached inferiors since we may be called + when gdb is in the process of aborting, and killing the attached + inferior may be very anti-social. This is particularly true if we + were attached just so we could use the /proc facilities to get + detailed information about it's status. + +*/ + +void +DEFUN_VOID(kill_inferior_fast) +{ + if (inferior_pid != 0 && !attach_flag) + { + unconditionally_kill_inferior (); + } +} + +/* + +GLOBAL FUNCTION + + kill_inferior - kill any currently inferior + +SYNOPSIS + + void kill_inferior (void) + +DESCRIPTION + + Kill any current inferior. + +NOTES + + Kills even attached inferiors. Presumably the user has already + been prompted that the inferior is an attached one rather than + one started by gdb. (FIXME?) + +*/ + +void +DEFUN_VOID(kill_inferior) +{ + if (inferior_pid != 0) + { + unconditionally_kill_inferior (); + target_mourn_inferior (); + } +} + +/* + +LOCAL FUNCTION + + unconditionally_kill_inferior - terminate the inferior + +SYNOPSIS + + static void unconditionally_kill_inferior (void) + +DESCRIPTION + + Kill the current inferior. Should not be called until it + is at least tested that there is an inferior. + +NOTE + + A possibly useful enhancement would be to first try sending + the inferior a terminate signal, politely asking it to commit + suicide, before we murder it. + +*/ + +static void +DEFUN_VOID(unconditionally_kill_inferior) +{ + int signo; + + signo = SIGKILL; + (void) ioctl (pi.fd, PIOCKILL, &signo); + close_proc_file (); + wait ((int *) 0); +} + +/* + +GLOBAL FUNCTION + + child_xfer_memory -- copy data to or from inferior memory space + +SYNOPSIS + + int child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, + int dowrite, struct target_ops target) + +DESCRIPTION + + Copy LEN bytes to/from inferior's memory starting at MEMADDR + from/to debugger memory starting at MYADDR. Copy from inferior + if DOWRITE is zero or to inferior if DOWRITE is nonzero. + + Returns the length copied, which is either the LEN argument or + zero. This xfer function does not do partial moves, since child_ops + doesn't allow memory operations to cross below us in the target stack + anyway. + +NOTES + + The /proc interface makes this an almost trivial task. + */ + + +int +DEFUN(child_xfer_memory, (memaddr, myaddr, len, dowrite, target), + CORE_ADDR memaddr AND + char *myaddr AND + int len AND + int dowrite AND + struct target_ops target /* ignored */) +{ + int nbytes = 0; + + if (lseek (pi.fd, (off_t) memaddr, 0) == (off_t) memaddr) + { + if (dowrite) + { + nbytes = write (pi.fd, myaddr, len); + } + else + { + nbytes = read (pi.fd, myaddr, len); + } + if (nbytes < 0) + { + nbytes = 0; + } + } + return (nbytes); +} + +/* + +GLOBAL FUNCTION + + store_inferior_registers -- copy register values back to inferior + +SYNOPSIS + + void store_inferior_registers (int regno) + +DESCRIPTION + + Store our current register values back into the inferior. If + REGNO is -1 then store all the register, otherwise store just + the value specified by REGNO. + +NOTES + + If we are storing only a single register, we first have to get all + the current values from the process, overwrite the desired register + in the gregset with the one we want from gdb's registers, and then + send the whole set back to the process. For writing all the + registers, all we have to do is generate the gregset and send it to + the process. + + Also note that the process has to be stopped on an event of interest + for this to work, which basically means that it has to have been + run under the control of one of the other /proc ioctl calls and not + ptrace. Since we don't use ptrace anyway, we don't worry about this + fine point, but it is worth noting for future reference. + + Gdb is confused about what this function is supposed to return. + Some versions return a value, others return nothing. Some are + declared to return a value and actually return nothing. Gdb ignores + anything returned. (FIXME) + + */ + +void +DEFUN(store_inferior_registers, (regno), + int regno) +{ + if (regno != -1) + { + (void) ioctl (pi.fd, PIOCGREG, &pi.gregset); + } + fill_gregset (&pi.gregset, regno); + (void) ioctl (pi.fd, PIOCSREG, &pi.gregset); + +#if defined (FP0_REGNUM) + + /* Now repeat everything using the floating point register set, if the + target has floating point hardware. Since we ignore the returned value, + we'll never know whether it worked or not anyway. */ + + if (regno != -1) + { + (void) ioctl (pi.fd, PIOCGFPREG, &pi.fpregset); + } + fill_fpregset (&pi.fpregset, regno); + (void) ioctl (pi.fd, PIOCSFPREG, &pi.fpregset); + +#endif /* FP0_REGNUM */ + +} + +/* + +GLOBAL FUNCTION + + inferior_proc_init - initialize access to a /proc entry + +SYNOPSIS + + void inferior_proc_init (int pid) + +DESCRIPTION + + When gdb starts an inferior, this function is called in the parent + process immediately after the fork. It waits for the child to stop + on the return from the exec system call (the child itself takes care + of ensuring that this is set up), then sets up the set of signals + and faults that are to be traced. + +NOTES + + If proc_init_failed ever gets called, control returns to the command + processing loop via the standard error handling code. + */ + +void +DEFUN(inferior_proc_init, (int pid), + int pid) +{ + if (!open_proc_file (pid)) + { + proc_init_failed ("can't open process file"); + } + else + { + (void) memset (&pi.prrun, 0, sizeof (pi.prrun)); + prfillset (&pi.prrun.pr_trace); + prfillset (&pi.prrun.pr_fault); + prdelset (&pi.prrun.pr_fault, FLTPAGE); + if (ioctl (pi.fd, PIOCWSTOP, &pi.prstatus) < 0) + { + proc_init_failed ("PIOCWSTOP failed"); + } + else if (ioctl (pi.fd, PIOCSTRACE, &pi.prrun.pr_trace) < 0) + { + proc_init_failed ("PIOCSTRACE failed"); + } + else if (ioctl (pi.fd, PIOCSFAULT, &pi.prrun.pr_fault) < 0) + { + proc_init_failed ("PIOCSFAULT failed"); + } + } +} + +/* + +GLOBAL FUNCTION + + proc_set_exec_trap -- arrange for exec'd child to halt at startup + +SYNOPSIS + + void proc_set_exec_trap (void) + +DESCRIPTION + + This function is called in the child process when starting up + an inferior, prior to doing the exec of the actual inferior. + It sets the child process's exitset to make exit from the exec + system call an event of interest to stop on, and then simply + returns. The child does the exec, the system call returns, and + the child stops at the first instruction, ready for the gdb + parent process to take control of it. + +NOTE + + We need to use all local variables since the child may be sharing + it's data space with the parent, if vfork was used rather than + fork. + */ + +void +DEFUN_VOID(proc_set_exec_trap) +{ + sysset_t exitset; + auto char procname[32]; + int fd; + + (void) sprintf (procname, PROC_NAME_FMT, getpid ()); + if ((fd = open (procname, O_RDWR)) < 0) + { + perror (procname); + fflush (stderr); + _exit (127); + } + premptyset (&exitset); + praddset (&exitset, SYS_exec); + praddset (&exitset, SYS_execve); + if (ioctl (fd, PIOCSEXIT, &exitset) < 0) + { + perror (procname); + fflush (stderr); + _exit (127); + } +} + + +#ifdef ATTACH_DETACH + +/* + +GLOBAL FUNCTION + + attach -- attach to an already existing process + +SYNOPSIS + + int attach (int pid) + +DESCRIPTION + + Attach to an already existing process with the specified process + id. If the process is not already stopped, query whether to + stop it or not. + +NOTES + + The option of stopping at attach time is specific to the /proc + versions of gdb. Versions using ptrace force the attachee + to stop. + +*/ + +int +DEFUN(attach, (pid), + int pid) +{ + if (!open_proc_file (pid)) + { + perror_with_name (pi.pathname); + /* NOTREACHED */ + } + + /* Get current status of process and if it is not already stopped, + then stop it. Remember whether or not it was stopped when we first + examined it. */ + + if (ioctl (pi.fd, PIOCSTATUS, &pi.prstatus) < 0) + { + print_sys_errmsg (pi.pathname, errno); + close_proc_file (); + error ("PIOCSTATUS failed"); + } + if (pi.prstatus.pr_flags & (PR_STOPPED | PR_ISTOP)) + { + pi.was_stopped = 1; + } + else + { + pi.was_stopped = 0; + if (query ("Process is currently running, stop it? ")) + { + if (ioctl (pi.fd, PIOCSTOP, &pi.prstatus) < 0) + { + print_sys_errmsg (pi.pathname, errno); + close_proc_file (); + error ("PIOCSTOP failed"); + } + } + } + + /* Remember some things about the inferior that we will, or might, change + so that we can restore them when we detach. */ + + (void) ioctl (pi.fd, PIOCGTRACE, &pi.trace); + (void) ioctl (pi.fd, PIOCGFAULT, &pi.fltset); + (void) ioctl (pi.fd, PIOCGENTRY, &pi.entryset); + (void) ioctl (pi.fd, PIOCGEXIT, &pi.exitset); + + /* Set up trace and fault sets, as gdb expects them. */ + + (void) memset (&pi.prrun, 0, sizeof (pi.prrun)); + prfillset (&pi.prrun.pr_trace); + prfillset (&pi.prrun.pr_fault); + prdelset (&pi.prrun.pr_fault, FLTPAGE); + if (ioctl (pi.fd, PIOCSFAULT, &pi.prrun.pr_fault)) + { + print_sys_errmsg ("PIOCSFAULT failed"); + } + if (ioctl (pi.fd, PIOCSTRACE, &pi.prrun.pr_trace)) + { + print_sys_errmsg ("PIOCSTRACE failed"); + } + attach_flag = 1; + return (pid); +} + +/* + +GLOBAL FUNCTION + + detach -- detach from an attached-to process + +SYNOPSIS + + void detach (int signal) + +DESCRIPTION + + Detach from the current attachee. + + If signal is non-zero, the attachee is started running again and sent + the specified signal. + + If signal is zero and the attachee was not already stopped when we + attached to it, then we make it runnable again when we detach. + + Otherwise, we query whether or not to make the attachee runnable + again, since we may simply want to leave it in the state it was in + when we attached. + + We report any problems, but do not consider them errors, since we + MUST detach even if some things don't seem to go right. This may not + be the ideal situation. (FIXME). + */ + +void +DEFUN(detach, (signal), + int signal) +{ + if (signal) + { + struct siginfo siginfo; + siginfo.si_signo = signal; + siginfo.si_code = 0; + siginfo.si_errno = 0; + if (ioctl (pi.fd, PIOCSSIG, &siginfo) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSSIG failed.\n"); + } + } + if (ioctl (pi.fd, PIOCSEXIT, &pi.exitset) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSEXIT failed.\n"); + } + if (ioctl (pi.fd, PIOCSENTRY, &pi.entryset) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSENTRY failed.\n"); + } + if (ioctl (pi.fd, PIOCSTRACE, &pi.trace) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSTRACE failed.\n"); + } + if (ioctl (pi.fd, PIOCSFAULT, &pi.fltset) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSFAULT failed.\n"); + } + if (ioctl (pi.fd, PIOCSTATUS, &pi.prstatus) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSTATUS failed.\n"); + } + else + { + if (signal || (pi.prstatus.pr_flags & (PR_STOPPED | PR_ISTOP))) + { + if (signal || !pi.was_stopped || + query ("Was stopped when attached, make it runnable again? ")) + { + (void) memset (&pi.prrun, 0, sizeof (pi.prrun)); + pi.prrun.pr_flags = PRCFAULT; + if (ioctl (pi.fd, PIOCRUN, &pi.prrun)) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCRUN failed.\n"); + } + } + } + } + close_proc_file (); + attach_flag = 0; +} + +/* + +GLOBAL FUNCTION + + proc_wait -- emulate wait() as much as possible + +SYNOPSIS + + int proc_wait (int *statloc) + +DESCRIPTION + + Try to emulate wait() as much as possible. Not sure why we can't + just use wait(), but it seems to have problems when applied to a + process being controlled with the /proc interface. + +NOTES + + We have a race problem here with no obvious solution. We need to let + the inferior run until it stops on an event of interest, which means + that we need to use the PIOCWSTOP ioctl. However, we cannot use this + ioctl if the process is already stopped on something that is not an + event of interest, or the call will hang indefinitely. Thus we first + use PIOCSTATUS to see if the process is not stopped. If not, then we + use PIOCWSTOP. But during the window between the two, if the process + stops for any reason that is not an event of interest (such as a job + control signal) then gdb will hang. One possible workaround is to set + an alarm to wake up every minute of so and check to see if the process + is still running, and if so, then reissue the PIOCWSTOP. But this is + a real kludge, so has not been implemented. FIXME: investigate + alternatives. + + FIXME: Investigate why wait() seems to have problems with programs + being control by /proc routines. + + */ + +int +DEFUN(proc_wait, (statloc), + int *statloc) +{ + short what; + short why; + int statval = 0; + int checkerr = 0; + int rtnval = -1; + + if (ioctl (pi.fd, PIOCSTATUS, &pi.prstatus) < 0) + { + checkerr++; + } + else if (!(pi.prstatus.pr_flags & (PR_STOPPED | PR_ISTOP))) + { + if (ioctl (pi.fd, PIOCWSTOP, &pi.prstatus) < 0) + { + checkerr++; + } + } + if (checkerr) + { + if (errno == ENOENT) + { + rtnval = wait (&statval); + if (rtnval != inferior_pid) + { + error ("PIOCWSTOP, wait failed, returned %d", rtnval); + /* NOTREACHED */ + } + } + else + { + print_sys_errmsg (pi.pathname, errno); + error ("PIOCSTATUS or PIOCWSTOP failed."); + /* NOTREACHED */ + } + } + else if (pi.prstatus.pr_flags & (PR_STOPPED | PR_ISTOP)) + { + rtnval = pi.prstatus.pr_pid; + why = pi.prstatus.pr_why; + what = pi.prstatus.pr_what; + if (why == PR_SIGNALLED) + { + statval = (what << 8) | 0177; + } + else if ((why == PR_SYSEXIT) && + (what == SYS_exec || what == SYS_execve)) + { + statval = (SIGTRAP << 8) | 0177; + } + else if (why == PR_REQUESTED) + { + statval = (SIGSTOP << 8) | 0177; + } + else if (why == PR_JOBCONTROL) + { + statval = (what << 8) | 0177; + } + else if (why == PR_FAULTED) + { + switch (what) + { + case FLTPRIV: + case FLTILL: + statval = (SIGILL << 8) | 0177; + break; + case FLTBPT: + case FLTTRACE: + statval = (SIGTRAP << 8) | 0177; + break; + case FLTSTACK: + case FLTACCESS: + case FLTBOUNDS: + statval = (SIGSEGV << 8) | 0177; + break; + case FLTIOVF: + case FLTIZDIV: + case FLTFPE: + statval = (SIGFPE << 8) | 0177; + break; + case FLTPAGE: /* Recoverable page fault */ + default: + rtnval = -1; + error ("PIOCWSTOP, unknown why %d, what %d", why, what); + /* NOTREACHED */ + } + } + else + { + rtnval = -1; + error ("PIOCWSTOP, unknown why %d, what %d", why, what); + /* NOTREACHED */ + } + } + else + { + error ("PIOCWSTOP, stopped for unknown/unhandled reason, flags %#x", + pi.prstatus.pr_flags); + /* NOTREACHED */ + } + if (statloc) + { + *statloc = statval; + } + return (rtnval); +} + +/* + +GLOBAL FUNCTION + + child_resume -- resume execution of the inferior process + +SYNOPSIS + + void child_resume (int step, int signal) + +DESCRIPTION + + Resume execution of the inferior process. If STEP is nozero, then + just single step it. If SIGNAL is nonzero, restart it with that + signal activated. + +NOTE + + It may not be absolutely necessary to specify the PC value for + restarting, but to be safe we use the value that gdb considers + to be current. One case where this might be necessary is if the + user explicitly changes the PC value that gdb considers to be + current. FIXME: Investigate if this is necessary or not. + */ + +void +DEFUN(child_resume, (step, signal), + int step AND + int signal) +{ + errno = 0; + pi.prrun.pr_flags = PRSVADDR | PRSTRACE | PRSFAULT | PRCFAULT; + pi.prrun.pr_vaddr = (caddr_t) *(int *) ®isters[REGISTER_BYTE (PC_REGNUM)]; + if (signal) + { + if (signal != pi.prstatus.pr_cursig) + { + struct siginfo siginfo; + siginfo.si_signo = signal; + siginfo.si_code = 0; + siginfo.si_errno = 0; + (void) ioctl (pi.fd, PIOCSSIG, &siginfo); + } + } + else + { + pi.prrun.pr_flags |= PRCSIG; + } + if (step) + { + pi.prrun.pr_flags |= PRSTEP; + } + if (ioctl (pi.fd, PIOCRUN, &pi.prrun) != 0) + { + perror_with_name (pi.pathname); + /* NOTREACHED */ + } +} + +/* + +GLOBAL FUNCTION + + fetch_inferior_registers -- fetch current registers from inferior + +SYNOPSIS + + void fetch_inferior_registers (void) + +DESCRIPTION + + Read the current values of the inferior's registers, both the + general register set and floating point registers (if supported) + and update gdb's idea of their current values. + +*/ + +void +DEFUN_VOID(fetch_inferior_registers) +{ + if (ioctl (pi.fd, PIOCGREG, &pi.gregset) != -1) + { + supply_gregset (&pi.gregset); + } +#if defined (FP0_REGNUM) + if (ioctl (pi.fd, PIOCGFPREG, &pi.fpregset) != -1) + { + supply_fpregset (&pi.fpregset); + } +#endif +} + +#endif /* ATTACH_DETACH */ + +/* + +LOCAL FUNCTION + + proc_init_failed - called whenever /proc access initialization fails + +SYNOPSIS + + static void proc_init_failed (char *why) + +DESCRIPTION + + This function is called whenever initialization of access to a /proc + entry fails. It prints a suitable error message, does some cleanup, + and then invokes the standard error processing routine which dumps + us back into the command loop. + */ + +static void +DEFUN(proc_init_failed, (why), + char *why) +{ + print_sys_errmsg (pi.pathname, errno); + (void) kill (pi.pid, SIGKILL); + close_proc_file (); + error (why); + /* NOTREACHED */ +} + +/* + +LOCAL FUNCTION + + close_proc_file - close any currently open /proc entry + +SYNOPSIS + + static void close_proc_file (void) + +DESCRIPTION + + Close any currently open /proc entry and mark the process information + entry as invalid. In order to ensure that we don't try to reuse any + stale information, the pid, fd, and pathnames are explicitly + invalidated, which may be overkill. + + */ + +static void +DEFUN_VOID(close_proc_file) +{ + pi.pid = 0; + if (pi.valid) + { + (void) close (pi.fd); + } + pi.fd = -1; + if (pi.pathname) + { + free (pi.pathname); + pi.pathname = NULL; + } + pi.valid = 0; +} + +/* + +LOCAL FUNCTION + + open_proc_file - open a /proc entry for a given process id + +SYNOPSIS + + static int open_proc_file (pid) + +DESCRIPTION + + Given a process id, close the existing open /proc entry (if any) + and open one for the new process id. Once it is open, then + mark the local process information structure as valid, which + guarantees that the pid, fd, and pathname fields match an open + /proc entry. Returns zero if the open fails, nonzero otherwise. + + Note that the pathname is left intact, even when the open fails, + so that callers can use it to construct meaningful error messages + rather than just "file open failed". + */ + +static int +DEFUN(open_proc_file, (pid), + int pid) +{ + pi.valid = 0; + if (pi.valid) + { + (void) close (pi.fd); + } + if (pi.pathname == NULL) + { + pi.pathname = xmalloc (32); + } + sprintf (pi.pathname, PROC_NAME_FMT, pid); + if ((pi.fd = open (pi.pathname, O_RDWR)) >= 0) + { + pi.valid = 1; + pi.pid = pid; + } + return (pi.valid); +} + +#endif /* USE_PROC_FS */ |