xref: /freebsd/usr.bin/lastcomm/readrec.c (revision d6b92ffa)
1 /*-
2  * Copyright (c) 2007 Diomidis Spinellis
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 AUTHOR 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 AUTHOR 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  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/acct.h>
35 
36 #include <errno.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <string.h>
40 
41 int	 readrec_forward(FILE *f, struct acctv3 *av2);
42 int	 readrec_backward(FILE *f, struct acctv3 *av2);
43 
44 /*
45  * Reverse offsetof: return the offset of field f
46  * from the end of the structure s.
47  */
48 #define roffsetof(s, f) (sizeof(s) - offsetof(s, f))
49 
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  */
56 static int
57 fread_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  */
75 static float
76 decode_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  */
92 static int
93 readrec_v1(FILE *f, struct acctv3 *av3)
94 {
95 	struct acctv1 av1;
96 	int rv;
97 
98 	if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF)
99 		return (EOF);
100 	av3->ac_zero = 0;
101 	av3->ac_version = 3;
102 	av3->ac_len = av3->ac_len2 = sizeof(*av3);
103 	memcpy(av3->ac_comm, av1.ac_comm, AC_COMM_LEN);
104 	av3->ac_utime = decode_comp(av1.ac_utime) * 1000000;
105 	av3->ac_stime = decode_comp(av1.ac_stime) * 1000000;
106 	av3->ac_etime = decode_comp(av1.ac_etime) * 1000000;
107 	av3->ac_btime = av1.ac_btime;
108 	av3->ac_uid = av1.ac_uid;
109 	av3->ac_gid = av1.ac_gid;
110 	av3->ac_mem = av1.ac_mem;
111 	av3->ac_io = decode_comp(av1.ac_io);
112 	av3->ac_tty = av1.ac_tty;
113 	av3->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  */
122 static int
123 readrec_v2(FILE *f, struct acctv3 *av3)
124 {
125 	struct acctv2 av2;
126 	int rv;
127 
128 	if ((rv = fread_record(&av2, sizeof(av2), f)) == EOF)
129 		return (EOF);
130 	av3->ac_zero = 0;
131 	av3->ac_version = 3;
132 	av3->ac_len = av3->ac_len2 = sizeof(*av3);
133 	memcpy(av3->ac_comm, av2.ac_comm, AC_COMM_LEN);
134 	av3->ac_utime = av2.ac_utime;
135 	av3->ac_stime = av2.ac_stime;
136 	av3->ac_etime = av2.ac_etime;
137 	av3->ac_btime = av2.ac_btime;
138 	av3->ac_uid = av2.ac_uid;
139 	av3->ac_gid = av2.ac_gid;
140 	av3->ac_mem = av2.ac_mem;
141 	av3->ac_io = av2.ac_io;
142 	av3->ac_tty = av2.ac_tty;
143 	av3->ac_flagx = av2.ac_flagx;
144 	return (0);
145 }
146 
147 /*
148  * Read an v2 accounting record stored at the current
149  * position of stream f.
150  * Return EOF on error or end-of-file.
151  */
152 static int
153 readrec_v3(FILE *f, struct acctv3 *av3)
154 {
155 
156 	return (fread_record(av3, sizeof(*av3), f));
157 }
158 
159 /*
160  * Read a new-style (post-v1) accounting record stored at
161  * the current position of stream f.
162  * Convert the data to the current record format.
163  * Return EOF on error or end-of-file.
164  */
165 static int
166 readrec_vx(FILE *f, struct acctv3 *av3)
167 {
168 	uint8_t magic, version;
169 
170 	if (fread_record(&magic, sizeof(magic), f) == EOF ||
171 	    fread_record(&version, sizeof(version), f) == EOF ||
172 	    ungetc(version, f) == EOF ||
173 	    ungetc(magic, f) == EOF)
174 		return (EOF);
175 	switch (version) {
176 	case 2:
177 		return (readrec_v2(f, av3));
178 	case 3:
179 		return (readrec_v3(f, av3));
180 
181 	/* Add handling for more versions here. */
182 
183 	default:
184 		errno = EFTYPE;
185 		return (EOF);
186 	}
187 }
188 
189 /*
190  * Read an accounting record stored at the current
191  * position of stream f.
192  * Old-format records are converted to the current record
193  * format.
194  * Return the number of records read (1 or 0 at the end-of-file),
195  * or EOF on error.
196  */
197 int
198 readrec_forward(FILE *f, struct acctv3 *av3)
199 {
200 	int magic, rv;
201 
202 	if ((magic = getc(f)) == EOF)
203 		return (ferror(f) ? EOF : 0);
204 	if (ungetc(magic, f) == EOF)
205 		return (EOF);
206 	if (magic != 0)
207 		/* Old record format. */
208 		rv = readrec_v1(f, av3);
209 	else
210 		/* New record formats. */
211 		rv = readrec_vx(f, av3);
212 	return (rv == EOF ? EOF : 1);
213 }
214 
215 /*
216  * Read an accounting record ending at the current
217  * position of stream f.
218  * Old-format records are converted to the current record
219  * format.
220  * The file pointer is positioned at the beginning of the
221  * record read.
222  * Return the number of records read (1 or 0 at the end-of-file),
223  * or EOF on error.
224  */
225 int
226 readrec_backward(FILE *f, struct acctv3 *av3)
227 {
228 	off_t pos;
229 	int c;
230 	uint16_t len;
231 
232 	if ((pos = ftell(f)) == -1)
233 		return (EOF);
234 	if (pos == 0)
235 		return (0);
236 	if (fseek(f, -roffsetof(struct acctv3, ac_trailer),
237 	    SEEK_CUR) == EOF ||
238 	    (c = getc(f)) == EOF)
239 		return (EOF);
240 	if (c & ANVER) {
241 		/*
242 		 * New record formats.  For v2 and v3 offset from the
243 		 * end for ac_len2 should be same.
244 		 */
245 		if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2),
246 		    SEEK_SET) == EOF ||
247 		    fread_record(&len, sizeof(len), f) == EOF ||
248 		    fseeko(f, pos - len, SEEK_SET) == EOF ||
249 		    readrec_vx(f, av3) == EOF ||
250 		    fseeko(f, pos - len, SEEK_SET) == EOF)
251 			return (EOF);
252 		else
253 			return (1);
254 	} else {
255 		/* Old record format. */
256 		if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF ||
257 		    readrec_v1(f, av3) == EOF ||
258 		    fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF)
259 			return (EOF);
260 		else
261 			return (1);
262 	}
263 }
264