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  * Copyright 2021 Oxide Computer Company
19  */
20 
21 #include <limits.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include <stddef.h>
29 #include <sys/mkdev.h>
30 
31 #include "libproc.h"
32 #include "Pcontrol.h"
33 #include "proc_fd.h"
34 
35 /*
36  * Pfdinfo.c - obtain open file information.
37  */
38 
39 void
Pinitfd(struct ps_prochandle * P)40 Pinitfd(struct ps_prochandle *P)
41 {
42 	list_create(&P->fd_head, sizeof (fd_info_t),
43 	    offsetof(fd_info_t, fd_list));
44 }
45 
46 /*
47  * Allocate an fd_info structure and stick it on the list.
48  * (Unless one already exists.)  The list is sorted in
49  * reverse order.  We will traverse it in that order later.
50  * This makes the usual ordered insert *fast*.
51  */
52 fd_info_t *
Pfd2info(struct ps_prochandle * P,int fd)53 Pfd2info(struct ps_prochandle *P, int fd)
54 {
55 	fd_info_t	*fip, *next = NULL;
56 
57 	for (fip = list_head(&P->fd_head); fip != NULL;
58 	    fip = list_next(&P->fd_head, fip)) {
59 		if (fip->fd_info == NULL)
60 			continue;
61 
62 		if (fip->fd_info->pr_fd == fd) {
63 			return (fip);
64 		}
65 		if (fip->fd_info->pr_fd < fd) {
66 			break;
67 		}
68 	}
69 
70 	next = fip;
71 	if ((fip = calloc(1, sizeof (*fip))) == NULL)
72 		return (NULL);
73 
74 	list_insert_before(&P->fd_head, next, fip);
75 	return (fip);
76 }
77 
78 static int
fdwalk_cb(const prfdinfo_t * info,void * arg)79 fdwalk_cb(const prfdinfo_t *info, void *arg)
80 {
81 	struct ps_prochandle *P = arg;
82 	fd_info_t *fip;
83 
84 	fip = Pfd2info(P, info->pr_fd);
85 	if (fip == NULL) {
86 		errno = ENOMEM;
87 		return (-1);
88 	}
89 
90 	if (fip->fd_info == NULL)
91 		fip->fd_info = proc_fdinfo_dup(info);
92 
93 	if (fip->fd_info == NULL) {
94 		errno = ENOMEM;
95 		return (-1);
96 	}
97 
98 	return (0);
99 }
100 
101 /*
102  * Attempt to load the open file information from a live process.
103  */
104 static void
load_fdinfo(struct ps_prochandle * P)105 load_fdinfo(struct ps_prochandle *P)
106 {
107 	/*
108 	 * In the unlikely case there are *no* file descriptors open,
109 	 * we will keep rescanning the proc directory, which will be empty.
110 	 * This is an edge case it isn't worth adding additional state to
111 	 * to eliminate.
112 	 */
113 	if (!list_is_empty(&P->fd_head))
114 		return;
115 
116 	if (P->state == PS_DEAD || P->state == PS_IDLE)
117 		return;
118 
119 	proc_fdwalk(P->pid, fdwalk_cb, P);
120 }
121 
122 int
Pfdinfo_iter(struct ps_prochandle * P,proc_fdinfo_f * func,void * cd)123 Pfdinfo_iter(struct ps_prochandle *P, proc_fdinfo_f *func, void *cd)
124 {
125 	fd_info_t *fip;
126 	int rv;
127 
128 	/* Make sure we have live data, if appropriate */
129 	load_fdinfo(P);
130 
131 	/* NB: We walk the list backwards. */
132 
133 	for (fip = list_tail(&P->fd_head); fip != NULL;
134 	    fip = list_prev(&P->fd_head, fip)) {
135 		if ((rv = func(cd, fip->fd_info)) != 0)
136 			return (rv);
137 	}
138 	return (0);
139 }
140