1/*
2 * dproc.c -- Darwin process access functions for libproc-based lsof
3 */
4
5
6/*
7 * Portions Copyright 2005-2007 Apple Inc.  All rights reserved.
8 *
9 * Copyright 2005 Purdue Research Foundation, West Lafayette, Indiana
10 * 47907.  All rights reserved.
11 *
12 * Written by Allan Nathanson, Apple Inc., and Victor A. Abell, Purdue
13 * University.
14 *
15 * This software is not subject to any license of the American Telephone
16 * and Telegraph Company or the Regents of the University of California.
17 *
18 * Permission is granted to anyone to use this software for any purpose on
19 * any computer system, and to alter it and redistribute it freely, subject
20 * to the following restrictions:
21 *
22 * 1. Neither the authors, nor Apple Inc. nor Purdue University are
23 *    responsible for any consequences of the use of this software.
24 *
25 * 2. The origin of this software must not be misrepresented, either
26 *    by explicit claim or by omission.  Credit to the authors, Apple
27 *    Inc. and Purdue University must appear in documentation and sources.
28 *    and sources.
29 *
30 * 3. Altered versions must be plainly marked as such, and must not be
31 *    misrepresented as being the original software.
32 *
33 * 4. This notice may not be removed or altered.
34 */
35
36
37#ifndef lint
38static char copyright[] =
39"@(#) Copyright 2005-2007 Apple Inc. and Purdue Research Foundation.\nAll rights reserved.\n";
40static char *rcsid = "$Id: dproc.c,v 1.8 2012/04/10 16:41:04 abe Exp abe $";
41#endif
42
43#include "lsof.h"
44
45
46/*
47 * Local definitions
48 */
49
50#define	PIDS_INCR	(sizeof(int) * 32)	/* PID space increment */
51#define	VIPS_INCR	16			/* Vips space increment */
52
53#if	DARWINV>=900
54#define	THREADS_INCR	(sizeof(uint64_t) * 32)	/* Threads space increment */
55#endif	/* DARWINV>=900 */
56
57#ifdef	PROC_PIDLISTFILEPORTS
58#define	FILEPORTS_INCR	(sizeof(struct proc_fileportinfo) * 32)	/* Fileports space increment */
59#endif	/* PROC_PIDLISTFILEPORTS */
60
61/*
62 * Local static variables
63 */
64
65static struct proc_fdinfo *Fds = (struct proc_fdinfo *)NULL;
66						/* FD buffer */
67static int NbPids = 0;				/* bytes allocated to Pids */
68static int NbFds = 0;				/* bytes allocated to FDs */
69static int *Pids = (int *)NULL;			/* PID buffer */
70
71#if	DARWINV>=900
72static int NbThreads = 0;			/* Threads bytes allocated */
73static uint64_t *Threads = (uint64_t *)NULL;	/* Thread buffer */
74#endif	/* DARWINV>=900 */
75
76#ifdef	PROC_PIDLISTFILEPORTS
77static struct proc_fileportinfo *Fps = (struct proc_fileportinfo *)NULL;
78						/* fileport buffer */
79static int NbFps = 0;				/* bytes allocated to fileports */
80#endif	/* PROC_PIDLISTFILEPORTS */
81
82/*
83 * Local structure definitions
84 */
85
86static struct vips_info {
87	dev_t	dev;
88	ino_t	ino;
89} *Vips	= (struct vips_info *)NULL;		/* recorded vnodes */
90static int NbVips = 0;				/* bytes allocated to Vips */
91static int NVips = 0;				/* entries allocated to Vips */
92
93
94/*
95 * Local function prototypes
96 */
97_PROTOTYPE(static void enter_vn_text,(struct vnode_info_path *vip, int *n));
98_PROTOTYPE(static void process_fds,(int pid, uint32_t n, int ckscko));
99_PROTOTYPE(static void process_text,(int pid));
100
101#if	DARWINV>=900
102_PROTOTYPE(static void process_threads,(int pid, uint32_t n));
103#endif	/* DARWINV>=900 */
104
105#ifdef	PROC_PIDLISTFILEPORTS
106_PROTOTYPE(static void process_fileports,(int pid, int ckscko));
107#endif	/* PROC_PIDLISTFILEPORTS */
108
109/*
110 * enter_vn_text() -- enter vnode information text reference
111 */
112
113static void
114enter_vn_text(vip, n)
115	struct vnode_info_path *vip;	/* vnode info */
116	int *n;				/* number of vips[] entries in use */
117{
118	int i;
119/*
120 * Ignore the request if the vnode information has already been entered.
121 */
122	for (i = 0; i < *n; i++) {
123	    if ((vip->vip_vi.vi_stat.vst_dev == Vips[i].dev)
124	    &&  (vip->vip_vi.vi_stat.vst_ino == Vips[i].ino))
125	    {
126		return;
127	    }
128	}
129/*
130 * Save the text file information.
131 */
132	alloc_lfile(" txt", -1);
133	Cfp = (struct file *)NULL;
134	(void) enter_vnode_info(vip);
135	if (Lf->sf)
136	    link_lfile();
137/*
138 * Record the entry of the vnode information.
139 */
140	if (i >= NVips) {
141
142	/*
143	 * Allocate space for recording the vnode information.
144	 */
145	    NVips += VIPS_INCR;
146	    NbVips += (int)(VIPS_INCR * sizeof(struct vips_info));
147	    if (!Vips)
148		Vips = (struct vips_info *)malloc((MALLOC_S)NbVips);
149	    else
150		Vips = (struct vips_info *)realloc((MALLOC_P *)Vips,
151						   (MALLOC_S)NbVips);
152	    if (!Vips) {
153		(void) fprintf(stderr, "%s: PID %d: no text recording space\n",
154		    Pn, Lp->pid);
155		Exit(1);
156	    }
157	}
158/*
159 * Record the vnode information.
160 */
161	Vips[*n].dev = vip->vip_vi.vi_stat.vst_dev;
162	Vips[*n].ino = vip->vip_vi.vi_stat.vst_ino;
163	(*n)++;
164}
165
166
167/*
168 * gather_proc_info() -- gather process information
169 */
170
171void
172gather_proc_info()
173{
174	short cckreg;			/* conditional status of regular file
175					 * checking:
176					 *     0 = unconditionally check
177					 *     1 = conditionally check */
178	short ckscko;			/* socket file only checking status:
179					 *     0 = none
180					 *     1 = check only socket files,
181					 *	   including TCP and UDP
182					 *	   streams with eXPORT data,
183					 *	   where supported */
184	int cre, cres, ef, i, nb, np, pid;
185	short pss, sf;
186	struct proc_taskallinfo tai;
187	struct proc_vnodepathinfo vpi;
188/*
189 * Define socket and regular file conditional processing flags.
190 *
191 * If only socket files have been selected, or socket files have been
192 * selected, ANDed with other selection options, enable the skipping of
193 * regular files.
194 *
195 * If socket files and some process options have been selected, enable
196 * conditional skipping of regular file; i.e., regular files will be skipped
197 * unless they belong to a process selected by one of the specified options.
198 */
199	if (Selflags & SELNW) {
200
201	/*
202	 * Some network files selection options have been specified.
203	 */
204	    if (Fand || !(Selflags & ~SELNW)) {
205
206	    /*
207	     * Selection ANDing or only network file options have been
208	     * specified, so set unconditional skipping of regular files
209	     * and socket file only checking.
210	     */
211		cckreg = 0;
212		ckscko = 1;
213	    } else {
214
215	    /*
216	     * If ORed file selection options have been specified, or no
217	     * ORed process selection options have been specified, enable
218	     * unconditional file checking and clear socket file only
219	     * checking.
220	     *
221	     * If only ORed process selection options have been specified,
222	     * enable conditional file skipping and socket file only checking.
223	     */
224		if ((Selflags & SELFILE) || !(Selflags & SELPROC))
225		    cckreg = ckscko = 0;
226		else
227		    cckreg = ckscko = 1;
228	    }
229	} else {
230
231	/*
232	 * No network file selection options were specified.  Enable
233	 * unconditional file checking and clear socket file only checking.
234	 */
235	    cckreg = ckscko = 0;
236	}
237/*
238 * Determine how many bytes are needed to contain the PIDs on the system;
239 * make sure sufficient buffer space is allocated to hold them (and a few
240 * extra); then read the list of PIDs.
241 */
242	if ((nb = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) {
243	    (void) fprintf(stderr, "%s: can't get PID byte count: %s\n",
244		Pn, strerror(errno));
245	    Exit(1);
246	}
247	if (nb > NbPids) {
248	    while (nb > NbPids) {
249		NbPids += PIDS_INCR;
250	    }
251	    if (!Pids)
252		Pids = (int *)malloc((MALLOC_S)NbPids);
253	    else
254		Pids = (int *)realloc((MALLOC_P *)Pids, (MALLOC_S)NbPids);
255	    if (!Pids) {
256		(void) fprintf(stderr,
257		    "%s: can't allocate space for %d PIDs\n", Pn,
258		    (int)(NbPids / sizeof(int *)));
259		Exit(1);
260	    }
261	}
262/*
263 * Get the list of PIDs.
264 */
265	for (ef = 0; !ef;) {
266	    if ((nb = proc_listpids(PROC_ALL_PIDS, 0, Pids, NbPids)) <= 0) {
267		(void) fprintf(stderr, "%s: can't get list of PIDs: %s\n",
268		    Pn, strerror(errno));
269		Exit(1);
270	    }
271
272	    if ((nb + sizeof(int)) < NbPids) {
273
274	    /*
275	     * There is room in the buffer for at least one more PID.
276	     */
277		np = nb / sizeof(int);
278		ef = 1;
279	    } else {
280
281	    /*
282	     * The PID buffer must be enlarged.
283	     */
284		NbPids += PIDS_INCR;
285		Pids = (int *)realloc((MALLOC_P *)Pids, (MALLOC_S)NbPids);
286		if (!Pids) {
287		    (void) fprintf(stderr,
288			"%s: can't allocate space for %d PIDs\n", Pn,
289			(int)(NbPids / sizeof(int *)));
290		    Exit(1);
291		}
292	    }
293	}
294/*
295 * Loop through the identified processes.
296 */
297	for (i = 0; i < np; i++) {
298	    if (!(pid = Pids[i]))
299		continue;
300	    nb = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &tai, sizeof(tai));
301	    if (nb <= 0) {
302		if ((errno == EPERM) || (errno == ESRCH))
303		    continue;
304		if (!Fwarn) {
305		    (void) fprintf(stderr, "%s: PID %d information error: %s\n",
306			Pn, pid, strerror(errno));
307		}
308		continue;
309	    } else if (nb < sizeof(tai)) {
310		(void) fprintf(stderr,
311		    "%s: PID %d: proc_pidinfo(PROC_PIDTASKALLINFO);\n",
312		    Pn, pid);
313		(void) fprintf(stderr,
314		    "      too few bytes; expected %ld, got %d\n",
315		    sizeof(tai), nb);
316		Exit(1);
317	    }
318	/*
319	 * Check for process or command exclusion.
320	 */
321	    if (is_proc_excl((int)pid, (int)tai.pbsd.pbi_pgid,
322			     (UID_ARG)tai.pbsd.pbi_uid, &pss, &sf))
323	    {
324		continue;
325	    }
326	    tai.pbsd.pbi_comm[sizeof(tai.pbsd.pbi_comm) - 1] = '\0';
327	    if (is_cmd_excl(tai.pbsd.pbi_comm, &pss, &sf))
328		continue;
329	    if (tai.pbsd.pbi_name[0]) {
330		tai.pbsd.pbi_name[sizeof(tai.pbsd.pbi_name) - 1] = '\0';
331		if (is_cmd_excl(tai.pbsd.pbi_name, &pss, &sf))
332		    continue;
333	    }
334	    if (cckreg) {
335
336	    /*
337	     * If conditional checking of regular files is enabled, enable
338	     * socket file only checking, based on the process' selection
339	     * status.
340	     */
341		ckscko = (sf & SELPROC) ? 0 : 1;
342	    }
343	/*
344	 * Get root and current directory information.
345	 */
346	    if (!ckscko) {
347		nb = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi,
348		     sizeof(vpi));
349		if (nb <= 0) {
350		    cre = errno;
351		    cres = 1;
352		} else if (nb < sizeof(vpi)) {
353		    (void) fprintf(stderr,
354			"%s: PID %d: proc_pidinfo(PROC_PIDVNODEPATHINFO);\n",
355			Pn, pid);
356		    (void) fprintf(stderr,
357			"      too few bytes; expected %ld, got %d\n",
358			sizeof(vpi), nb);
359		    Exit(1);
360		} else
361		    cres = 0;
362	    }
363	/*
364	 * Allocate local process space.
365	 */
366	    alloc_lproc((int)pid, (int)tai.pbsd.pbi_pgid,
367		(int)tai.pbsd.pbi_ppid, (UID_ARG)tai.pbsd.pbi_uid,
368		(tai.pbsd.pbi_name[0] != '\0') ? tai.pbsd.pbi_name
369					       : tai.pbsd.pbi_comm,
370		(int)pss, (int)sf);
371	    Plf = (struct lfile *)NULL;
372	/*
373	 * Save current working directory information.
374	 */
375	    if (!ckscko) {
376		if (cres || vpi.pvi_cdir.vip_path[0]) {
377		    alloc_lfile(CWD, -1);
378		    Cfp = (struct file *)NULL;
379		    if (cres) {
380
381		    /*
382		     * If the CWD|RTD information access error is ESRCH,
383		     * ignore it; otherwise report the error's message in the
384		     * CWD's NAME  column.
385		     */
386			if (cre != ESRCH) {
387			    (void) snpf(Namech, Namechl, "%s|%s info error: %s",
388				CWD + 1, RTD + 1, strerror(cre));
389			    Namech[Namechl - 1] = '\0';
390			    enter_nm(Namech);
391			    if (Lf->sf)
392				link_lfile();
393			}
394		    } else {
395			(void) enter_vnode_info(&vpi.pvi_cdir);
396			if (Lf->sf)
397			    link_lfile();
398		    }
399		}
400	    }
401	/*
402	 * Save root directory information.
403	 */
404	    if (!ckscko) {
405		if (!cres && vpi.pvi_rdir.vip_path[0]) {
406		    alloc_lfile(RTD, -1);
407		    Cfp = (struct file *)NULL;
408		    (void) enter_vnode_info(&vpi.pvi_rdir);
409		    if (Lf->sf)
410			link_lfile();
411		}
412	    }
413
414#if	DARWINV>=900
415	/*
416	 * Check for per-thread current working directories
417	 */
418	    if (!ckscko) {
419		if (tai.pbsd.pbi_flags & PROC_FLAG_THCWD) {
420	    	    (void) process_threads(pid, tai.ptinfo.pti_threadnum);
421		}
422	    }
423#endif	/* DARWINV>=900 */
424
425	/*
426	 * Print text file information.
427	 */
428	    if (!ckscko)
429		(void) process_text(pid);
430
431#ifdef	PROC_PIDLISTFILEPORTS
432	/*
433	 * Loop through the fileports
434	 */
435	    (void) process_fileports(pid, ckscko);
436#endif	/* PROC_PIDLISTFILEPORTS */
437
438	/*
439	 * Loop through the file descriptors.
440	 */
441	    (void) process_fds(pid, tai.pbsd.pbi_nfiles, ckscko);
442	/*
443	 * Examine results.
444	 */
445	    if (examine_lproc())
446		return;
447	}
448}
449
450
451/*
452 * initialize() -- perform all initialization
453 */
454
455void
456initialize()
457{
458}
459
460
461/*
462 * process_fds() -- process file descriptors
463 */
464
465static void
466process_fds(pid, n, ckscko)
467	int pid;			/* PID of interest */
468	uint32_t n;			/* max FDs */
469	int ckscko;			/* check socket files only */
470{
471	int i, isock, nb, nf;
472	struct proc_fdinfo *fdp;
473/*
474 * Make sure an FD buffer has been allocated.
475 */
476	if (!Fds) {
477	    NbFds = sizeof(struct proc_fdinfo) * n;
478	    Fds = (struct proc_fdinfo *)malloc((MALLOC_S)NbFds);
479	} else if (NbFds < sizeof(struct proc_fdinfo) * n) {
480
481	/*
482	 * More proc_fdinfo space is required.  Allocate it.
483	 */
484	    NbFds = sizeof(struct proc_fdinfo) * n;
485	    Fds = (struct proc_fdinfo *)realloc((MALLOC_P *)Fds,
486						(MALLOC_S)NbFds);
487	}
488	if (!Fds) {
489	    (void) fprintf(stderr,
490		"%s: PID %d: can't allocate space for %d FDs\n",
491		Pn, pid, (int)(NbFds / sizeof(struct proc_fdinfo)));
492	    Exit(1);
493	}
494/*
495 * Get FD information for the process.
496 */
497	nb = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, Fds, NbFds);
498	if (nb <= 0) {
499	    if (errno == ESRCH) {
500
501	    /*
502	     * Quit if no FD information is available for the process.
503	     */
504		return;
505	    }
506	/*
507	 * Make a dummy file entry with an error message in its NAME column.
508	 */
509	    alloc_lfile(" err", -1);
510	    (void) snpf(Namech, Namechl, "FD info error: %s", strerror(errno));
511	    Namech[Namechl - 1] = '\0';
512	    enter_nm(Namech);
513	    if (Lf->sf)
514		link_lfile();
515	    return;
516	}
517	nf = (int)(nb / sizeof(struct proc_fdinfo));
518/*
519 * Loop through the file descriptors.
520 */
521	for (i = 0; i < nf; i++) {
522	    fdp = &Fds[i];
523	    alloc_lfile(NULL, (int)fdp->proc_fd);
524	/*
525	 * Process the file by its type.
526	 */
527	    isock = 0;
528	    switch (fdp->proc_fdtype) {
529	    case PROX_FDTYPE_ATALK:
530		if (!ckscko)
531		    (void) process_atalk(pid, fdp->proc_fd);
532		break;
533	    case PROX_FDTYPE_FSEVENTS:
534		if (!ckscko)
535		    (void) process_fsevents(pid, fdp->proc_fd);
536		break;
537	    case PROX_FDTYPE_KQUEUE:
538		if (!ckscko)
539		    (void) process_kqueue(pid, fdp->proc_fd);
540		break;
541	    case PROX_FDTYPE_PIPE:
542		if (!ckscko)
543		    (void) process_pipe(pid, fdp->proc_fd);
544		break;
545	    case PROX_FDTYPE_PSEM:
546		if (!ckscko)
547		    (void) process_psem(pid, fdp->proc_fd);
548		break;
549	    case PROX_FDTYPE_SOCKET:
550		(void) process_socket(pid, fdp->proc_fd);
551		isock = 1;
552		break;
553	    case PROX_FDTYPE_PSHM:
554		(void) process_pshm(pid, fdp->proc_fd);
555		break;
556	    case PROX_FDTYPE_VNODE:
557		(void) process_vnode(pid, fdp->proc_fd);
558		break;
559	    default:
560		(void) snpf(Namech, Namechl - 1, "unknown file type: %d",
561		    fdp->proc_fdtype);
562		Namech[Namechl - 1] = '\0';
563		(void) enter_nm(Namech);
564		break;
565	    }
566	    if (Lf->sf) {
567		if (!ckscko || isock)
568		    link_lfile();
569	    }
570	}
571}
572
573
574#ifdef	PROC_PIDLISTFILEPORTS
575/*
576 * process_fileports() -- process fileports
577 */
578
579static void
580process_fileports(pid, ckscko)
581	int pid;			/* PID of interest */
582	int ckscko;			/* check socket files only */
583{
584	int ef, i, isock, nb = 0, nf;
585	struct proc_fileportinfo *fpi;
586
587/*
588 * Get fileport information for the process.
589 */
590	for (ef = 0; !ef;) {
591	    nb = proc_pidinfo(pid, PROC_PIDLISTFILEPORTS, 0, Fps, NbFps);
592	    if (nb == 0) {
593
594		/*
595		 * Quit if no fileport information
596		 */
597		return;
598	    } else if (nb < 0) {
599		if (errno == ESRCH) {
600
601		/*
602		 * Quit if no fileport information is available for the process.
603		 */
604		    return;
605		}
606	    /*
607	     * Make a dummy file entry with an error message in its NAME column.
608	     */
609		alloc_lfile(" err", -1);
610		(void) snpf(Namech, Namechl, "FILEPORT info error: %s", strerror(errno));
611		Namech[Namechl - 1] = '\0';
612		enter_nm(Namech);
613		if (Lf->sf)
614		    link_lfile();
615	    }
616
617	    if ((nb + sizeof(struct proc_fileportinfo)) < NbFps) {
618
619    	    /*
620	     * There is room in the buffer for at least one more fileport.
621	     */
622		ef = 1;
623	    } else {
624		if (Fps && ((nb = proc_pidinfo(pid, PROC_PIDLISTFILEPORTS, 0, NULL, 0)) <= 0)) {
625		    (void) fprintf(stderr, "%s: can't get fileport byte count: %s\n",
626					Pn, strerror(errno));
627		    Exit(1);
628		}
629
630		/*
631		 * The fileport buffer must be enlarged.
632		 */
633		while (nb > NbFps) {
634		    NbFps += FILEPORTS_INCR;
635		}
636		if (!Fps)
637		    Fps = (struct proc_fileportinfo *)malloc((MALLOC_S)NbFps);
638		else
639		    Fps = (struct proc_fileportinfo *)realloc((MALLOC_P *)Fps, (MALLOC_S)NbFps);
640	    }
641	}
642
643/*
644 * Loop through the fileports.
645 */
646	nf = (int)(nb / sizeof(struct proc_fileportinfo));
647	for (i = 0; i < nf; i++) {
648	    fpi = &Fps[i];
649	/*
650	 * fileport reported as "fp." with "(fileport=0xXXXX)" in the Name column
651	 */
652	    alloc_lfile(" fp.", -1);
653	    Lf->fileport = fpi->proc_fileport;
654	/*
655	 * Process the file by its type.
656	 */
657	    isock = 0;
658	    switch (fpi->proc_fdtype) {
659	    case PROX_FDTYPE_PIPE:
660		if (!ckscko)
661		    (void) process_fileport_pipe(pid, fpi->proc_fileport);
662		break;
663	    case PROX_FDTYPE_SOCKET:
664		(void) process_fileport_socket(pid, fpi->proc_fileport);
665		isock = 1;
666		break;
667	    case PROX_FDTYPE_PSHM:
668		(void) process_fileport_pshm(pid, fpi->proc_fileport);
669		break;
670	    case PROX_FDTYPE_VNODE:
671		(void) process_fileport_vnode(pid, fpi->proc_fileport);
672		break;
673	    default:
674		(void) snpf(Namech, Namechl - 1, "unknown file type: %d",
675		    fpi->proc_fileport);
676		Namech[Namechl - 1] = '\0';
677		(void) enter_nm(Namech);
678		break;
679	    }
680	    if (Lf->sf) {
681		if (!ckscko || isock)
682		    link_lfile();
683	    }
684	}
685}
686#endif	/* PROC_PIDLISTFILEPORTS */
687
688
689/*
690 * process_text() -- process text information
691 */
692
693static void
694process_text(pid)
695	int pid;			/* PID */
696{
697	uint64_t a;
698	int i, n, nb;
699	struct proc_regionwithpathinfo rwpi;
700
701	for (a = (uint64_t)0, i = n = 0; i < 10000; i++) {
702	    nb = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO, a, &rwpi,
703			      sizeof(rwpi));
704	    if (nb <= 0) {
705		if ((errno == ESRCH) || (errno == EINVAL)) {
706
707		/*
708		 * Quit if no more text information is available for the
709		 * process.
710		 */
711		    return;
712		}
713	    /*
714	     * Warn about all other errors via a NAME column message.
715	     */
716		alloc_lfile(" txt", -1);
717		Cfp = (struct file *)NULL;
718		(void) snpf(Namech, Namechl,
719		    "region info error: %s", strerror(errno));
720		Namech[Namechl - 1] = '\0';
721		enter_nm(Namech);
722		if (Lf->sf)
723		    link_lfile();
724		return;
725	    } else if (nb < sizeof(rwpi)) {
726		(void) fprintf(stderr,
727		    "%s: PID %d: proc_pidinfo(PROC_PIDREGIONPATHINFO);\n",
728		    Pn, pid);
729		(void) fprintf(stderr,
730		    "      too few bytes; expected %ld, got %d\n",
731		    sizeof(rwpi), nb);
732		Exit(1);
733	    }
734	    if (rwpi.prp_vip.vip_path[0])
735		enter_vn_text(&rwpi.prp_vip, &n);
736	    a = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
737	}
738}
739
740
741#if	DARWINV>=900
742/*
743 * process_threads() -- process thread information
744 */
745
746#define TWD		" twd"          /* per-thread current working directory
747					 * fd name */
748
749static void
750process_threads(pid, n)
751	int pid;			/* PID */
752	uint32_t n;			/* number of threads */
753{
754	int i, nb, nt;
755/*
756 * Make sure a thread buffer has been allocated.
757 */
758	n += 10;
759	if (n > NbThreads) {
760	    while (n > NbThreads) {
761		NbThreads += THREADS_INCR;
762	    }
763	    if (!Threads)
764		Threads = (uint64_t *)malloc((MALLOC_S)NbThreads);
765	    else
766		Threads = (uint64_t *)realloc((MALLOC_P *)Threads,
767					      (MALLOC_S)NbThreads);
768	    if (!Threads) {
769		(void) fprintf(stderr,
770		    "%s: can't allocate space for %d Threads\n", Pn,
771		    (int)(NbThreads / sizeof(int *)));
772		Exit(1);
773	    }
774	}
775/*
776 * Get thread information for the process.
777 */
778	nb = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, Threads, NbThreads);
779	if (nb <= 0) {
780	    if (errno == ESRCH) {
781
782	    /*
783	     * Quit if no thread information is available for the
784	     * process.
785	     */
786		return;
787	    }
788	}
789	nt = (int)(nb / sizeof(uint64_t));
790/*
791 * Loop through the threads.
792 */
793	for (i = 0; i < nt; i++) {
794	    uint64_t t;
795	    struct proc_threadwithpathinfo tpi;
796
797	    t = Threads[i];
798	    nb = proc_pidinfo(pid, PROC_PIDTHREADPATHINFO, t, &tpi,
799			      sizeof(tpi));
800	    if (nb <= 0) {
801		if ((errno == ESRCH) || (errno == EINVAL)) {
802
803		/*
804		 * Quit if no more thread information is available for the
805		 * process.
806		 */
807		    return;
808		}
809	    /*
810	     * Warn about all other errors via a NAME column message.
811	     */
812		alloc_lfile(TWD, -1);
813		Cfp = (struct file *)NULL;
814		(void) snpf(Namech, Namechl,
815		    "thread info error: %s", strerror(errno));
816		Namech[Namechl - 1] = '\0';
817		enter_nm(Namech);
818		if (Lf->sf)
819		    link_lfile();
820		return;
821	    } else if (nb < sizeof(tpi)) {
822		(void) fprintf(stderr,
823		    "%s: PID %d: proc_pidinfo(PROC_PIDTHREADPATHINFO);\n",
824		    Pn, pid);
825		(void) fprintf(stderr,
826		    "      too few bytes; expected %ld, got %d\n",
827		    sizeof(tpi), nb);
828		Exit(1);
829	    }
830	    if (tpi.pvip.vip_path[0]) {
831		alloc_lfile(TWD, -1);
832		Cfp = (struct file *)NULL;
833		(void) enter_vnode_info(&tpi.pvip);
834		if (Lf->sf)
835		    link_lfile();
836	    }
837	}
838}
839#endif	/* DARWINV>=900 */
840