nrelease - fix/improve livecd
[dragonfly.git] / lib / libkvm / kvm_file.c
1 /*-
2  * Copyright (c) 1989, 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)kvm_file.c       8.1 (Berkeley) 6/4/93
30  * $FreeBSD: src/lib/libkvm/kvm_file.c,v 1.11 2000/02/18 16:39:00 peter Exp $
31  */
32
33 /*
34  * File list interface for kvm.
35  */
36
37 #include <sys/user.h>   /* MUST BE FIRST */
38 #include <sys/param.h>
39 #define _KERNEL
40 #include <sys/file.h>
41 #undef _KERNEL
42 #include <sys/stat.h>
43 #include <sys/ioctl.h>
44 #include <sys/kinfo.h>
45 #include <nlist.h>
46
47 #include <vm/vm.h>
48 #include <vm/vm_param.h>
49 #include <vm/swap_pager.h>
50
51 #include <sys/sysctl.h>
52
53 #include <limits.h>
54 #include <ndbm.h>
55 #include <paths.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59
60 #include "kvm.h"
61 #include "kvm_private.h"
62
63 #define KREAD(kd, addr, obj) \
64         (kvm_read(kd, addr, obj, sizeof(*obj)) != sizeof(*obj))
65
66 /* XXX copied from sys/kern/subr_kcore.c */
67 static void
68 kcore_make_file(struct kinfo_file *ufile, struct file *kfile,
69     pid_t pid, uid_t owner, int n)
70 {
71         bzero(ufile, sizeof(*ufile));
72         ufile->f_size = sizeof(*ufile);
73         ufile->f_pid = pid;
74         ufile->f_uid = owner;
75
76         ufile->f_fd = n;
77         ufile->f_file = kfile;
78         ufile->f_data = kfile->f_data;
79         ufile->f_type = kfile->f_type;
80         ufile->f_count = kfile->f_count;
81         ufile->f_msgcount = kfile->f_msgcount;
82         ufile->f_offset = kfile->f_offset;
83         ufile->f_flag = kfile->f_flag;
84 }
85
86 /*
87  * Get file structures.
88  */
89 static int
90 kvm_deadfiles(kvm_t *kd, struct kinfo_proc *kproc, int kproc_cnt, int nfiles)
91 {
92         struct kinfo_file *kinfo_file = (struct kinfo_file *)kd->argspc;
93         struct fdnode *fd_files;
94         size_t size;
95         int i, fd_nfiles, found = 0;
96
97         fd_nfiles = NDFILE;
98         fd_files = malloc(fd_nfiles * sizeof(struct fdnode));
99         if (fd_files == NULL) {
100                 _kvm_err(kd, kd->program, "alloc fd_files failed");
101                 return 0;
102         }
103
104         for (i = 0; i < kproc_cnt; ++i) {
105                 const struct kinfo_proc *kp = &kproc[i];
106                 struct filedesc fdp;
107                 int n, f;
108
109                 if (kp->kp_fd == 0)
110                         continue;
111                 if (KREAD(kd, kp->kp_fd, &fdp)) {
112                         _kvm_err(kd, kd->program, "can't read fdp");
113                         free(fd_files);
114                         return 0;
115                 }
116                 if (fdp.fd_files == NULL)
117                         continue;
118
119                 if (fdp.fd_nfiles > fd_nfiles) {
120                         struct fdnode *new_fd_files;
121
122                         fd_nfiles = fdp.fd_nfiles;
123                         new_fd_files =
124                             malloc(fd_nfiles * sizeof(struct fdnode));
125                         free(fd_files);
126                         if (new_fd_files == NULL) {
127                                 _kvm_err(kd, kd->program,
128                                     "realloc fd_files failed");
129                                 return 0;
130                         }
131                         fd_files = new_fd_files;
132                 }
133                 n = fdp.fd_nfiles * sizeof(struct fdnode);
134
135                 if (kvm_read(kd, (uintptr_t)fdp.fd_files, fd_files, n) != n) {
136                         _kvm_err(kd, kd->program, "can't read fd_files");
137                         free(fd_files);
138                         return 0;
139                 }
140                 for (f = 0; f < fdp.fd_nfiles; ++f) {
141                         struct file kf;
142
143                         if (fd_files[f].fp == NULL)
144                                 continue;
145                         if (KREAD(kd, (uintptr_t)fd_files[f].fp, &kf)) {
146                                 _kvm_err(kd, kd->program, "can't read file");
147                                 free(fd_files);
148                                 return 0;
149                         }
150
151                         kcore_make_file(kinfo_file, &kf,
152                             kp->kp_pid, kp->kp_uid, f);
153                         kinfo_file++;
154                         found++;
155
156                         if (found == nfiles) {
157                                 nfiles *= 2;
158                                 size = nfiles * sizeof(struct kinfo_file);
159
160                                 kd->argspc = _kvm_realloc(kd, kd->argspc, size);
161                                 if (kd->argspc == NULL) {
162                                         free(fd_files);
163                                         return 0;
164                                 }
165                                 kd->arglen = size;
166
167                                 kinfo_file = (struct kinfo_file *)kd->argspc;
168                                 kinfo_file += found;
169                         }
170                 }
171         }
172         free(fd_files);
173         return found;
174 }
175
176 struct kinfo_file *
177 kvm_getfiles(kvm_t *kd, int op __unused, int arg __unused, int *cnt)
178 {
179         int nfiles;
180         size_t size;
181
182         if (kvm_ishost(kd)) {
183                 int mib[2], st;
184
185                 size = 0;
186                 mib[0] = CTL_KERN;
187                 mib[1] = KERN_FILE;
188                 st = sysctl(mib, 2, NULL, &size, NULL, 0);
189                 if (st == -1) {
190                         _kvm_syserr(kd, kd->program, "sysctl KERN_FILE failed");
191                         return NULL;
192                 }
193                 if (kd->argspc == NULL)
194                         kd->argspc = _kvm_malloc(kd, size);
195                 else if (kd->arglen < (ssize_t)size)
196                         kd->argspc = _kvm_realloc(kd, kd->argspc, size);
197                 if (kd->argspc == NULL)
198                         return NULL;
199                 kd->arglen = size;
200                 st = sysctl(mib, 2, kd->argspc, &size, NULL, 0);
201                 if (st == -1 || size % sizeof(struct kinfo_file) != 0) {
202                         _kvm_syserr(kd, kd->program, "sysctl KERN_FILE failed");
203                         return NULL;
204                 }
205                 nfiles = size / sizeof(struct kinfo_file);
206         } else {
207                 struct kinfo_proc *kproc0, *kproc;
208                 int kproc_cnt, kproc_len;
209                 struct nlist nl[2];
210
211                 /*
212                  * Get all processes and save them in kproc.
213                  */
214                 kproc0 = kvm_getprocs(kd, KERN_PROC_ALL, 0, &kproc_cnt);
215                 if (kproc0 == NULL)
216                         return NULL;
217                 kproc_len = kproc_cnt * sizeof(struct kinfo_proc);
218                 kproc = malloc(kproc_len);
219                 if (kproc == NULL) {
220                         _kvm_syserr(kd, kd->program,
221                             "malloc kinfo_proc failed");
222                         return NULL;
223                 }
224                 memcpy(kproc, kproc0, kproc_len);
225
226                 /*
227                  * Get the # of files
228                  */
229                 memset(nl, 0, sizeof(nl));
230                 nl[0].n_name = "_nfiles";
231                 if (kvm_nlist(kd, nl) != 0) {
232                         _kvm_err(kd, kd->program, "%s: no such symbol",
233                             nl[0].n_name);
234                         free(kproc);
235                         return NULL;
236                 }
237                 if (KREAD(kd, nl[0].n_value, &nfiles)) {
238                         _kvm_err(kd, kd->program, "can't read nfiles");
239                         free(kproc);
240                         return NULL;
241                 }
242
243                 /*
244                  * stdio/stderr/stdout are normally duplicated
245                  * across all processes.
246                  */
247                 nfiles += (kproc_cnt * 3);
248                 size = nfiles * sizeof(struct kinfo_file);
249                 if (kd->argspc == NULL)
250                         kd->argspc = _kvm_malloc(kd, size);
251                 else if (kd->arglen < (ssize_t)size)
252                         kd->argspc = _kvm_realloc(kd, kd->argspc, size);
253                 if (kd->argspc == NULL) {
254                         free(kproc);
255                         return NULL;
256                 }
257                 kd->arglen = size;
258
259                 nfiles = kvm_deadfiles(kd, kproc, kproc_cnt, nfiles);
260                 free(kproc);
261
262                 if (nfiles == 0)
263                         return NULL;
264         }
265         *cnt = nfiles;
266         return (struct kinfo_file *)(kd->argspc);
267 }