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 */
43fd_info_t *
44Pfd2info(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
76static int
77fdwalk_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 */
102static void
103load_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
120int
121Pfdinfo_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