xref: /illumos-gate/usr/src/lib/libproc/common/Pfdinfo.c (revision eb9a1df2aeb866bf1de4494433b6d7e5fa07b3ae)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
14  */
15 /*
16  * Copyright (c) 2013 Joyent, Inc.  All Rights reserved.
17  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
18  */
19 
20 #include <limits.h>
21 #include <stdio.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <dirent.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <sys/mkdev.h>
28 
29 #include "libproc.h"
30 #include "Pcontrol.h"
31 #include "proc_fd.h"
32 
33 /*
34  * Pfdinfo.c - obtain open file information.
35  */
36 
37 /*
38  * Allocate an fd_info structure and stick it on the list.
39  * (Unless one already exists.)  The list is sorted in
40  * reverse order.  We will traverse it in that order later.
41  * This makes the usual ordered insert *fast*.
42  */
43 fd_info_t *
44 Pfd2info(struct ps_prochandle *P, int fd)
45 {
46 	fd_info_t	*fip = list_next(&P->fd_head);
47 	fd_info_t	*next;
48 	int i;
49 
50 	if (fip == NULL) {
51 		list_link(&P->fd_head, NULL);
52 		fip = list_next(&P->fd_head);
53 	}
54 
55 	for (i = 0; i < P->num_fd; i++, fip = list_next(fip)) {
56 		if (fip->fd_info == NULL)
57 			continue;
58 
59 		if (fip->fd_info->pr_fd == fd) {
60 			return (fip);
61 		}
62 		if (fip->fd_info->pr_fd < fd) {
63 			break;
64 		}
65 	}
66 
67 	next = fip;
68 	if ((fip = calloc(1, sizeof (*fip))) == NULL)
69 		return (NULL);
70 
71 	list_link(fip, next ? next : (void *)&(P->fd_head));
72 	P->num_fd++;
73 	return (fip);
74 }
75 
76 static int
77 fdwalk_cb(const prfdinfo_t *info, void *arg)
78 {
79 	struct ps_prochandle *P = arg;
80 	fd_info_t *fip;
81 
82 	fip = Pfd2info(P, info->pr_fd);
83 	if (fip == NULL) {
84 		errno = ENOMEM;
85 		return (-1);
86 	}
87 
88 	if (fip->fd_info == NULL)
89 		fip->fd_info = proc_fdinfo_dup(info);
90 
91 	if (fip->fd_info == NULL) {
92 		errno = ENOMEM;
93 		return (-1);
94 	}
95 
96 	return (0);
97 }
98 
99 /*
100  * Attempt to load the open file information from a live process.
101  */
102 static void
103 load_fdinfo(struct ps_prochandle *P)
104 {
105 	/*
106 	 * In the unlikely case there are *no* file descriptors open,
107 	 * we will keep rescanning the proc directory, which will be empty.
108 	 * This is an edge case it isn't worth adding additional state to
109 	 * to eliminate.
110 	 */
111 	if (P->num_fd > 0)
112 		return;
113 
114 	if (P->state == PS_DEAD || P->state == PS_IDLE)
115 		return;
116 
117 	proc_fdwalk(P->pid, fdwalk_cb, P);
118 }
119 
120 int
121 Pfdinfo_iter(struct ps_prochandle *P, proc_fdinfo_f *func, void *cd)
122 {
123 	fd_info_t *fip;
124 	int rv;
125 
126 	/* Make sure we have live data, if appropriate */
127 	load_fdinfo(P);
128 
129 	/* NB: We walk the list backwards. */
130 
131 	for (fip = list_prev(&P->fd_head);
132 	    fip != (void *)&P->fd_head && fip != NULL;
133 	    fip = list_prev(fip)) {
134 		if ((rv = func(cd, fip->fd_info)) != 0)
135 			return (rv);
136 	}
137 	return (0);
138 }
139