2 * dproc.c - Linux process access functions for /proc-based lsof
7 * Copyright 1997 Purdue Research Foundation, West Lafayette, Indiana
8 * 47907. All rights reserved.
10 * Written by Victor A. Abell
12 * This software is not subject to any license of the American Telephone
13 * and Telegraph Company or the Regents of the University of California.
15 * Permission is granted to anyone to use this software for any purpose on
16 * any computer system, and to alter it and redistribute it freely, subject
17 * to the following restrictions:
19 * 1. Neither the authors nor Purdue University are responsible for any
20 * consequences of the use of this software.
22 * 2. The origin of this software must not be misrepresented, either by
23 * explicit claim or by omission. Credit to the authors and Purdue
24 * University must appear in documentation and sources.
26 * 3. Altered versions must be plainly marked as such, and must not be
27 * misrepresented as being the original software.
29 * 4. This notice may not be removed or altered.
33 static char copyright[] =
34 "@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
35 static char *rcsid = "$Id: dproc.c,v 1.31 2018/03/26 21:52:29 abe Exp $";
45 #define FDINFO_FLAGS 1 /* fdinfo flags available */
46 #define FDINFO_POS 2 /* fdinfo position available */
48 #if defined(HASEPTOPTS) && defined(HASPTYEPT)
49 #define FDINFO_TTY_INDEX 4 /* fdinfo tty-index available */
50 #endif /* defined(HASEPTOPTS) && defined(HASPTYEPT) */
52 #if defined(HASEPTOPTS) && defined(HASPTYEPT)
53 #define FDINFO_ALL (FDINFO_FLAGS | FDINFO_POS | FDINFO_TTY_INDEX)
54 #else /* !(defined(HASEPTOPTS) && defined(HASPTYEPT)) */
55 #define FDINFO_ALL (FDINFO_FLAGS | FDINFO_POS )
56 #endif /* defined(HASEPTOPTS) && defined(HASPTYEPT) */
58 #define LSTAT_TEST_FILE "/"
59 #define LSTAT_TEST_SEEK 1
61 #if !defined(ULLONG_MAX)
62 #define ULLONG_MAX 18446744073709551615ULL
63 #endif /* !defined(ULLONG_MAX) */
71 int flags; /* flags: line value */
72 off_t pos; /* pos: line value */
74 #if defined(HASEPTOPTS) && defined(HASPTYEPT)
75 int tty_index; /* pty line index */
76 #endif /* defined(HASEPTOPTS) && defined(HASPTYEPT) */
85 static short Cckreg; /* conditional status of regular file
87 * 0 = unconditionally check
88 * 1 = conditionally check */
89 static short Ckscko; /* socket file only checking status:
91 * 1 = check only socket files */
95 * Local function prototypes
98 _PROTOTYPE(static MALLOC_S alloc_cbf,(MALLOC_S len, char **cbf, MALLOC_S cbfa));
99 _PROTOTYPE(static int get_fdinfo,(char *p, int msk, struct l_fdinfo *fi));
100 _PROTOTYPE(static int getlinksrc,(char *ln, char *src, int srcl, char **rest));
101 _PROTOTYPE(static int isefsys,(char *path, char *type, int l,
102 efsys_list_t **rep, struct lfile **lfr));
103 _PROTOTYPE(static int nm2id,(char *nm, int *id, int *idl));
104 _PROTOTYPE(static int read_id_stat,(char *p, int id, char **cmd, int *ppid,
106 _PROTOTYPE(static void process_proc_map,(char *p, struct stat *s, int ss));
107 _PROTOTYPE(static int process_id,(char *idp, int idpl, char *cmd, UID_ARG uid,
108 int pid, int ppid, int pgid, int tid,
110 _PROTOTYPE(static int statEx,(char *p, struct stat *s, int *ss));
113 #if defined(HASSELINUX)
114 _PROTOTYPE(static int cmp_cntx_eq,(char *pcntx, char *ucntx));
121 * cmp_cntx_eq -- compare program and user security contexts
125 cmp_cntx_eq(pcntx, ucntx)
126 char *pcntx; /* program context */
127 char *ucntx; /* user supplied context */
129 return !fnmatch(ucntx, pcntx, 0);
134 * enter_cntx_arg() - enter name ecurity context argument
139 char *cntx; /* context */
143 * Search the argument list for a duplicate.
145 for (cntxp = CntxArg; cntxp; cntxp = cntxp->next) {
146 if (!strcmp(cntxp->cntx, cntx)) {
148 (void) fprintf(stderr, "%s: duplicate context: %s\n",
155 * Create and link a new context argument list entry.
157 if (!(cntxp = (cntxlist_t *)malloc((MALLOC_S)sizeof(cntxlist_t)))) {
158 (void) fprintf(stderr, "%s: no space for context: %s\n", Pn, cntx);
163 cntxp->next = CntxArg;
167 #endif /* defined(HASSELINUX) */
171 * alloc_cbf() -- allocate a command buffer
175 alloc_cbf(len, cbf, cbfa)
176 MALLOC_S len; /* required length */
177 char **cbf; /* current buffer */
178 MALLOC_S cbfa; /* current buffer allocation */
181 *cbf = (char *)realloc((MALLOC_P *)*cbf, len);
183 *cbf = (char *)malloc(len);
185 (void) fprintf(stderr,
186 "%s: can't allocate command %d bytes\n", Pn, (int)len);
194 * gather_proc_info() -- gather process information
201 char cmdbuf[MAXPATHLEN];
203 unsigned char ht, pidts;
204 int n, nl, pgid, pid, ppid, prv, rv, tid, tpgid, tppid, tx;
205 static char *path = (char *)NULL;
206 static int pathl = 0;
207 static char *pidpath = (char *)NULL;
208 static MALLOC_S pidpathl = 0;
209 static MALLOC_S pidx = 0;
210 static DIR *ps = (DIR *)NULL;
212 static char *taskpath = (char *)NULL;
213 static int taskpathl = 0;
214 static char *tidpath = (char *)NULL;
215 static int tidpathl = 0;
223 pidx = strlen(PROCFS) + 1;
224 pidpathl = pidx + 64 + 1; /* 64 is growth room */
225 if (!(pidpath = (char *)malloc(pidpathl))) {
226 (void) fprintf(stderr,
227 "%s: can't allocate %d bytes for \"%s/\"<pid>\n",
228 Pn, (int)pidpathl, PROCFS);
231 (void) snpf(pidpath, pidpathl, "%s/", PROCFS);
234 * Get lock and net information.
236 (void) make_proc_path(pidpath, pidx, &path, &pathl, "locks");
237 (void) get_locks(path);
238 (void) make_proc_path(pidpath, pidx, &path, &pathl, "net/");
239 (void) set_net_paths(path, strlen(path));
241 * If only socket files have been selected, or socket files have been selected
242 * ANDed with other selection options, enable the skipping of regular files.
244 * If socket files and some process options have been selected, enable
245 * conditional skipping of regular file; i.e., regular files will be skipped
246 * unless they belong to a process selected by one of the specified options.
248 if (Selflags & SELNW) {
251 * Some network files selection options have been specified.
253 if (Fand || !(Selflags & ~SELNW)) {
256 * Selection ANDing or only network file options have been
257 * specified, so set unconditional skipping of regular files
258 * and socket file only checking.
265 * If ORed file selection options have been specified, or no ORed
266 * process selection options have been specified, enable
267 * unconditional file checking and clear socket file only checking.
269 * If only ORed process selection options have been specified,
270 * enable conditional file skipping and socket file only checking.
272 if ((Selflags & SELFILE) || !(Selflags & SelProc))
280 * No network file selection options were specified. Enable
281 * unconditional file checking and clear socket file only checking.
286 * Read /proc, looking for PID directories. Open each one and
287 * gather its process and file information.
290 if (!(ps = opendir(PROCFS))) {
291 (void) fprintf(stderr, "%s: can't open %s\n", Pn, PROCFS);
295 (void) rewinddir(ps);
296 while ((dp = readdir(ps))) {
297 if (nm2id(dp->d_name, &pid, &n))
300 * Build path to PID's directory.
302 if ((pidx + n + 1 + 1) > pidpathl) {
303 pidpathl = pidx + n + 1 + 1 + 64;
304 if (!(pidpath = (char *)realloc((MALLOC_P *)pidpath, pidpathl)))
306 (void) fprintf(stderr,
307 "%s: can't allocate %d bytes for \"%s/%s/\"\n",
308 Pn, (int)pidpathl, PROCFS, dp->d_name);
312 (void) snpf(pidpath + pidx, pidpathl - pidx, "%s/", dp->d_name);
315 * Process the PID's stat info.
317 if (stat(pidpath, &sb))
319 uid = (UID_ARG)sb.st_uid;
322 * Get the PID's command name.
324 (void) make_proc_path(pidpath, n, &path, &pathl, "stat");
325 if ((prv = read_id_stat(path, pid, &cmd, &ppid, &pgid)) < 0)
328 #if defined(HASTASKS)
330 * Task reporting has been selected, so save the process' command
331 * string, so that task processing won't change it in the buffer of
334 * Check the tasks of the process first, so that the "-p<PID> -aK"
335 * options work properly.
337 else if (!IgnTasks && (Selflags & SELTASK)) {
338 strncpy(cmdbuf, cmd, sizeof(cmdbuf) - 1);
339 cmdbuf[sizeof(cmdbuf) - 1] = '\0';
341 (void) make_proc_path(pidpath, n, &taskpath, &taskpathl,
344 if ((ts = opendir(taskpath))) {
347 * Process the PID's tasks. Record the open files of those
348 * whose TIDs do not match the PID and which are themselves
351 while ((dp = readdir(ts))) {
354 * Get the task ID. Skip the task if its ID matches the
357 if (nm2id(dp->d_name, &tid, &nl))
364 * Form the path for the TID.
366 if ((tx + 1 + nl + 1 + 4) > tidpathl) {
367 tidpathl = tx + 1 + n + 1 + 4 + 64;
369 tidpath = (char *)realloc((MALLOC_P *)tidpath,
372 tidpath = (char *)malloc((MALLOC_S)tidpathl);
374 (void) fprintf(stderr,
375 "%s: can't allocate %d task bytes", Pn,
377 (void) fprintf(stderr, " for \"%s/%s/stat\"\n",
378 taskpath, dp->d_name);
382 (void) snpf(tidpath, tidpathl, "%s/%s/stat", taskpath,
385 * Check the task state.
387 rv = read_id_stat(tidpath, tid, &tcmd, &tppid,
389 if ((rv < 0) || (rv == 1))
392 * Attempt to record the task.
394 if (!process_id(tidpath, (tx + 1 + nl+ 1), cmd, uid,
395 pid, tppid, tpgid, tid, tcmd))
403 #endif /* defined(HASTASKS) */
406 * If the main process is a task and task selection has been specified
407 * along with option ANDing, enter the main process temporarily as a
408 * task, so that the "-aK" option set lists the main process along
411 if ((prv >= 0) && (prv != 1)) {
412 tid = (Fand && ht && pidts && !IgnTasks && (Selflags & SELTASK))
414 if ((!process_id(pidpath, n, cmd, uid, pid, ppid, pgid, tid,
426 * get_fdinfo() - get values from /proc/<PID>fdinfo/FD
430 get_fdinfo(p, msk, fi)
431 char *p; /* path to fdinfo file */
432 int msk; /* mask for information type: e.g.,
433 * the FDINFO_* definition */
434 struct l_fdinfo *fi; /* pointer to local fdinfo values
435 * return structure */
437 char buf[MAXPATHLEN + 1], *ep, **fp;
441 unsigned long long ull;
443 * Signal no values returned (0) if no fdinfo pointer was provided or if the
444 * fdinfo path can't be opened.
449 #if defined(HASEPTOPTS) && defined(HASPTYEPT)
451 #endif /* defined(HASEPTOPTS) && defined(HASPTYEPT) */
453 if (!p || !*p || !(fs = fopen(p, "r")))
456 * Read the fdinfo file.
458 while (fgets(buf, sizeof(buf), fs)) {
459 if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 2)
461 if (!fp[0] || !*fp[0] || !fp[1] || !*fp[1])
463 if (!strcmp(fp[0], "flags:")) {
466 * Process a "flags:" line.
469 if ((ul = strtoul(fp[1], &ep, 0)) == ULONG_MAX
472 fi->flags = (unsigned int)ul;
473 if ((rv |= FDINFO_FLAGS) == msk)
475 } else if (!strcmp(fp[0], "pos:")) {
478 * Process a "pos:" line.
481 if ((ull = strtoull(fp[1], &ep, 0)) == ULLONG_MAX
484 fi->pos = (off_t)ull;
485 if ((rv |= FDINFO_POS) == FDINFO_ALL)
488 #if defined(HASEPTOPTS) && defined(HASPTYEPT)
489 } else if (!strcmp(fp[0], "tty-index:")) {
492 * Process a "tty-index:" line.
495 if ((ul = strtoul(fp[1], &ep, 0)) == ULONG_MAX
498 fi->tty_index = (int)ul;
499 if (fi->tty_index < 0) {
502 * Oops! If integer overflow occurred, reset the field.
506 if ((rv |= FDINFO_TTY_INDEX) == msk)
508 #endif /* defined(HASEPTOPTS) && defined(HASPTYEPT) */
514 * Signal via the return value what information was obtained. (0 == none)
521 * getlinksrc() - get the source path name for the /proc/<PID>/fd/<FD> link
526 getlinksrc(ln, src, srcl, rest)
527 char *ln; /* link path */
528 char *src; /* link source path return address */
529 int srcl; /* length of src[] */
530 char **rest; /* pointer to what follows the ':' in
531 * the link source path (NULL if no
532 * return requested) */
538 *rest = (char *)NULL;
539 if ((ll = readlink(ln, src, srcl - 1)) < 1
545 if ((cp = strchr(src, ':'))) {
556 * initialize() - perform all initialization
564 char path[MAXPATHLEN];
567 * Test for -i and -X option conflict.
569 if (Fxopt && (Fnet || Nwad)) {
570 (void) fprintf(stderr, "%s: -i is useless when -X is specified.\n",
575 * Open LSTAT_TEST_FILE and seek to byte LSTAT_TEST_SEEK, then lstat the
576 * /proc/<PID>/fd/<FD> for LSTAT_TEST_FILE to see what position is reported.
577 * If the result is LSTAT_TEST_SEEK, enable offset reporting.
579 * If the result isn't LSTAT_TEST_SEEK, next check the fdinfo file for the
580 * open LSTAT_TEST_FILE file descriptor. If it exists and contains a "pos:"
581 * value, and if the value is LSTAT_TEST_SEEK, enable offset reporting.
583 if ((fd = open(LSTAT_TEST_FILE, O_RDONLY)) >= 0) {
584 if (lseek(fd, (off_t)LSTAT_TEST_SEEK, SEEK_SET)
585 == (off_t)LSTAT_TEST_SEEK) {
586 (void) snpf(path, sizeof(path), "%s/%d/fd/%d", PROCFS, Mypid,
588 if (!lstat(path, &sb)) {
589 if (sb.st_size == (off_t)LSTAT_TEST_SEEK)
594 (void) snpf(path, sizeof(path), "%s/%d/fdinfo/%d", PROCFS,
596 if (get_fdinfo(path, FDINFO_POS, &fi) & FDINFO_POS) {
597 if (fi.pos == (off_t)LSTAT_TEST_SEEK)
604 if (Foffset && !Fwarn)
605 (void) fprintf(stderr,
606 "%s: WARNING: can't report offset; disregarding -o.\n",
611 if (Fsv && (OffType != 2)) {
612 if (!Fwarn && FsvByf)
613 (void) fprintf(stderr,
614 "%s: WARNING: can't report file flags; disregarding +f.\n",
619 * Make sure the local mount info table is loaded if doing anything other
620 * than just Internet lookups. (HasNFS is defined during the loading of the
621 * local mount table.)
629 * make_proc_path() - make a path in a /proc directory
632 * pp = pointer to /proc prefix
633 * lp = length of prefix
634 * np = pointer to malloc'd buffer to receive new file's path
635 * nl = length of new file path buffer
636 * sf = new path's suffix
638 * return: length of new path
639 * np = updated with new path
640 * nl = updated with new path length
644 make_proc_path(pp, pl, np, nl, sf)
645 char *pp; /* path prefix -- e.g., /proc/<pid>/ */
646 int pl; /* strlen(pp) */
647 char **np; /* malloc'd receiving buffer */
648 int *nl; /* strlen(*np) */
649 char *sf; /* suffix of new path */
655 if ((rl = pl + sl + 1) > *nl) {
657 cp = (char *)realloc((MALLOC_P *)cp, rl);
659 cp = (char *)malloc(rl);
661 (void) fprintf(stderr,
662 "%s: can't allocate %d bytes for %s%s\n",
663 Pn, (int)rl, pp, sf);
669 (void) snpf(*np, *nl, "%s", pp);
670 (void) snpf(*np + pl, *nl - pl, "%s", sf);
676 * isefsys() -- is path on a file system exempted with -e
678 * Note: alloc_lfile() must have been called in advance.
682 isefsys(path, type, l, rep, lfr)
683 char *path; /* path to file */
684 char *type; /* unknown file type */
685 int l; /* link request: 0 = report
687 efsys_list_t **rep; /* returned Efsysl pointer, if not
689 struct lfile **lfr; /* allocated struct lfile pointer */
694 char nmabuf[MAXPATHLEN + 1];
696 len = (int) strlen(path);
697 for (ep = Efsysl; ep; ep = ep->next) {
700 * Look for a matching exempt file system path at the beginning of
705 if (strncmp(ep->path, path, ep->pathl))
708 * If only reporting, return information as requested.
716 * Process an exempt file.
720 if (mp->ds & SB_DEV) {
722 ds = Lf->dev_def = 1;
724 if (mp->ds & SB_RDEV) {
726 ds = Lf->rdev_def = 1;
730 (void) enter_dev_ch("UNKNOWN");
732 (void) snpf(Lf->type, sizeof(Lf->type), "%s",
733 (type ? type : "UNKN"));
734 (void) enter_nm(path);
735 (void) snpf(nmabuf, sizeof(nmabuf), "(%ce %s)",
736 ep->rdlnk ? '+' : '-', ep->path);
737 nmabuf[sizeof(nmabuf) - 1] = '\0';
738 (void) add_nma(nmabuf, strlen(nmabuf));
744 *lfr = (struct lfile *)NULL;
752 * nm2id() - convert a name to an integer ID
757 char *nm; /* pointer to name */
758 int *id; /* pointer to ID receiver */
759 int *idl; /* pointer to ID length receiver */
761 register int tid, tidl;
763 for (*id = *idl = tid = tidl = 0; *nm; nm++) {
765 #if defined(__STDC__) /* { */
766 if (!isdigit((unsigned char)*nm))
767 #else /* !defined(__STDC__) } { */
768 if (!isascii(*nm) || !isdigit((unsigned char)*cp))
769 #endif /* defined(__STDC__) } */
774 tid = tid * 10 + (int)(*nm - '0');
784 * open_proc_stream() -- open a /proc stream
788 open_proc_stream(p, m, buf, sz, act)
789 char *p; /* pointer to path to open */
790 char *m; /* pointer to mode -- e.g., "r" */
791 char **buf; /* pointer tp setvbuf() address
793 size_t *sz; /* setvbuf() size (0 if none or if
794 * getpagesize() desired */
795 int act; /* fopen() failure action:
796 * 0 : return (FILE *)NULL
797 * <>0 : fprintf() an error message
801 FILE *fs; /* opened stream */
802 static size_t psz = (size_t)0; /* page size */
803 size_t tsz; /* temporary size */
807 if (!(fs = fopen(p, m))) {
809 return((FILE *)NULL);
810 (void) fprintf(stderr, "%s: can't fopen(%s, \"%s\"): %s\n",
811 Pn, p, m, strerror(errno));
815 * Return the stream if no buffer change is required.
820 * Determine the buffer size required.
828 * Allocate a buffer for the stream, as required.
831 if (!(*buf = (char *)malloc((MALLOC_S)tsz))) {
832 (void) fprintf(stderr,
833 "%s: can't allocate %d bytes for %s stream buffer\n",
840 * Assign the buffer to the stream.
842 if (setvbuf(fs, *buf, _IOFBF, tsz)) {
843 (void) fprintf(stderr, "%s: setvbuf(%s)=%d failure: %s\n",
844 Pn, p, (int)tsz, strerror(errno));
852 * process_id - process ID: PID or LWP
854 * return: 0 == ID processed
855 * 1 == ID not processed
859 process_id(idp, idpl, cmd, uid, pid, ppid, pgid, tid, tcmd)
860 char *idp; /* pointer to ID's path */
861 int idpl; /* pointer to ID's path length */
862 char *cmd; /* pointer to ID's command */
863 UID_ARG uid; /* ID's UID */
864 int pid; /* ID's PID */
865 int ppid; /* parent PID */
866 int pgid; /* parent GID */
867 int tid; /* task ID, if non-zero */
868 char *tcmd; /* task command, if non-NULL) */
871 static char *dpath = (char *)NULL;
872 static int dpathl = 0;
873 short efs, enls, enss, lnk, oty, pn, pss, sf;
874 int fd, i, ls, n, ss, sv;
878 static char *ipath = (char *)NULL;
879 static int ipathl = 0;
883 char nmabuf[MAXPATHLEN + 1], pbuf[MAXPATHLEN + 1];
884 static char *path = (char *)NULL;
885 static int pathl = 0;
886 static char *pathi = (char *)NULL;
887 static int pathil = 0;
891 #if defined(HASSELINUX)
893 #endif /* defined(HASSELINUX) */
896 * See if process is excluded.
898 if (is_proc_excl(pid, pgid, uid, &pss, &sf, tid)
899 || is_cmd_excl(cmd, &pss, &sf))
902 #if defined(HASEPTOPTS)
905 #else /* !defined(HASEPTOPTS) */
907 #endif /* defined(HASEPTOPTS) */
910 if (Cckreg && !FeptE) {
913 * If conditional checking of regular files is enabled, enable
914 * socket file only checking, based on the process' selection
917 Ckscko = (sf & SelProc) ? 0 : 1;
919 alloc_lproc(pid, pgid, ppid, uid, cmd, (int)pss, (int)sf);
920 Plf = (struct lfile *)NULL;
922 #if defined(HASTASKS)
924 * Enter task information.
928 if (!(Lp->tcmd = mkstrcpy(tcmd, (MALLOC_S *)NULL))) {
929 (void) fprintf(stderr,
930 "%s: PID %d, TID %d, no space for task name: ",
932 safestrprt(tcmd, stderr, 1);
936 #endif /* defined(HASTASKS) */
939 * Process the ID's current working directory info.
943 (void) make_proc_path(idp, idpl, &path, &pathl, "cwd");
944 alloc_lfile(CWD, -1);
945 if (getlinksrc(path, pbuf, sizeof(pbuf), (char **)NULL) < 1) {
947 zeromem((char *)&sb, sizeof(sb));
949 (void) snpf(nmabuf, sizeof(nmabuf), "(readlink: %s)",
951 nmabuf[sizeof(nmabuf) - 1] = '\0';
952 (void) add_nma(nmabuf, strlen(nmabuf));
958 if (Efsysl && !isefsys(pbuf, "UNKNcwd", 1, NULL, &lfr)) {
964 if ((sv = statsafely(path, &sb)))
965 sv = statEx(pbuf, &sb, &ss);
967 sv = stat(path, &sb);
971 (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)",
973 nmabuf[sizeof(nmabuf) - 1] = '\0';
974 (void) add_nma(nmabuf, strlen(nmabuf));
980 (void) process_proc_node(lnk ? pbuf : path,
982 (struct stat *)NULL, 0);
988 * Process the ID's root directory info.
992 (void) make_proc_path(idp, idpl, &path, &pathl, "root");
993 alloc_lfile(RTD, -1);
994 if (getlinksrc(path, pbuf, sizeof(pbuf), (char **)NULL) < 1) {
996 zeromem((char *)&sb, sizeof(sb));
997 (void) snpf(nmabuf, sizeof(nmabuf), "(readlink: %s)",
999 nmabuf[sizeof(nmabuf) - 1] = '\0';
1000 (void) add_nma(nmabuf, strlen(nmabuf));
1006 if (Efsysl && !isefsys(pbuf, "UNKNrtd", 1, NULL, NULL))
1011 if ((sv = statsafely(path, &sb)))
1012 sv = statEx(pbuf, &sb, &ss);
1014 sv = stat(path, &sb);
1018 (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)",
1020 nmabuf[sizeof(nmabuf) - 1] = '\0';
1021 (void) add_nma(nmabuf, strlen(nmabuf));
1027 (void) process_proc_node(lnk ? pbuf : path,
1029 (struct stat *)NULL, 0);
1035 * Process the ID's execution info.
1037 lnk = ss = txts = 0;
1039 (void) make_proc_path(idp, idpl, &path, &pathl, "exe");
1040 alloc_lfile("txt", -1);
1041 if (getlinksrc(path, pbuf, sizeof(pbuf), (char **)NULL) < 1) {
1042 zeromem((void *)&sb, sizeof(sb));
1044 if ((errno != ENOENT) || uid) {
1045 (void) snpf(nmabuf, sizeof(nmabuf), "(readlink: %s)",
1047 nmabuf[sizeof(nmabuf) - 1] = '\0';
1048 (void) add_nma(nmabuf, strlen(nmabuf));
1055 if (Efsysl && !isefsys(pbuf, "UNKNtxt", 1, NULL, NULL))
1060 if ((sv = statsafely(path, &sb))) {
1061 sv = statEx(pbuf, &sb, &ss);
1062 if (!sv && (ss & SB_DEV) && (ss & SB_INO))
1066 sv = stat(path, &sb);
1070 (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)",
1072 nmabuf[sizeof(nmabuf) - 1] = '\0';
1073 (void) add_nma(nmabuf, strlen(nmabuf));
1080 (void) process_proc_node(lnk ? pbuf : path,
1082 (struct stat *)NULL, 0);
1088 * Process the ID's memory map info.
1091 (void) make_proc_path(idp, idpl, &path, &pathl, "maps");
1092 (void) process_proc_map(path, txts ? &sb : (struct stat *)NULL,
1096 #if defined(HASSELINUX)
1098 * Process the PID's SELinux context.
1103 * If the -Z (cntx) option was specified, match the valid contexts.
1106 if (getpidcon(pid, &Lp->cntx) == -1) {
1107 Lp->cntx = (char *)NULL;
1109 (void) snpf(nmabuf, sizeof(nmabuf),
1110 "(getpidcon: %s)", strerror(errno));
1111 if (!(Lp->cntx = strdup(nmabuf))) {
1112 (void) fprintf(stderr,
1113 "%s: no context error space: PID %ld",
1118 } else if (CntxArg) {
1121 * See if context includes the process.
1123 for (cntxp = CntxArg; cntxp; cntxp = cntxp->next) {
1124 if (cmp_cntx_eq(Lp->cntx, cntxp->cntx)) {
1133 #endif /* defined(HASSELINUX) */
1136 * Process the ID's file descriptor directory.
1138 if ((i = make_proc_path(idp, idpl, &dpath, &dpathl, "fd/")) < 3)
1140 dpath[i - 1] = '\0';
1142 && ((j = make_proc_path(idp, idpl, &ipath, &ipathl, "fdinfo/")) >= 7))
1146 if (!(fdp = opendir(dpath))) {
1148 (void) snpf(nmabuf, sizeof(nmabuf), "%s (opendir: %s)",
1149 dpath, strerror(errno));
1150 alloc_lfile("NOFD", -1);
1151 nmabuf[sizeof(nmabuf) - 1] = '\0';
1152 (void) add_nma(nmabuf, strlen(nmabuf));
1158 while ((fp = readdir(fdp))) {
1159 if (nm2id(fp->d_name, &fd, &n))
1161 (void) make_proc_path(dpath, i, &path, &pathl, fp->d_name);
1162 (void) alloc_lfile((char *)NULL, fd);
1163 if (getlinksrc(path, pbuf, sizeof(pbuf), &rest) < 1) {
1164 zeromem((char *)&sb, sizeof(sb));
1167 (void) snpf(nmabuf, sizeof(nmabuf), "(readlink: %s)",
1169 nmabuf[sizeof(nmabuf) - 1] = '\0';
1170 (void) add_nma(nmabuf, strlen(nmabuf));
1176 if (Efsysl && !isefsys(pbuf, "UNKNfd", 1, NULL, &lfr)) {
1181 if (lstatsafely(path, &lsb)) {
1182 (void) statEx(pbuf, &lsb, &ls);
1188 if (statsafely(path, &sb)) {
1189 (void) statEx(pbuf, &sb, &ss);
1196 ls = lstat(path, &lsb) ? 0 : SB_ALL;
1198 ss = stat(path, &sb) ? 0 : SB_ALL;
1201 if (!ls && !Fwarn) {
1202 (void) snpf(nmabuf, sizeof(nmabuf), "lstat: %s)",
1204 nmabuf[sizeof(nmabuf) - 1] = '\0';
1205 (void) add_nma(nmabuf, strlen(nmabuf));
1207 if (!ss && !Fwarn) {
1208 (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)",
1210 nmabuf[sizeof(nmabuf) - 1] = '\0';
1211 (void) add_nma(nmabuf, strlen(nmabuf));
1215 && ((sb.st_mode & S_IFMT) == S_IFSOCK))
1224 if (pn || (efs && lfr && oty)) {
1226 (void) make_proc_path(ipath, j, &pathi, &pathil,
1228 if ((av = get_fdinfo(pathi,FDINFO_ALL,&fi)) & FDINFO_POS) {
1231 lfr->off = (SZOFFTYPE)fi.pos;
1236 lsb.st_size = fi.pos;
1241 #if !defined(HASNOFSFLAGS)
1242 if ((av & FDINFO_FLAGS) && (Fsv & FSV_FG)) {
1244 lfr->ffg = (long)fi.flags;
1247 Lf->ffg = (long)fi.flags;
1251 # endif /* !defined(HASNOFSFLAGS) */
1255 process_proc_node(lnk ? pbuf : path, path, &sb, ss, &lsb,
1257 if ((Lf->ntype == N_ANON_INODE) && rest && *rest)
1260 #if defined(HASEPTOPTS) && defined(HASPTYEPT)
1263 && is_pty_ptmx(Lf->rdev)
1264 && (av & FDINFO_TTY_INDEX)
1266 enter_ptmxi(fi.tty_index);
1267 Lf->tty_index = fi.tty_index;
1268 Lf->sf |= SELPTYINFO;
1270 #endif /* defined(HASEPTOPTS) && defined(HASPTYEPT) */
1277 (void) closedir(fdp);
1283 * process_proc_map() - process the memory map of a process
1287 process_proc_map(p, s, ss)
1288 char *p; /* path to process maps file */
1289 struct stat *s; /* executing text file state buffer */
1290 int ss; /* *s status -- i.e., SB_* values */
1292 char buf[MAXPATHLEN + 1], *ep, fmtbuf[32], **fp, nmabuf[MAXPATHLEN + 1];
1294 int ds, efs, en, i, mss, nf, sv;
1306 static struct saved_map *sm = (struct saved_map *)NULL;
1309 static char *vbuf = (char *)NULL;
1310 static size_t vsz = (size_t)0;
1312 * Open the /proc/<pid>/maps file, assign a page size buffer to its stream,
1315 if (!(ms = open_proc_stream(p, "r", &vbuf, &vsz, 0)))
1317 while (fgets(buf, sizeof(buf), ms)) {
1318 if ((nf = get_fields(buf, ":", &fp, &eb, 1)) < 7)
1319 continue; /* not enough fields */
1320 if (!fp[6] || !*fp[6])
1321 continue; /* no path name */
1323 * See if the path ends in " (deleted)". If it does, strip the
1324 * " (deleted)" characters and remember that they were there.
1326 if (((ds = (int)strlen(fp[6])) > 10)
1327 && !strcmp(fp[6] + ds - 10, " (deleted)"))
1329 *(fp[6] + ds - 10) = '\0';
1333 * Assemble the major and minor device numbers.
1336 if (!fp[3] || !*fp[3]
1337 || (maj = strtol(fp[3], &ep, 16)) == LONG_MIN || maj == LONG_MAX
1341 if (!fp[4] || !*fp[4]
1342 || (min = strtol(fp[4], &ep, 16)) == LONG_MIN || min == LONG_MAX
1346 * Assemble the device and inode numbers. If they are both zero, skip
1349 dev = (dev_t)makedev((int)maj, (int)min);
1350 if (!fp[5] || !*fp[5])
1353 if ((inode = strtoull(fp[5], &ep, 0)) == ULLONG_MAX
1359 * See if the device + inode pair match that of the executable.
1360 * If they do, skip this map entry.
1362 if (s && (ss & SB_DEV) && (ss & SB_INO)
1363 && (dev == s->st_dev) && (inode == (INODETYPE)s->st_ino))
1366 * See if this device + inode pair has already been processed as
1369 for (i = 0; i < ns; i++) {
1370 if (dev == sm[i].dev && inode == sm[i].inode)
1376 * Record the processing of this map entry's device and inode pair.
1380 len = (MALLOC_S)(sma * sizeof(struct saved_map));
1382 sm = (struct saved_map *)realloc(sm, len);
1384 sm = (struct saved_map *)malloc(len);
1386 (void) fprintf(stderr,
1387 "%s: can't allocate %d bytes for saved maps, PID %d\n",
1388 Pn, (int)len, Lp->pid);
1393 sm[ns++].inode = inode;
1395 * Allocate space for the mapped file, then get stat(2) information
1396 * for it. Skip the stat(2) operation if this is on an exempt file
1399 alloc_lfile("mem", -1);
1400 if (Efsysl && !isefsys(fp[6], (char *)NULL, 0, &rep, NULL))
1406 sv = statsafely(fp[6], &sb);
1408 sv = stat(fp[6], &sb);
1413 * Applying stat(2) to the file was not possible (file is on an
1414 * exempt file system) or stat(2) failed, so manufacture a partial
1415 * stat(2) reply from the process' maps file entry.
1417 * If the file has been deleted, reset its type to "DEL";
1418 * otherwise generate a stat() error name addition.
1420 zeromem((char *)&sb, sizeof(sb));
1422 sb.st_ino = (ino_t)inode;
1423 sb.st_mode = S_IFREG;
1424 mss = SB_DEV | SB_INO | SB_MODE;
1426 alloc_lfile("DEL", -1);
1427 else if (!efs && !Fwarn) {
1428 (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)",
1430 nmabuf[sizeof(nmabuf) - 1] = '\0';
1431 (void) add_nma(nmabuf, strlen(nmabuf));
1433 } else if ((sb.st_dev != dev) || ((INODETYPE)sb.st_ino != inode)) {
1436 * The stat(2) device and inode numbers don't match those obtained
1437 * from the process' maps file.
1439 * If the file has been deleted, reset its type to "DEL"; otherwise
1440 * generate inconsistency name additions.
1442 * Manufacture a partial stat(2) reply from the maps file
1446 alloc_lfile("DEL", -1);
1450 if (sb.st_dev != dev) {
1451 (void) snpf(nmabuf, sizeof(nmabuf),
1452 "(path dev=%d,%d%s",
1453 GET_MAJ_DEV(sb.st_dev), GET_MIN_DEV(sb.st_dev),
1454 ((INODETYPE)sb.st_ino == inode) ? ")" : ",");
1455 nmabuf[sizeof(nmabuf) - 1] = '\0';
1456 (void) add_nma(nmabuf, strlen(nmabuf));
1460 if ((INODETYPE)sb.st_ino != inode) {
1461 (void) snpf(fmtbuf, sizeof(fmtbuf), "%%sinode=%s)",
1463 (void) snpf(nmabuf, sizeof(nmabuf), fmtbuf,
1464 sep, (INODETYPE)sb.st_ino);
1465 nmabuf[sizeof(nmabuf) - 1] = '\0';
1466 (void) add_nma(nmabuf, strlen(nmabuf));
1469 zeromem((char *)&sb, sizeof(sb));
1471 sb.st_ino = (ino_t)inode;
1472 sb.st_mode = S_IFREG;
1473 mss = SB_DEV | SB_INO | SB_MODE;
1477 * Record the file's information.
1480 process_proc_node(fp[6], fp[6], &sb, mss, (struct stat *)NULL,
1485 * If this file is on an exempt file system, complete the lfile
1486 * structure, but change its type and add the exemption note to
1489 Lf->dev = sb.st_dev;
1490 Lf->inode = (ino_t)sb.st_ino;
1491 Lf->dev_def = Lf->inp_ty = 1;
1492 (void) enter_nm(fp[6]);
1493 (void) snpf(Lf->type, sizeof(Lf->type), "%s",
1494 (ds ? "UNKNdel" : "UNKNmem"));
1495 (void) snpf(nmabuf, sizeof(nmabuf), "(%ce %s)",
1496 rep->rdlnk ? '+' : '-', rep->path);
1497 nmabuf[sizeof(nmabuf) - 1] = '\0';
1498 (void) add_nma(nmabuf, strlen(nmabuf));
1508 * read_id_stat() - read ID (PID or LWP ID) status
1510 * return: -1 == ID is unavailable
1512 * 1 == ID is a zombie
1513 * 2 == ID is a thread
1517 read_id_stat(p, id, cmd, ppid, pgid)
1518 char *p; /* path to status file */
1519 int id; /* ID: PID or LWP */
1520 char **cmd; /* malloc'd command name */
1521 int *ppid; /* returned parent PID for PID type */
1522 int *pgid; /* returned process group ID for PID
1525 char buf[MAXPATHLEN], *cp, *cp1, **fp;
1526 int ch, cx, es, nf, pc;
1527 static char *cbf = (char *)NULL;
1528 static MALLOC_S cbfa = 0;
1530 static char *vbuf = (char *)NULL;
1531 static size_t vsz = (size_t)0;
1533 * Open the stat file path, assign a page size buffer to its stream,
1534 * and read the file's first line.
1536 if (!(fs = open_proc_stream(p, "r", &vbuf, &vsz, 0)))
1538 if (!(cp = fgets(buf, sizeof(buf), fs))) {
1546 * Skip to the first field, and make sure it is a matching ID.
1549 while (*cp && (*cp != ' ') && (*cp != '\t'))
1553 if (atoi(cp1) != id)
1554 goto read_id_stat_exit;
1556 * The second field should contain the command, enclosed in parentheses.
1557 * If it also has embedded '\n' characters, replace them with '?' characters,
1558 * accumulating command characters until a closing parentheses appears.
1561 for (++cp; *cp && (*cp == ' '); cp++)
1563 if (!cp || (*cp != '('))
1564 goto read_id_stat_exit;
1566 pc = 1; /* start the parenthesis balance count at 1 */
1568 * Enter the command characters safely. Supply them from the initial read
1569 * of the stat file line, a '\n' if the initial read didn't yield a ')'
1570 * command closure, or by reading the rest of the command a character at
1571 * a time from the stat file. Count embedded '(' characters and balance
1572 * them with embedded ')' characters. The opening '(' starts the balance
1575 for (cx = es = 0;;) {
1579 if ((ch = fgetc(fs)) == EOF)
1580 goto read_id_stat_exit;
1582 if (ch == '(') /* a '(' advances the balance count */
1587 * Balance parentheses when a closure is encountered. When
1588 * they are balanced, this is the end of the command.
1594 if ((cx + 2) > cbfa)
1595 cbfa = alloc_cbf((cx + 2), &cbf, cbfa);
1600 es = 1; /* Switch to fgetc() when a '\0' appears. */
1604 * Read the remainder of the stat line if it was necessary to read command
1605 * characters individually from the stat file.
1607 * Separate the reminder into fields.
1610 cp = fgets(buf, sizeof(buf), fs);
1614 if ((nf = get_fields(cp, (char *)NULL, &fp, (int *)NULL, 0)) < 3)
1617 * Convert and return parent process (fourth field) and process group (fifth
1620 if (fp[1] && *fp[1])
1621 *ppid = atoi(fp[1]);
1624 if (fp[2] && *fp[2])
1625 *pgid = atoi(fp[2]);
1629 * Check the state in the third field. If it is 'Z', return that indication.
1631 if (fp[0] && !strcmp(fp[0], "Z"))
1633 else if (fp[0] && !strcmp(fp[0], "T"))
1640 * statEx() - extended stat() to get device numbers when a "safe" stat has
1641 * failed and the system has an NFS mount
1643 * Note: this function was suggested by Paul Szabo as a way to get device
1644 * numbers for NFS files when an NFS mount point has the root_squash
1645 * option set. In that case, even if lsof is setuid(root), the identity
1646 * of its requests to stat() NFS files lose root permission and may fail.
1648 * This function should be used only when links have been successfully
1649 * resolved in the /proc path by getlinksrc().
1654 char *p; /* file path */
1655 struct stat *s; /* stat() result -- NULL if none
1657 int *ss; /* stat() status -- SB_* values */
1659 static size_t ca = 0;
1660 static char *cb = NULL;
1667 * Make a copy of the path.
1670 if ((sz + 1) > ca) {
1672 cb = (char *)realloc((MALLOC_P *)cb, sz + 1);
1674 cb = (char *)malloc(sz + 1);
1676 (void) fprintf(stderr,
1677 "%s: PID %ld: no statEx path space: %s\n",
1678 Pn, (long)Lp->pid, p);
1683 (void) strcpy(cb, p);
1685 * Trim trailing leaves from the end of the path one at a time and do a safe
1686 * stat() on each trimmed result. Stop when a safe stat() succeeds or doesn't
1687 * fail because of EACCES or EPERM.
1689 for (cp = strrchr(cb, '/'); cp && (cp != cb);) {
1691 if (!statsafely(cb, &sb)) {
1696 if ((ensv != EACCES) && (ensv != EPERM))
1698 cp = strrchr(cb, '/');
1701 * If a stat() on a trimmed result succeeded, form partial results containing
1702 * only the device and raw device numbers.
1704 zeromem((char *)s, sizeof(struct stat));
1707 s->st_dev = sb.st_dev;
1708 s->st_rdev = sb.st_rdev;
1709 *ss = SB_DEV | SB_RDEV;