1169857Sdds/*-
2169857Sdds * Copyright (c) 2007 Diomidis Spinellis
3169857Sdds * All rights reserved.
4169857Sdds *
5169857Sdds * Redistribution and use in source and binary forms, with or without
6169857Sdds * modification, are permitted provided that the following conditions
7169857Sdds * are met:
8169857Sdds * 1. Redistributions of source code must retain the above copyright
9169857Sdds *    notice, this list of conditions and the following disclaimer.
10169857Sdds * 2. Redistributions in binary form must reproduce the above copyright
11169857Sdds *    notice, this list of conditions and the following disclaimer in the
12169857Sdds *    documentation and/or other materials provided with the distribution.
13169857Sdds *
14169857Sdds * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15169857Sdds * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16169857Sdds * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17169857Sdds * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18169857Sdds * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19169857Sdds * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169857Sdds * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21169857Sdds * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22169857Sdds * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23169857Sdds * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24169857Sdds * SUCH DAMAGE.
25169857Sdds *
26169857Sdds */
27169857Sdds
28169857Sdds#include <sys/cdefs.h>
29169857Sdds__FBSDID("$FreeBSD$");
30169857Sdds
31169857Sdds#include <sys/param.h>
32169857Sdds#include <sys/stat.h>
33169857Sdds#include <sys/types.h>
34169857Sdds#include <sys/acct.h>
35169857Sdds
36169857Sdds#include <errno.h>
37169857Sdds#include <stddef.h>
38169857Sdds#include <stdio.h>
39169857Sdds#include <string.h>
40169857Sdds
41169857Sddsint	 readrec_forward(FILE *f, struct acctv2 *av2);
42169857Sddsint	 readrec_backward(FILE *f, struct acctv2 *av2);
43169857Sdds
44169857Sdds/*
45169857Sdds * Reverse offsetof: return the offset of field f
46169857Sdds * from the end of the structure s.
47169857Sdds */
48169857Sdds#define roffsetof(s, f) (sizeof(s) - offsetof(s, f))
49169857Sdds
50169857Sdds/*
51169857Sdds * Read exactly one record of size size from stream f into ptr.
52169857Sdds * Failure to read the complete record is considered a file format error,
53169857Sdds * and will set errno to EFTYPE.
54169857Sdds * Return 0 on success, EOF on end of file or error.
55169857Sdds */
56169857Sddsstatic int
57169857Sddsfread_record(void *ptr, size_t size, FILE *f)
58169857Sdds{
59169857Sdds	size_t rv;
60169857Sdds
61169857Sdds	if ((rv = fread(ptr, 1, size, f)) == size)
62169857Sdds		return (0);
63169857Sdds	else if (ferror(f) || rv == 0)
64169857Sdds		return (EOF);
65169857Sdds	else {
66169857Sdds		/* Short read. */
67169857Sdds		errno = EFTYPE;
68169857Sdds		return (EOF);
69169857Sdds	}
70169857Sdds}
71169857Sdds
72169857Sdds/*
73169857Sdds * Return the value of a comp_t field.
74169857Sdds */
75169857Sddsstatic float
76169857Sddsdecode_comp(comp_t v)
77169857Sdds{
78169857Sdds	int result, exp;
79169857Sdds
80169857Sdds	result = v & 017777;
81169857Sdds	for (exp = v >> 13; exp; exp--)
82169857Sdds		result <<= 3;
83169857Sdds	return ((double)result / AHZV1);
84169857Sdds}
85169857Sdds
86169857Sdds/*
87169857Sdds * Read a v1 accounting record stored at the current
88169857Sdds * position of stream f.
89169857Sdds * Convert the data to the current record format.
90169857Sdds * Return EOF on error or end-of-file.
91169857Sdds */
92169857Sddsstatic int
93169857Sddsreadrec_v1(FILE *f, struct acctv2 *av2)
94169857Sdds{
95169857Sdds	struct acctv1 av1;
96169857Sdds	int rv;
97169857Sdds
98169857Sdds	if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF)
99169857Sdds		return (EOF);
100169857Sdds	av2->ac_zero = 0;
101169857Sdds	av2->ac_version = 2;
102169857Sdds	av2->ac_len = av2->ac_len2 = sizeof(*av2);
103169857Sdds	memcpy(av2->ac_comm, av1.ac_comm, AC_COMM_LEN);
104169857Sdds	av2->ac_utime = decode_comp(av1.ac_utime) * 1000000;
105169857Sdds	av2->ac_stime = decode_comp(av1.ac_stime) * 1000000;
106169857Sdds	av2->ac_etime = decode_comp(av1.ac_etime) * 1000000;
107169857Sdds	av2->ac_btime = av1.ac_btime;
108169857Sdds	av2->ac_uid = av1.ac_uid;
109169857Sdds	av2->ac_gid = av1.ac_gid;
110169857Sdds	av2->ac_mem = av1.ac_mem;
111169857Sdds	av2->ac_io = decode_comp(av1.ac_io);
112169857Sdds	av2->ac_tty = av1.ac_tty;
113169857Sdds	av2->ac_flagx = av1.ac_flag | ANVER;
114169857Sdds	return (0);
115169857Sdds}
116169857Sdds
117169857Sdds/*
118169857Sdds * Read an v2 accounting record stored at the current
119169857Sdds * position of stream f.
120169857Sdds * Return EOF on error or end-of-file.
121169857Sdds */
122169857Sddsstatic int
123169857Sddsreadrec_v2(FILE *f, struct acctv2 *av2)
124169857Sdds{
125169857Sdds	return (fread_record(av2, sizeof(*av2), f));
126169857Sdds}
127169857Sdds
128169857Sdds/*
129169857Sdds * Read a new-style (post-v1) accounting record stored at
130169857Sdds * the current position of stream f.
131169857Sdds * Convert the data to the current record format.
132169857Sdds * Return EOF on error or end-of-file.
133169857Sdds */
134169857Sddsstatic int
135169857Sddsreadrec_vx(FILE *f, struct acctv2 *av2)
136169857Sdds{
137169857Sdds	uint8_t magic, version;
138169857Sdds
139169857Sdds	if (fread_record(&magic, sizeof(magic), f) == EOF ||
140169857Sdds	    fread_record(&version, sizeof(version), f) == EOF ||
141169857Sdds	    ungetc(version, f) == EOF ||
142169857Sdds	    ungetc(magic, f) == EOF)
143169857Sdds		return (EOF);
144169857Sdds	switch (version) {
145169857Sdds	case 2:
146169857Sdds		return (readrec_v2(f, av2));
147169857Sdds
148169857Sdds	/* Add handling for more versions here. */
149169857Sdds
150169857Sdds	default:
151169857Sdds		errno = EFTYPE;
152169857Sdds		return (EOF);
153169857Sdds	}
154169857Sdds}
155169857Sdds
156169857Sdds/*
157169857Sdds * Read an accounting record stored at the current
158169857Sdds * position of stream f.
159169857Sdds * Old-format records are converted to the current record
160169857Sdds * format.
161169857Sdds * Return the number of records read (1 or 0 at the end-of-file),
162169857Sdds * or EOF on error.
163169857Sdds */
164169857Sddsint
165169857Sddsreadrec_forward(FILE *f, struct acctv2 *av2)
166169857Sdds{
167169857Sdds	int magic, rv;
168169857Sdds
169169857Sdds	if ((magic = getc(f)) == EOF)
170169857Sdds		return (ferror(f) ? EOF : 0);
171169857Sdds	if (ungetc(magic, f) == EOF)
172169857Sdds		return (EOF);
173169857Sdds	if (magic != 0)
174169857Sdds		/* Old record format. */
175169857Sdds		rv = readrec_v1(f, av2);
176169857Sdds	else
177169857Sdds		/* New record formats. */
178169857Sdds		rv = readrec_vx(f, av2);
179169857Sdds	return (rv == EOF ? EOF : 1);
180169857Sdds}
181169857Sdds
182169857Sdds/*
183169857Sdds * Read an accounting record ending at the current
184169857Sdds * position of stream f.
185169857Sdds * Old-format records are converted to the current record
186169857Sdds * format.
187169857Sdds * The file pointer is positioned at the beginning of the
188169857Sdds * record read.
189169857Sdds * Return the number of records read (1 or 0 at the end-of-file),
190169857Sdds * or EOF on error.
191169857Sdds */
192169857Sddsint
193169857Sddsreadrec_backward(FILE *f, struct acctv2 *av2)
194169857Sdds{
195169857Sdds	off_t pos;
196169857Sdds	int c;
197169857Sdds	uint16_t len;
198169857Sdds
199169857Sdds	if ((pos = ftell(f)) == -1)
200169857Sdds		return (EOF);
201169857Sdds	if (pos == 0)
202169857Sdds		return (0);
203169857Sdds	if (fseek(f, -roffsetof(struct acctv2, ac_trailer),
204169857Sdds	    SEEK_CUR) == EOF ||
205169857Sdds	    (c = getc(f)) == EOF)
206169857Sdds		return (EOF);
207169857Sdds	if (c & ANVER) {
208169857Sdds		/* New record formats. */
209169857Sdds		if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2),
210169857Sdds		    SEEK_SET) == EOF ||
211169857Sdds		    fread_record(&len, sizeof(len), f) == EOF ||
212169857Sdds		    fseeko(f, pos - len, SEEK_SET) == EOF ||
213169857Sdds		    readrec_vx(f, av2) == EOF ||
214169857Sdds		    fseeko(f, pos - len, SEEK_SET) == EOF)
215169857Sdds			return (EOF);
216169857Sdds		else
217169857Sdds			return (1);
218169857Sdds	} else {
219169857Sdds		/* Old record format. */
220169857Sdds		if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF ||
221169857Sdds		    readrec_v1(f, av2) == EOF ||
222169857Sdds		    fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF)
223169857Sdds			return (EOF);
224169857Sdds		else
225169857Sdds			return (1);
226169857Sdds	}
227169857Sdds}
228