xref: /freebsd/usr.bin/lastcomm/readrec.c (revision 40a8ac8f62b535d30349faf28cf47106b7041b83)
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 acctv2 *av2);
42 int	 readrec_backward(FILE *f, struct acctv2 *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 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  */
122 static int
123 readrec_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  */
134 static int
135 readrec_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  */
164 int
165 readrec_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  */
192 int
193 readrec_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