136108Sjb/*-
250472Speter * Copyright (c) 2007 Diomidis Spinellis
336108Sjb * All rights reserved.
436108Sjb *
543818Swes * Redistribution and use in source and binary forms, with or without
643818Swes * modification, are permitted provided that the following conditions
750177Shoek * are met:
843818Swes * 1. Redistributions of source code must retain the above copyright
943818Swes *    notice, this list of conditions and the following disclaimer.
1043818Swes * 2. Redistributions in binary form must reproduce the above copyright
1143818Swes *    notice, this list of conditions and the following disclaimer in the
1243818Swes *    documentation and/or other materials provided with the distribution.
1350177Shoek *
1443818Swes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1543818Swes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1643818Swes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750177Shoek * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18199243Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19220154Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20220154Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2143818Swes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2243818Swes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350177Shoek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450177Shoek * SUCH DAMAGE.
2543818Swes *
2636108Sjb */
2736108Sjb
2850177Shoek#include <sys/cdefs.h>
2950177Shoek__FBSDID("$FreeBSD$");
3056805Sobrien
3136108Sjb#include <sys/param.h>
32199243Sed#include <sys/stat.h>
3336108Sjb#include <sys/types.h>
34199243Sed#include <sys/acct.h>
35199243Sed
36199243Sed#include <errno.h>
37199243Sed#include <stddef.h>
38199243Sed#include <stdio.h>
39199243Sed#include <string.h>
40199243Sed
41170088Sdougbint	 readrec_forward(FILE *f, struct acctv2 *av2);
4236108Sjbint	 readrec_backward(FILE *f, struct acctv2 *av2);
4336108Sjb
44267236Snwhitehorn/*
45180487Sed * Reverse offsetof: return the offset of field f
46180487Sed * from the end of the structure s.
47180487Sed */
48121468Ssimokawa#define roffsetof(s, f) (sizeof(s) - offsetof(s, f))
49121468Ssimokawa
50/*
51 * Read exactly one record of size size from stream f into ptr.
52 * Failure to read the complete record is considered a file format error,
53 * and will set errno to EFTYPE.
54 * Return 0 on success, EOF on end of file or error.
55 */
56static int
57fread_record(void *ptr, size_t size, FILE *f)
58{
59	size_t rv;
60
61	if ((rv = fread(ptr, 1, size, f)) == size)
62		return (0);
63	else if (ferror(f) || rv == 0)
64		return (EOF);
65	else {
66		/* Short read. */
67		errno = EFTYPE;
68		return (EOF);
69	}
70}
71
72/*
73 * Return the value of a comp_t field.
74 */
75static float
76decode_comp(comp_t v)
77{
78	int result, exp;
79
80	result = v & 017777;
81	for (exp = v >> 13; exp; exp--)
82		result <<= 3;
83	return ((double)result / AHZV1);
84}
85
86/*
87 * Read a v1 accounting record stored at the current
88 * position of stream f.
89 * Convert the data to the current record format.
90 * Return EOF on error or end-of-file.
91 */
92static int
93readrec_v1(FILE *f, struct acctv2 *av2)
94{
95	struct acctv1 av1;
96	int rv;
97
98	if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF)
99		return (EOF);
100	av2->ac_zero = 0;
101	av2->ac_version = 2;
102	av2->ac_len = av2->ac_len2 = sizeof(*av2);
103	memcpy(av2->ac_comm, av1.ac_comm, AC_COMM_LEN);
104	av2->ac_utime = decode_comp(av1.ac_utime) * 1000000;
105	av2->ac_stime = decode_comp(av1.ac_stime) * 1000000;
106	av2->ac_etime = decode_comp(av1.ac_etime) * 1000000;
107	av2->ac_btime = av1.ac_btime;
108	av2->ac_uid = av1.ac_uid;
109	av2->ac_gid = av1.ac_gid;
110	av2->ac_mem = av1.ac_mem;
111	av2->ac_io = decode_comp(av1.ac_io);
112	av2->ac_tty = av1.ac_tty;
113	av2->ac_flagx = av1.ac_flag | ANVER;
114	return (0);
115}
116
117/*
118 * Read an v2 accounting record stored at the current
119 * position of stream f.
120 * Return EOF on error or end-of-file.
121 */
122static int
123readrec_v2(FILE *f, struct acctv2 *av2)
124{
125	return (fread_record(av2, sizeof(*av2), f));
126}
127
128/*
129 * Read a new-style (post-v1) accounting record stored at
130 * the current position of stream f.
131 * Convert the data to the current record format.
132 * Return EOF on error or end-of-file.
133 */
134static int
135readrec_vx(FILE *f, struct acctv2 *av2)
136{
137	uint8_t magic, version;
138
139	if (fread_record(&magic, sizeof(magic), f) == EOF ||
140	    fread_record(&version, sizeof(version), f) == EOF ||
141	    ungetc(version, f) == EOF ||
142	    ungetc(magic, f) == EOF)
143		return (EOF);
144	switch (version) {
145	case 2:
146		return (readrec_v2(f, av2));
147
148	/* Add handling for more versions here. */
149
150	default:
151		errno = EFTYPE;
152		return (EOF);
153	}
154}
155
156/*
157 * Read an accounting record stored at the current
158 * position of stream f.
159 * Old-format records are converted to the current record
160 * format.
161 * Return the number of records read (1 or 0 at the end-of-file),
162 * or EOF on error.
163 */
164int
165readrec_forward(FILE *f, struct acctv2 *av2)
166{
167	int magic, rv;
168
169	if ((magic = getc(f)) == EOF)
170		return (ferror(f) ? EOF : 0);
171	if (ungetc(magic, f) == EOF)
172		return (EOF);
173	if (magic != 0)
174		/* Old record format. */
175		rv = readrec_v1(f, av2);
176	else
177		/* New record formats. */
178		rv = readrec_vx(f, av2);
179	return (rv == EOF ? EOF : 1);
180}
181
182/*
183 * Read an accounting record ending at the current
184 * position of stream f.
185 * Old-format records are converted to the current record
186 * format.
187 * The file pointer is positioned at the beginning of the
188 * record read.
189 * Return the number of records read (1 or 0 at the end-of-file),
190 * or EOF on error.
191 */
192int
193readrec_backward(FILE *f, struct acctv2 *av2)
194{
195	off_t pos;
196	int c;
197	uint16_t len;
198
199	if ((pos = ftell(f)) == -1)
200		return (EOF);
201	if (pos == 0)
202		return (0);
203	if (fseek(f, -roffsetof(struct acctv2, ac_trailer),
204	    SEEK_CUR) == EOF ||
205	    (c = getc(f)) == EOF)
206		return (EOF);
207	if (c & ANVER) {
208		/* New record formats. */
209		if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2),
210		    SEEK_SET) == EOF ||
211		    fread_record(&len, sizeof(len), f) == EOF ||
212		    fseeko(f, pos - len, SEEK_SET) == EOF ||
213		    readrec_vx(f, av2) == EOF ||
214		    fseeko(f, pos - len, SEEK_SET) == EOF)
215			return (EOF);
216		else
217			return (1);
218	} else {
219		/* Old record format. */
220		if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF ||
221		    readrec_v1(f, av2) == EOF ||
222		    fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF)
223			return (EOF);
224		else
225			return (1);
226	}
227}
228