core.c revision 249672
1249666Strociny/*-
2249666Strociny * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
3249666Strociny * All rights reserved.
4249666Strociny *
5249666Strociny * Redistribution and use in source and binary forms, with or without
6249666Strociny * modification, are permitted provided that the following conditions
7249666Strociny * are met:
8249666Strociny * 1. Redistributions of source code must retain the above copyright
9249666Strociny *    notice, this list of conditions and the following disclaimer.
10249666Strociny * 2. Redistributions in binary form must reproduce the above copyright
11249666Strociny *    notice, this list of conditions and the following disclaimer in the
12249666Strociny *    documentation and/or other materials provided with the distribution.
13249666Strociny *
14249666Strociny * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15249666Strociny * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16249666Strociny * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17249666Strociny * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18249666Strociny * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19249666Strociny * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20249666Strociny * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21249666Strociny * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22249666Strociny * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23249666Strociny * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24249666Strociny * SUCH DAMAGE.
25249666Strociny *
26249666Strociny * $FreeBSD: head/lib/libprocstat/core.c 249672 2013-04-20 07:57:08Z trociny $
27249666Strociny */
28249666Strociny
29249666Strociny#include <sys/param.h>
30249666Strociny#include <sys/elf.h>
31249666Strociny#include <sys/user.h>
32249666Strociny
33249666Strociny#include <assert.h>
34249666Strociny#include <err.h>
35249666Strociny#include <fcntl.h>
36249666Strociny#include <gelf.h>
37249666Strociny#include <libelf.h>
38249666Strociny#include <stdbool.h>
39249666Strociny#include <stdint.h>
40249666Strociny#include <stdio.h>
41249666Strociny#include <stdlib.h>
42249666Strociny#include <string.h>
43249666Strociny#include <unistd.h>
44249666Strociny
45249666Strociny#include "core.h"
46249666Strociny
47249666Strociny#define PROCSTAT_CORE_MAGIC	0x012DADB8
48249666Strocinystruct procstat_core
49249666Strociny{
50249666Strociny	int		pc_magic;
51249666Strociny	int		pc_fd;
52249666Strociny	Elf		*pc_elf;
53249666Strociny	GElf_Ehdr	pc_ehdr;
54249666Strociny	GElf_Phdr	pc_phdr;
55249666Strociny};
56249666Strociny
57249666Strocinystatic bool	core_offset(struct procstat_core *core, off_t offset);
58249666Strocinystatic bool	core_read(struct procstat_core *core, void *buf, size_t len);
59249666Strociny
60249666Strocinystruct procstat_core *
61249666Strocinyprocstat_core_open(const char *filename)
62249666Strociny{
63249666Strociny	struct procstat_core *core;
64249666Strociny	Elf *e;
65249666Strociny	GElf_Ehdr ehdr;
66249666Strociny	GElf_Phdr phdr;
67249666Strociny	size_t nph;
68249666Strociny	int fd, i;
69249666Strociny
70249666Strociny	if (elf_version(EV_CURRENT) == EV_NONE) {
71249666Strociny		warnx("ELF library too old");
72249666Strociny		return (NULL);
73249666Strociny	}
74249666Strociny	fd = open(filename, O_RDONLY, 0);
75249666Strociny	if (fd == -1) {
76249666Strociny		warn("open(%s)", filename);
77249666Strociny		return (NULL);
78249666Strociny	}
79249666Strociny	e = elf_begin(fd, ELF_C_READ, NULL);
80249666Strociny	if (e == NULL) {
81249666Strociny		warnx("elf_begin: %s", elf_errmsg(-1));
82249666Strociny		goto fail;
83249666Strociny	}
84249666Strociny	if (elf_kind(e) != ELF_K_ELF) {
85249666Strociny		warnx("%s is not an ELF object", filename);
86249666Strociny		goto fail;
87249666Strociny	}
88249666Strociny	if (gelf_getehdr(e, &ehdr) == NULL) {
89249666Strociny		warnx("gelf_getehdr: %s", elf_errmsg(-1));
90249666Strociny		goto fail;
91249666Strociny	}
92249666Strociny	if (ehdr.e_type != ET_CORE) {
93249666Strociny		warnx("%s is not a CORE file", filename);
94249666Strociny		goto fail;
95249666Strociny	}
96249666Strociny	if (elf_getphnum(e, &nph) == 0) {
97249666Strociny		warnx("program headers not found");
98249666Strociny		goto fail;
99249666Strociny	}
100249666Strociny	for (i = 0; i < ehdr.e_phnum; i++) {
101249666Strociny		if (gelf_getphdr(e, i, &phdr) != &phdr) {
102249666Strociny			warnx("gelf_getphdr: %s", elf_errmsg(-1));
103249666Strociny			goto fail;
104249666Strociny		}
105249666Strociny		if (phdr.p_type == PT_NOTE)
106249666Strociny			break;
107249666Strociny	}
108249666Strociny	if (i == ehdr.e_phnum) {
109249666Strociny		warnx("NOTE program header not found");
110249666Strociny		goto fail;
111249666Strociny	}
112249666Strociny	core = malloc(sizeof(struct procstat_core));
113249666Strociny	if (core == NULL) {
114249666Strociny		warn("malloc(%zu)", sizeof(struct procstat_core));
115249666Strociny		goto fail;
116249666Strociny	}
117249666Strociny	core->pc_magic = PROCSTAT_CORE_MAGIC;
118249666Strociny	core->pc_fd = fd;
119249666Strociny	core->pc_elf = e;
120249666Strociny	core->pc_ehdr = ehdr;
121249666Strociny	core->pc_phdr = phdr;
122249666Strociny
123249666Strociny	return (core);
124249666Strocinyfail:
125249666Strociny	if (e != NULL)
126249666Strociny		elf_end(e);
127249666Strociny	close(fd);
128249666Strociny
129249666Strociny	return (NULL);
130249666Strociny}
131249666Strociny
132249666Strocinyvoid
133249666Strocinyprocstat_core_close(struct procstat_core *core)
134249666Strociny{
135249666Strociny
136249666Strociny	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
137249666Strociny
138249666Strociny	elf_end(core->pc_elf);
139249666Strociny	close(core->pc_fd);
140249666Strociny	free(core);
141249666Strociny}
142249666Strociny
143249666Strocinyvoid *
144249666Strocinyprocstat_core_get(struct procstat_core *core, enum psc_type type, void *buf,
145249666Strociny    size_t *lenp)
146249666Strociny{
147249666Strociny	Elf_Note nhdr;
148249666Strociny	off_t offset, eoffset;
149249666Strociny	void *freebuf;
150249666Strociny	size_t len;
151249666Strociny	u_int32_t n_type;
152249666Strociny	int cstructsize, structsize;
153249666Strociny	char nbuf[8];
154249666Strociny
155249666Strociny	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
156249666Strociny
157249666Strociny	switch(type) {
158249666Strociny	case PSC_TYPE_PROC:
159249666Strociny		n_type = NT_PROCSTAT_PROC;
160249666Strociny		structsize = sizeof(struct kinfo_proc);
161249666Strociny		break;
162249666Strociny	case PSC_TYPE_FILES:
163249666Strociny		n_type = NT_PROCSTAT_FILES;
164249666Strociny		structsize = sizeof(struct kinfo_file);
165249666Strociny		break;
166249666Strociny	case PSC_TYPE_VMMAP:
167249666Strociny		n_type = NT_PROCSTAT_VMMAP;
168249666Strociny		structsize = sizeof(struct kinfo_vmentry);
169249666Strociny		break;
170249670Strociny	case PSC_TYPE_GROUPS:
171249670Strociny		n_type = NT_PROCSTAT_GROUPS;
172249670Strociny		structsize = sizeof(gid_t);
173249670Strociny		break;
174249672Strociny	case PSC_TYPE_UMASK:
175249672Strociny		n_type = NT_PROCSTAT_UMASK;
176249672Strociny		structsize = sizeof(u_short);
177249672Strociny		break;
178249666Strociny	default:
179249666Strociny		warnx("unknown core stat type: %d", type);
180249666Strociny		return (NULL);
181249666Strociny	}
182249666Strociny
183249666Strociny	offset = core->pc_phdr.p_offset;
184249666Strociny	eoffset = offset + core->pc_phdr.p_filesz;
185249666Strociny
186249666Strociny	while (offset < eoffset) {
187249666Strociny		if (!core_offset(core, offset))
188249666Strociny			return (NULL);
189249666Strociny		if (!core_read(core, &nhdr, sizeof(nhdr)))
190249666Strociny			return (NULL);
191249666Strociny
192249666Strociny		offset += sizeof(nhdr) +
193249666Strociny		    roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) +
194249666Strociny		    roundup2(nhdr.n_descsz, sizeof(Elf32_Size));
195249666Strociny
196249666Strociny		if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0)
197249666Strociny			break;
198249666Strociny		if (nhdr.n_type != n_type)
199249666Strociny			continue;
200249666Strociny		if (nhdr.n_namesz != 8)
201249666Strociny			continue;
202249666Strociny		if (!core_read(core, nbuf, sizeof(nbuf)))
203249666Strociny			return (NULL);
204249666Strociny		if (strcmp(nbuf, "FreeBSD") != 0)
205249666Strociny			continue;
206249666Strociny		if (nhdr.n_descsz < sizeof(cstructsize)) {
207249666Strociny			warnx("corrupted core file");
208249666Strociny			return (NULL);
209249666Strociny		}
210249666Strociny		if (!core_read(core, &cstructsize, sizeof(cstructsize)))
211249666Strociny			return (NULL);
212249666Strociny		if (cstructsize != structsize) {
213249666Strociny			warnx("version mismatch");
214249666Strociny			return (NULL);
215249666Strociny		}
216249666Strociny		len = nhdr.n_descsz - sizeof(cstructsize);
217249666Strociny		if (len == 0)
218249666Strociny			return (NULL);
219249666Strociny		if (buf != NULL) {
220249666Strociny			len = MIN(len, *lenp);
221249666Strociny			freebuf = NULL;
222249666Strociny		} else {
223249666Strociny			freebuf = buf = malloc(len);
224249666Strociny			if (buf == NULL) {
225249666Strociny				warn("malloc(%zu)", len);
226249666Strociny				return (NULL);
227249666Strociny			}
228249666Strociny		}
229249666Strociny		if (!core_read(core, buf, len)) {
230249666Strociny			free(freebuf);
231249666Strociny			return (NULL);
232249666Strociny		}
233249666Strociny		*lenp = len;
234249666Strociny		return (buf);
235249666Strociny        }
236249666Strociny
237249666Strociny	return (NULL);
238249666Strociny}
239249666Strociny
240249666Strocinystatic bool
241249666Strocinycore_offset(struct procstat_core *core, off_t offset)
242249666Strociny{
243249666Strociny
244249666Strociny	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
245249666Strociny
246249666Strociny	if (lseek(core->pc_fd, offset, SEEK_SET) == -1) {
247249666Strociny		warn("core: lseek(%jd)", (intmax_t)offset);
248249666Strociny		return (false);
249249666Strociny	}
250249666Strociny	return (true);
251249666Strociny}
252249666Strociny
253249666Strocinystatic bool
254249666Strocinycore_read(struct procstat_core *core, void *buf, size_t len)
255249666Strociny{
256249666Strociny	ssize_t n;
257249666Strociny
258249666Strociny	assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
259249666Strociny
260249666Strociny	n = read(core->pc_fd, buf, len);
261249666Strociny	if (n == -1) {
262249666Strociny		warn("core: read");
263249666Strociny		return (false);
264249666Strociny	}
265249666Strociny	if (n < (ssize_t)len) {
266249666Strociny		warnx("core: short read");
267249666Strociny		return (false);
268249666Strociny	}
269249666Strociny	return (true);
270249666Strociny}
271