xref: /freebsd/lib/libprocstat/core.c (revision eec6cb1cf20290299c34bcab79119823f6ac7a1f)
1 /*-
2  * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
3  * 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  *
14  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/elf.h>
31 #include <sys/user.h>
32 
33 #include <assert.h>
34 #include <err.h>
35 #include <fcntl.h>
36 #include <gelf.h>
37 #include <libelf.h>
38 #include <stdbool.h>
39 #include <stdint.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "core.h"
46 
47 #define PROCSTAT_CORE_MAGIC	0x012DADB8
48 struct procstat_core
49 {
50 	int		pc_magic;
51 	int		pc_fd;
52 	Elf		*pc_elf;
53 	GElf_Ehdr	pc_ehdr;
54 	GElf_Phdr	pc_phdr;
55 };
56 
57 static bool	core_offset(struct procstat_core *core, off_t offset);
58 static bool	core_read(struct procstat_core *core, void *buf, size_t len);
59 
60 struct procstat_core *
61 procstat_core_open(const char *filename)
62 {
63 	struct procstat_core *core;
64 	Elf *e;
65 	GElf_Ehdr ehdr;
66 	GElf_Phdr phdr;
67 	size_t nph;
68 	int fd, i;
69 
70 	if (elf_version(EV_CURRENT) == EV_NONE) {
71 		warnx("ELF library too old");
72 		return (NULL);
73 	}
74 	fd = open(filename, O_RDONLY, 0);
75 	if (fd == -1) {
76 		warn("open(%s)", filename);
77 		return (NULL);
78 	}
79 	e = elf_begin(fd, ELF_C_READ, NULL);
80 	if (e == NULL) {
81 		warnx("elf_begin: %s", elf_errmsg(-1));
82 		goto fail;
83 	}
84 	if (elf_kind(e) != ELF_K_ELF) {
85 		warnx("%s is not an ELF object", filename);
86 		goto fail;
87 	}
88 	if (gelf_getehdr(e, &ehdr) == NULL) {
89 		warnx("gelf_getehdr: %s", elf_errmsg(-1));
90 		goto fail;
91 	}
92 	if (ehdr.e_type != ET_CORE) {
93 		warnx("%s is not a CORE file", filename);
94 		goto fail;
95 	}
96 	if (elf_getphnum(e, &nph) == 0) {
97 		warnx("program headers not found");
98 		goto fail;
99 	}
100 	for (i = 0; i < ehdr.e_phnum; i++) {
101 		if (gelf_getphdr(e, i, &phdr) != &phdr) {
102 			warnx("gelf_getphdr: %s", elf_errmsg(-1));
103 			goto fail;
104 		}
105 		if (phdr.p_type == PT_NOTE)
106 			break;
107 	}
108 	if (i == ehdr.e_phnum) {
109 		warnx("NOTE program header not found");
110 		goto fail;
111 	}
112 	core = malloc(sizeof(struct procstat_core));
113 	if (core == NULL) {
114 		warn("malloc(%zu)", sizeof(struct procstat_core));
115 		goto fail;
116 	}
117 	core->pc_magic = PROCSTAT_CORE_MAGIC;
118 	core->pc_fd = fd;
119 	core->pc_elf = e;
120 	core->pc_ehdr = ehdr;
121 	core->pc_phdr = phdr;
122 
123 	return (core);
124 fail:
125 	if (e != NULL)
126 		elf_end(e);
127 	close(fd);
128 
129 	return (NULL);
130 }
131 
132 void
133 procstat_core_close(struct procstat_core *core)
134 {
135 
136 	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
137 
138 	elf_end(core->pc_elf);
139 	close(core->pc_fd);
140 	free(core);
141 }
142 
143 void *
144 procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf,
145     size_t *lenp)
146 {
147 	Elf_Note nhdr;
148 	off_t offset, eoffset;
149 	void *freebuf;
150 	size_t len;
151 	u_int32_t n_type;
152 	int cstructsize, structsize;
153 	char nbuf[8];
154 
155 	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
156 
157 	switch(type) {
158 	case PSC_TYPE_PROC:
159 		n_type = NT_PROCSTAT_PROC;
160 		structsize = sizeof(struct kinfo_proc);
161 		break;
162 	case PSC_TYPE_FILES:
163 		n_type = NT_PROCSTAT_FILES;
164 		structsize = sizeof(struct kinfo_file);
165 		break;
166 	case PSC_TYPE_VMMAP:
167 		n_type = NT_PROCSTAT_VMMAP;
168 		structsize = sizeof(struct kinfo_vmentry);
169 		break;
170 	case PSC_TYPE_GROUPS:
171 		n_type = NT_PROCSTAT_GROUPS;
172 		structsize = sizeof(gid_t);
173 		break;
174 	case PSC_TYPE_UMASK:
175 		n_type = NT_PROCSTAT_UMASK;
176 		structsize = sizeof(u_short);
177 		break;
178 	case PSC_TYPE_RLIMIT:
179 		n_type = NT_PROCSTAT_RLIMIT;
180 		structsize = sizeof(struct rlimit) * RLIM_NLIMITS;
181 		break;
182 	case PSC_TYPE_OSREL:
183 		n_type = NT_PROCSTAT_OSREL;
184 		structsize = sizeof(int);
185 		break;
186 	default:
187 		warnx("unknown core stat type: %d", type);
188 		return (NULL);
189 	}
190 
191 	offset = core->pc_phdr.p_offset;
192 	eoffset = offset + core->pc_phdr.p_filesz;
193 
194 	while (offset < eoffset) {
195 		if (!core_offset(core, offset))
196 			return (NULL);
197 		if (!core_read(core, &nhdr, sizeof(nhdr)))
198 			return (NULL);
199 
200 		offset += sizeof(nhdr) +
201 		    roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) +
202 		    roundup2(nhdr.n_descsz, sizeof(Elf32_Size));
203 
204 		if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0)
205 			break;
206 		if (nhdr.n_type != n_type)
207 			continue;
208 		if (nhdr.n_namesz != 8)
209 			continue;
210 		if (!core_read(core, nbuf, sizeof(nbuf)))
211 			return (NULL);
212 		if (strcmp(nbuf, "FreeBSD") != 0)
213 			continue;
214 		if (nhdr.n_descsz < sizeof(cstructsize)) {
215 			warnx("corrupted core file");
216 			return (NULL);
217 		}
218 		if (!core_read(core, &cstructsize, sizeof(cstructsize)))
219 			return (NULL);
220 		if (cstructsize != structsize) {
221 			warnx("version mismatch");
222 			return (NULL);
223 		}
224 		len = nhdr.n_descsz - sizeof(cstructsize);
225 		if (len == 0)
226 			return (NULL);
227 		if (buf != NULL) {
228 			len = MIN(len, *lenp);
229 			freebuf = NULL;
230 		} else {
231 			freebuf = buf = malloc(len);
232 			if (buf == NULL) {
233 				warn("malloc(%zu)", len);
234 				return (NULL);
235 			}
236 		}
237 		if (!core_read(core, buf, len)) {
238 			free(freebuf);
239 			return (NULL);
240 		}
241 		*lenp = len;
242 		return (buf);
243         }
244 
245 	return (NULL);
246 }
247 
248 static bool
249 core_offset(struct procstat_core *core, off_t offset)
250 {
251 
252 	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
253 
254 	if (lseek(core->pc_fd, offset, SEEK_SET) == -1) {
255 		warn("core: lseek(%jd)", (intmax_t)offset);
256 		return (false);
257 	}
258 	return (true);
259 }
260 
261 static bool
262 core_read(struct procstat_core *core, void *buf, size_t len)
263 {
264 	ssize_t n;
265 
266 	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
267 
268 	n = read(core->pc_fd, buf, len);
269 	if (n == -1) {
270 		warn("core: read");
271 		return (false);
272 	}
273 	if (n < (ssize_t)len) {
274 		warnx("core: short read");
275 		return (false);
276 	}
277 	return (true);
278 }
279