trace.c revision 1.2.2.2 1 1.2.2.2 pgoyette /* $NetBSD: trace.c,v 1.2.2.2 2018/04/16 01:59:48 pgoyette Exp $ */
2 1.2.2.2 pgoyette
3 1.2.2.2 pgoyette /* trace.c
4 1.2.2.2 pgoyette
5 1.2.2.2 pgoyette Subroutines that support tracing of OMAPI wire transactions and
6 1.2.2.2 pgoyette provide a mechanism for programs using OMAPI to trace their own
7 1.2.2.2 pgoyette transactions... */
8 1.2.2.2 pgoyette
9 1.2.2.2 pgoyette /*
10 1.2.2.2 pgoyette * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
11 1.2.2.2 pgoyette * Copyright (c) 2001-2003 by Internet Software Consortium
12 1.2.2.2 pgoyette *
13 1.2.2.2 pgoyette * This Source Code Form is subject to the terms of the Mozilla Public
14 1.2.2.2 pgoyette * License, v. 2.0. If a copy of the MPL was not distributed with this
15 1.2.2.2 pgoyette * file, You can obtain one at http://mozilla.org/MPL/2.0/.
16 1.2.2.2 pgoyette *
17 1.2.2.2 pgoyette * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
18 1.2.2.2 pgoyette * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19 1.2.2.2 pgoyette * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
20 1.2.2.2 pgoyette * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21 1.2.2.2 pgoyette * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22 1.2.2.2 pgoyette * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
23 1.2.2.2 pgoyette * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 1.2.2.2 pgoyette *
25 1.2.2.2 pgoyette * Internet Systems Consortium, Inc.
26 1.2.2.2 pgoyette * 950 Charter Street
27 1.2.2.2 pgoyette * Redwood City, CA 94063
28 1.2.2.2 pgoyette * <info (at) isc.org>
29 1.2.2.2 pgoyette * https://www.isc.org/
30 1.2.2.2 pgoyette *
31 1.2.2.2 pgoyette */
32 1.2.2.2 pgoyette
33 1.2.2.2 pgoyette #include <sys/cdefs.h>
34 1.2.2.2 pgoyette __RCSID("$NetBSD: trace.c,v 1.2.2.2 2018/04/16 01:59:48 pgoyette Exp $");
35 1.2.2.2 pgoyette
36 1.2.2.2 pgoyette #include "dhcpd.h"
37 1.2.2.2 pgoyette #include <omapip/omapip_p.h>
38 1.2.2.2 pgoyette #include <errno.h>
39 1.2.2.2 pgoyette
40 1.2.2.2 pgoyette #if defined (TRACING)
41 1.2.2.2 pgoyette void (*trace_set_time_hook) (TIME);
42 1.2.2.2 pgoyette static int tracing_stopped;
43 1.2.2.2 pgoyette static int traceoutfile;
44 1.2.2.2 pgoyette static int traceindex;
45 1.2.2.2 pgoyette static trace_type_t **trace_types;
46 1.2.2.2 pgoyette static int trace_type_count;
47 1.2.2.2 pgoyette static int trace_type_max;
48 1.2.2.2 pgoyette static trace_type_t *new_trace_types;
49 1.2.2.2 pgoyette static FILE *traceinfile;
50 1.2.2.2 pgoyette static tracefile_header_t tracefile_header;
51 1.2.2.2 pgoyette static int trace_playback_flag;
52 1.2.2.2 pgoyette trace_type_t trace_time_marker;
53 1.2.2.2 pgoyette
54 1.2.2.2 pgoyette #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
55 1.2.2.2 pgoyette extern omapi_array_t *trace_listeners;
56 1.2.2.2 pgoyette extern omapi_array_t *omapi_connections;
57 1.2.2.2 pgoyette
58 1.2.2.2 pgoyette extern int errno;
59 1.2.2.2 pgoyette
60 1.2.2.2 pgoyette void trace_free_all ()
61 1.2.2.2 pgoyette {
62 1.2.2.2 pgoyette trace_type_t *tp;
63 1.2.2.2 pgoyette int i;
64 1.2.2.2 pgoyette tp = new_trace_types;
65 1.2.2.2 pgoyette while (tp) {
66 1.2.2.2 pgoyette new_trace_types = tp -> next;
67 1.2.2.2 pgoyette if (tp -> name) {
68 1.2.2.2 pgoyette dfree (tp -> name, MDL);
69 1.2.2.2 pgoyette tp -> name = (char *)0;
70 1.2.2.2 pgoyette }
71 1.2.2.2 pgoyette dfree (tp, MDL);
72 1.2.2.2 pgoyette tp = new_trace_types;
73 1.2.2.2 pgoyette }
74 1.2.2.2 pgoyette for (i = 0; i < trace_type_count; i++) {
75 1.2.2.2 pgoyette if (trace_types [i]) {
76 1.2.2.2 pgoyette if (trace_types [i] -> name)
77 1.2.2.2 pgoyette dfree (trace_types [i] -> name, MDL);
78 1.2.2.2 pgoyette dfree (trace_types [i], MDL);
79 1.2.2.2 pgoyette }
80 1.2.2.2 pgoyette }
81 1.2.2.2 pgoyette dfree (trace_types, MDL);
82 1.2.2.2 pgoyette trace_types = (trace_type_t **)0;
83 1.2.2.2 pgoyette trace_type_count = trace_type_max = 0;
84 1.2.2.2 pgoyette
85 1.2.2.2 pgoyette omapi_array_free (&trace_listeners, MDL);
86 1.2.2.2 pgoyette omapi_array_free (&omapi_connections, MDL);
87 1.2.2.2 pgoyette }
88 1.2.2.2 pgoyette #endif
89 1.2.2.2 pgoyette
90 1.2.2.2 pgoyette static isc_result_t trace_type_record (trace_type_t *,
91 1.2.2.2 pgoyette unsigned, const char *, int);
92 1.2.2.2 pgoyette
93 1.2.2.2 pgoyette int trace_playback ()
94 1.2.2.2 pgoyette {
95 1.2.2.2 pgoyette return trace_playback_flag;
96 1.2.2.2 pgoyette }
97 1.2.2.2 pgoyette
98 1.2.2.2 pgoyette int trace_record ()
99 1.2.2.2 pgoyette {
100 1.2.2.2 pgoyette if (traceoutfile && !tracing_stopped)
101 1.2.2.2 pgoyette return 1;
102 1.2.2.2 pgoyette return 0;
103 1.2.2.2 pgoyette }
104 1.2.2.2 pgoyette
105 1.2.2.2 pgoyette isc_result_t trace_init (void (*set_time) (TIME),
106 1.2.2.2 pgoyette const char *file, int line)
107 1.2.2.2 pgoyette {
108 1.2.2.2 pgoyette trace_type_t *root_type;
109 1.2.2.2 pgoyette static int root_setup = 0;
110 1.2.2.2 pgoyette
111 1.2.2.2 pgoyette if (root_setup)
112 1.2.2.2 pgoyette return ISC_R_SUCCESS;
113 1.2.2.2 pgoyette
114 1.2.2.2 pgoyette trace_set_time_hook = set_time;
115 1.2.2.2 pgoyette
116 1.2.2.2 pgoyette root_type = trace_type_register ("trace-index-mapping",
117 1.2.2.2 pgoyette (void *)0, trace_index_map_input,
118 1.2.2.2 pgoyette trace_index_stop_tracing, file, line);
119 1.2.2.2 pgoyette if (!root_type)
120 1.2.2.2 pgoyette return ISC_R_UNEXPECTED;
121 1.2.2.2 pgoyette if (new_trace_types == root_type)
122 1.2.2.2 pgoyette new_trace_types = new_trace_types -> next;
123 1.2.2.2 pgoyette root_type -> index = 0;
124 1.2.2.2 pgoyette trace_type_stash (root_type);
125 1.2.2.2 pgoyette
126 1.2.2.2 pgoyette root_setup = 1;
127 1.2.2.2 pgoyette return ISC_R_SUCCESS;
128 1.2.2.2 pgoyette }
129 1.2.2.2 pgoyette
130 1.2.2.2 pgoyette isc_result_t trace_begin (const char *filename,
131 1.2.2.2 pgoyette const char *file, int line)
132 1.2.2.2 pgoyette {
133 1.2.2.2 pgoyette tracefile_header_t tfh;
134 1.2.2.2 pgoyette int status;
135 1.2.2.2 pgoyette trace_type_t *tptr, *next;
136 1.2.2.2 pgoyette isc_result_t result;
137 1.2.2.2 pgoyette
138 1.2.2.2 pgoyette if (traceoutfile) {
139 1.2.2.2 pgoyette log_error ("%s(%d): trace_begin called twice",
140 1.2.2.2 pgoyette file, line);
141 1.2.2.2 pgoyette return DHCP_R_INVALIDARG;
142 1.2.2.2 pgoyette }
143 1.2.2.2 pgoyette
144 1.2.2.2 pgoyette traceoutfile = open (filename, O_CREAT | O_WRONLY | O_EXCL, 0600);
145 1.2.2.2 pgoyette if (traceoutfile < 0 && errno == EEXIST) {
146 1.2.2.2 pgoyette log_error ("WARNING: Overwriting trace file \"%s\"", filename);
147 1.2.2.2 pgoyette traceoutfile = open (filename, O_WRONLY | O_EXCL | O_TRUNC,
148 1.2.2.2 pgoyette 0600);
149 1.2.2.2 pgoyette }
150 1.2.2.2 pgoyette
151 1.2.2.2 pgoyette if (traceoutfile < 0) {
152 1.2.2.2 pgoyette log_error ("%s(%d): trace_begin: %s: %m",
153 1.2.2.2 pgoyette file, line, filename);
154 1.2.2.2 pgoyette return ISC_R_UNEXPECTED;
155 1.2.2.2 pgoyette }
156 1.2.2.2 pgoyette #if defined (HAVE_SETFD)
157 1.2.2.2 pgoyette if (fcntl (traceoutfile, F_SETFD, 1) < 0)
158 1.2.2.2 pgoyette log_error ("Can't set close-on-exec on %s: %m", filename);
159 1.2.2.2 pgoyette #endif
160 1.2.2.2 pgoyette
161 1.2.2.2 pgoyette tfh.magic = htonl (TRACEFILE_MAGIC);
162 1.2.2.2 pgoyette tfh.version = htonl (TRACEFILE_VERSION);
163 1.2.2.2 pgoyette tfh.hlen = htonl (sizeof (tracefile_header_t));
164 1.2.2.2 pgoyette tfh.phlen = htonl (sizeof (tracepacket_t));
165 1.2.2.2 pgoyette
166 1.2.2.2 pgoyette status = write (traceoutfile, &tfh, sizeof tfh);
167 1.2.2.2 pgoyette if (status < 0) {
168 1.2.2.2 pgoyette log_error ("%s(%d): trace_begin write failed: %m", file, line);
169 1.2.2.2 pgoyette return ISC_R_UNEXPECTED;
170 1.2.2.2 pgoyette } else if (status != sizeof tfh) {
171 1.2.2.2 pgoyette log_error ("%s(%d): trace_begin: short write (%d:%ld)",
172 1.2.2.2 pgoyette file, line, status, (long)(sizeof tfh));
173 1.2.2.2 pgoyette trace_stop ();
174 1.2.2.2 pgoyette return ISC_R_UNEXPECTED;
175 1.2.2.2 pgoyette }
176 1.2.2.2 pgoyette
177 1.2.2.2 pgoyette /* Stash all the types that have already been set up. */
178 1.2.2.2 pgoyette if (new_trace_types) {
179 1.2.2.2 pgoyette next = new_trace_types;
180 1.2.2.2 pgoyette new_trace_types = (trace_type_t *)0;
181 1.2.2.2 pgoyette for (tptr = next; tptr; tptr = next) {
182 1.2.2.2 pgoyette next = tptr -> next;
183 1.2.2.2 pgoyette if (tptr -> index != 0) {
184 1.2.2.2 pgoyette result = (trace_type_record
185 1.2.2.2 pgoyette (tptr,
186 1.2.2.2 pgoyette strlen (tptr -> name), file, line));
187 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS)
188 1.2.2.2 pgoyette return status;
189 1.2.2.2 pgoyette }
190 1.2.2.2 pgoyette }
191 1.2.2.2 pgoyette }
192 1.2.2.2 pgoyette
193 1.2.2.2 pgoyette return ISC_R_SUCCESS;
194 1.2.2.2 pgoyette }
195 1.2.2.2 pgoyette
196 1.2.2.2 pgoyette isc_result_t trace_write_packet (trace_type_t *ttype, unsigned length,
197 1.2.2.2 pgoyette const char *buf, const char *file, int line)
198 1.2.2.2 pgoyette {
199 1.2.2.2 pgoyette trace_iov_t iov;
200 1.2.2.2 pgoyette
201 1.2.2.2 pgoyette iov.buf = buf;
202 1.2.2.2 pgoyette iov.len = length;
203 1.2.2.2 pgoyette return trace_write_packet_iov (ttype, 1, &iov, file, line);
204 1.2.2.2 pgoyette }
205 1.2.2.2 pgoyette
206 1.2.2.2 pgoyette isc_result_t trace_write_packet_iov (trace_type_t *ttype,
207 1.2.2.2 pgoyette int count, trace_iov_t *iov,
208 1.2.2.2 pgoyette const char *file, int line)
209 1.2.2.2 pgoyette {
210 1.2.2.2 pgoyette tracepacket_t tmp;
211 1.2.2.2 pgoyette int status;
212 1.2.2.2 pgoyette int i;
213 1.2.2.2 pgoyette int length;
214 1.2.2.2 pgoyette
215 1.2.2.2 pgoyette /* Really shouldn't get called here, but it may be hard to turn off
216 1.2.2.2 pgoyette tracing midstream if the trace file write fails or something. */
217 1.2.2.2 pgoyette if (tracing_stopped)
218 1.2.2.2 pgoyette return 0;
219 1.2.2.2 pgoyette
220 1.2.2.2 pgoyette if (!ttype) {
221 1.2.2.2 pgoyette log_error ("%s(%d): trace_write_packet with null trace type",
222 1.2.2.2 pgoyette file ? file : "<unknown file>", line);
223 1.2.2.2 pgoyette return DHCP_R_INVALIDARG;
224 1.2.2.2 pgoyette }
225 1.2.2.2 pgoyette if (!traceoutfile) {
226 1.2.2.2 pgoyette log_error ("%s(%d): trace_write_packet with no tracefile.",
227 1.2.2.2 pgoyette file ? file : "<unknown file>", line);
228 1.2.2.2 pgoyette return DHCP_R_INVALIDARG;
229 1.2.2.2 pgoyette }
230 1.2.2.2 pgoyette
231 1.2.2.2 pgoyette /* Compute the total length of the iov. */
232 1.2.2.2 pgoyette length = 0;
233 1.2.2.2 pgoyette for (i = 0; i < count; i++)
234 1.2.2.2 pgoyette length += iov [i].len;
235 1.2.2.2 pgoyette
236 1.2.2.2 pgoyette /* We have to swap out the data, because it may be read back on a
237 1.2.2.2 pgoyette machine of different endianness. */
238 1.2.2.2 pgoyette memset(&tmp, 0, sizeof(tmp));
239 1.2.2.2 pgoyette tmp.type_index = htonl (ttype -> index);
240 1.2.2.2 pgoyette tmp.when = htonl (time ((time_t *)0)); /* XXX */
241 1.2.2.2 pgoyette tmp.length = htonl (length);
242 1.2.2.2 pgoyette
243 1.2.2.2 pgoyette status = write (traceoutfile, &tmp, sizeof tmp);
244 1.2.2.2 pgoyette if (status < 0) {
245 1.2.2.2 pgoyette log_error ("%s(%d): trace_write_packet write failed: %m",
246 1.2.2.2 pgoyette file, line);
247 1.2.2.2 pgoyette return ISC_R_UNEXPECTED;
248 1.2.2.2 pgoyette } else if (status != sizeof tmp) {
249 1.2.2.2 pgoyette log_error ("%s(%d): trace_write_packet: short write (%d:%ld)",
250 1.2.2.2 pgoyette file, line, status, (long)(sizeof tmp));
251 1.2.2.2 pgoyette trace_stop ();
252 1.2.2.2 pgoyette }
253 1.2.2.2 pgoyette
254 1.2.2.2 pgoyette for (i = 0; i < count; i++) {
255 1.2.2.2 pgoyette status = write (traceoutfile, iov [i].buf, iov [i].len);
256 1.2.2.2 pgoyette if (status < 0) {
257 1.2.2.2 pgoyette log_error ("%s(%d): %s write failed: %m",
258 1.2.2.2 pgoyette file, line, "trace_write_packet");
259 1.2.2.2 pgoyette return ISC_R_UNEXPECTED;
260 1.2.2.2 pgoyette } else if (status != iov [i].len) {
261 1.2.2.2 pgoyette log_error ("%s(%d): %s: short write (%d:%d)",
262 1.2.2.2 pgoyette file, line,
263 1.2.2.2 pgoyette "trace_write_packet", status, length);
264 1.2.2.2 pgoyette trace_stop ();
265 1.2.2.2 pgoyette }
266 1.2.2.2 pgoyette }
267 1.2.2.2 pgoyette
268 1.2.2.2 pgoyette /* Write padding on the end of the packet to align the next
269 1.2.2.2 pgoyette packet to an 8-byte boundary. This is in case we decide to
270 1.2.2.2 pgoyette use mmap in some clever way later on. */
271 1.2.2.2 pgoyette if (length % 8) {
272 1.2.2.2 pgoyette static char zero [] = { 0, 0, 0, 0, 0, 0, 0 };
273 1.2.2.2 pgoyette unsigned padl = 8 - (length % 8);
274 1.2.2.2 pgoyette
275 1.2.2.2 pgoyette status = write (traceoutfile, zero, padl);
276 1.2.2.2 pgoyette if (status < 0) {
277 1.2.2.2 pgoyette log_error ("%s(%d): trace_write_packet write failed: %m",
278 1.2.2.2 pgoyette file, line);
279 1.2.2.2 pgoyette return ISC_R_UNEXPECTED;
280 1.2.2.2 pgoyette } else if (status != padl) {
281 1.2.2.2 pgoyette log_error ("%s(%d): trace_write_packet: short write (%d:%d)",
282 1.2.2.2 pgoyette file, line, status, padl);
283 1.2.2.2 pgoyette trace_stop ();
284 1.2.2.2 pgoyette }
285 1.2.2.2 pgoyette }
286 1.2.2.2 pgoyette
287 1.2.2.2 pgoyette return ISC_R_SUCCESS;
288 1.2.2.2 pgoyette }
289 1.2.2.2 pgoyette
290 1.2.2.2 pgoyette void trace_type_stash (trace_type_t *tptr)
291 1.2.2.2 pgoyette {
292 1.2.2.2 pgoyette trace_type_t **vec;
293 1.2.2.2 pgoyette int delta;
294 1.2.2.2 pgoyette if (trace_type_max <= tptr -> index) {
295 1.2.2.2 pgoyette delta = tptr -> index - trace_type_max + 10;
296 1.2.2.2 pgoyette vec = dmalloc (((trace_type_max + delta) *
297 1.2.2.2 pgoyette sizeof (trace_type_t *)), MDL);
298 1.2.2.2 pgoyette if (!vec)
299 1.2.2.2 pgoyette return;
300 1.2.2.2 pgoyette memset (&vec [trace_type_max], 0,
301 1.2.2.2 pgoyette (sizeof (trace_type_t *)) * delta);
302 1.2.2.2 pgoyette trace_type_max += delta;
303 1.2.2.2 pgoyette if (trace_types) {
304 1.2.2.2 pgoyette memcpy (vec, trace_types,
305 1.2.2.2 pgoyette trace_type_count * sizeof (trace_type_t *));
306 1.2.2.2 pgoyette dfree (trace_types, MDL);
307 1.2.2.2 pgoyette }
308 1.2.2.2 pgoyette trace_types = vec;
309 1.2.2.2 pgoyette }
310 1.2.2.2 pgoyette trace_types [tptr -> index] = tptr;
311 1.2.2.2 pgoyette if (tptr -> index >= trace_type_count)
312 1.2.2.2 pgoyette trace_type_count = tptr -> index + 1;
313 1.2.2.2 pgoyette }
314 1.2.2.2 pgoyette
315 1.2.2.2 pgoyette trace_type_t *trace_type_register (const char *name,
316 1.2.2.2 pgoyette void *baggage,
317 1.2.2.2 pgoyette void (*have_packet) (trace_type_t *,
318 1.2.2.2 pgoyette unsigned, char *),
319 1.2.2.2 pgoyette void (*stop_tracing) (trace_type_t *),
320 1.2.2.2 pgoyette const char *file, int line)
321 1.2.2.2 pgoyette {
322 1.2.2.2 pgoyette trace_type_t *ttmp;
323 1.2.2.2 pgoyette unsigned slen = strlen (name);
324 1.2.2.2 pgoyette isc_result_t status;
325 1.2.2.2 pgoyette
326 1.2.2.2 pgoyette ttmp = dmalloc (sizeof *ttmp, file, line);
327 1.2.2.2 pgoyette if (!ttmp)
328 1.2.2.2 pgoyette return ttmp;
329 1.2.2.2 pgoyette ttmp -> index = -1;
330 1.2.2.2 pgoyette ttmp -> name = dmalloc (slen + 1, file, line);
331 1.2.2.2 pgoyette if (!ttmp -> name) {
332 1.2.2.2 pgoyette dfree (ttmp, file, line);
333 1.2.2.2 pgoyette return (trace_type_t *)0;
334 1.2.2.2 pgoyette }
335 1.2.2.2 pgoyette strcpy (ttmp -> name, name);
336 1.2.2.2 pgoyette ttmp -> have_packet = have_packet;
337 1.2.2.2 pgoyette ttmp -> stop_tracing = stop_tracing;
338 1.2.2.2 pgoyette
339 1.2.2.2 pgoyette if (traceoutfile) {
340 1.2.2.2 pgoyette status = trace_type_record (ttmp, slen, file, line);
341 1.2.2.2 pgoyette if (status != ISC_R_SUCCESS) {
342 1.2.2.2 pgoyette dfree (ttmp -> name, file, line);
343 1.2.2.2 pgoyette dfree (ttmp, file, line);
344 1.2.2.2 pgoyette return (trace_type_t *)0;
345 1.2.2.2 pgoyette }
346 1.2.2.2 pgoyette } else {
347 1.2.2.2 pgoyette ttmp -> next = new_trace_types;
348 1.2.2.2 pgoyette new_trace_types = ttmp;
349 1.2.2.2 pgoyette }
350 1.2.2.2 pgoyette
351 1.2.2.2 pgoyette return ttmp;
352 1.2.2.2 pgoyette }
353 1.2.2.2 pgoyette
354 1.2.2.2 pgoyette static isc_result_t trace_type_record (trace_type_t *ttmp, unsigned slen,
355 1.2.2.2 pgoyette const char *file, int line)
356 1.2.2.2 pgoyette {
357 1.2.2.2 pgoyette trace_index_mapping_t *tim;
358 1.2.2.2 pgoyette isc_result_t status;
359 1.2.2.2 pgoyette
360 1.2.2.2 pgoyette tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line);
361 1.2.2.2 pgoyette if (!tim)
362 1.2.2.2 pgoyette return ISC_R_NOMEMORY;
363 1.2.2.2 pgoyette ttmp -> index = ++traceindex;
364 1.2.2.2 pgoyette trace_type_stash (ttmp);
365 1.2.2.2 pgoyette tim -> index = htonl (ttmp -> index);
366 1.2.2.2 pgoyette memcpy (tim -> name, ttmp -> name, slen);
367 1.2.2.2 pgoyette status = trace_write_packet (trace_types [0],
368 1.2.2.2 pgoyette slen + TRACE_INDEX_MAPPING_SIZE,
369 1.2.2.2 pgoyette (char *)tim, file, line);
370 1.2.2.2 pgoyette dfree (tim, file, line);
371 1.2.2.2 pgoyette return status;
372 1.2.2.2 pgoyette }
373 1.2.2.2 pgoyette
374 1.2.2.2 pgoyette /* Stop all registered trace types from trying to trace. */
375 1.2.2.2 pgoyette
376 1.2.2.2 pgoyette void trace_stop (void)
377 1.2.2.2 pgoyette {
378 1.2.2.2 pgoyette int i;
379 1.2.2.2 pgoyette
380 1.2.2.2 pgoyette for (i = 0; i < trace_type_count; i++)
381 1.2.2.2 pgoyette if (trace_types [i] -> stop_tracing)
382 1.2.2.2 pgoyette (*(trace_types [i] -> stop_tracing))
383 1.2.2.2 pgoyette (trace_types [i]);
384 1.2.2.2 pgoyette tracing_stopped = 1;
385 1.2.2.2 pgoyette }
386 1.2.2.2 pgoyette
387 1.2.2.2 pgoyette void trace_index_map_input (trace_type_t *ttype, unsigned length, char *buf)
388 1.2.2.2 pgoyette {
389 1.2.2.2 pgoyette trace_index_mapping_t *tmap;
390 1.2.2.2 pgoyette unsigned len;
391 1.2.2.2 pgoyette trace_type_t *tptr, **prev;
392 1.2.2.2 pgoyette
393 1.2.2.2 pgoyette if (length < TRACE_INDEX_MAPPING_SIZE) {
394 1.2.2.2 pgoyette log_error ("short trace index mapping");
395 1.2.2.2 pgoyette return;
396 1.2.2.2 pgoyette }
397 1.2.2.2 pgoyette tmap = (trace_index_mapping_t *)buf;
398 1.2.2.2 pgoyette
399 1.2.2.2 pgoyette prev = &new_trace_types;
400 1.2.2.2 pgoyette for (tptr = new_trace_types; tptr; tptr = tptr -> next) {
401 1.2.2.2 pgoyette len = strlen (tptr -> name);
402 1.2.2.2 pgoyette if (len == length - TRACE_INDEX_MAPPING_SIZE &&
403 1.2.2.2 pgoyette !memcmp (tptr -> name, tmap -> name, len)) {
404 1.2.2.2 pgoyette tptr -> index = ntohl (tmap -> index);
405 1.2.2.2 pgoyette trace_type_stash (tptr);
406 1.2.2.2 pgoyette *prev = tptr -> next;
407 1.2.2.2 pgoyette return;
408 1.2.2.2 pgoyette }
409 1.2.2.2 pgoyette prev = &tptr -> next;
410 1.2.2.2 pgoyette }
411 1.2.2.2 pgoyette
412 1.2.2.2 pgoyette log_error ("No registered trace type for type name %.*s",
413 1.2.2.2 pgoyette (int)length - TRACE_INDEX_MAPPING_SIZE, tmap -> name);
414 1.2.2.2 pgoyette return;
415 1.2.2.2 pgoyette }
416 1.2.2.2 pgoyette
417 1.2.2.2 pgoyette void trace_index_stop_tracing (trace_type_t *ttype) { }
418 1.2.2.2 pgoyette
419 1.2.2.2 pgoyette void trace_replay_init (void)
420 1.2.2.2 pgoyette {
421 1.2.2.2 pgoyette trace_playback_flag = 1;
422 1.2.2.2 pgoyette }
423 1.2.2.2 pgoyette
424 1.2.2.2 pgoyette void trace_file_replay (const char *filename)
425 1.2.2.2 pgoyette {
426 1.2.2.2 pgoyette tracepacket_t *tpkt = NULL;
427 1.2.2.2 pgoyette int status;
428 1.2.2.2 pgoyette char *buf = NULL;
429 1.2.2.2 pgoyette unsigned buflen;
430 1.2.2.2 pgoyette unsigned bufmax = 0;
431 1.2.2.2 pgoyette trace_type_t *ttype = NULL;
432 1.2.2.2 pgoyette isc_result_t result;
433 1.2.2.2 pgoyette int len;
434 1.2.2.2 pgoyette
435 1.2.2.2 pgoyette traceinfile = fopen (filename, "r");
436 1.2.2.2 pgoyette if (!traceinfile) {
437 1.2.2.2 pgoyette log_error("Can't open tracefile %s: %m", filename);
438 1.2.2.2 pgoyette return;
439 1.2.2.2 pgoyette }
440 1.2.2.2 pgoyette #if defined (HAVE_SETFD)
441 1.2.2.2 pgoyette if (fcntl (fileno(traceinfile), F_SETFD, 1) < 0)
442 1.2.2.2 pgoyette log_error("Can't set close-on-exec on %s: %m", filename);
443 1.2.2.2 pgoyette #endif
444 1.2.2.2 pgoyette status = fread(&tracefile_header, 1,
445 1.2.2.2 pgoyette sizeof tracefile_header, traceinfile);
446 1.2.2.2 pgoyette if (status < sizeof tracefile_header) {
447 1.2.2.2 pgoyette if (ferror(traceinfile))
448 1.2.2.2 pgoyette log_error("Error reading trace file header: %m");
449 1.2.2.2 pgoyette else
450 1.2.2.2 pgoyette log_error("Short read on trace file header: %d %ld.",
451 1.2.2.2 pgoyette status, (long)(sizeof tracefile_header));
452 1.2.2.2 pgoyette goto out;
453 1.2.2.2 pgoyette }
454 1.2.2.2 pgoyette tracefile_header.magic = ntohl(tracefile_header.magic);
455 1.2.2.2 pgoyette tracefile_header.version = ntohl(tracefile_header.version);
456 1.2.2.2 pgoyette tracefile_header.hlen = ntohl(tracefile_header.hlen);
457 1.2.2.2 pgoyette tracefile_header.phlen = ntohl(tracefile_header.phlen);
458 1.2.2.2 pgoyette
459 1.2.2.2 pgoyette if (tracefile_header.magic != TRACEFILE_MAGIC) {
460 1.2.2.2 pgoyette log_error("%s: not a dhcp trace file.", filename);
461 1.2.2.2 pgoyette goto out;
462 1.2.2.2 pgoyette }
463 1.2.2.2 pgoyette if (tracefile_header.version > TRACEFILE_VERSION) {
464 1.2.2.2 pgoyette log_error ("tracefile version %ld > current %ld.",
465 1.2.2.2 pgoyette (long int)tracefile_header.version,
466 1.2.2.2 pgoyette (long int)TRACEFILE_VERSION);
467 1.2.2.2 pgoyette goto out;
468 1.2.2.2 pgoyette }
469 1.2.2.2 pgoyette if (tracefile_header.phlen < sizeof *tpkt) {
470 1.2.2.2 pgoyette log_error("tracefile packet size too small - %ld < %ld",
471 1.2.2.2 pgoyette (long int)tracefile_header.phlen,
472 1.2.2.2 pgoyette (long int)sizeof *tpkt);
473 1.2.2.2 pgoyette goto out;
474 1.2.2.2 pgoyette }
475 1.2.2.2 pgoyette len = (sizeof tracefile_header) - tracefile_header.hlen;
476 1.2.2.2 pgoyette if (len < 0) {
477 1.2.2.2 pgoyette log_error("tracefile header size too small - %ld < %ld",
478 1.2.2.2 pgoyette (long int)tracefile_header.hlen,
479 1.2.2.2 pgoyette (long int)sizeof tracefile_header);
480 1.2.2.2 pgoyette goto out;
481 1.2.2.2 pgoyette }
482 1.2.2.2 pgoyette if (len > 0) {
483 1.2.2.2 pgoyette status = fseek(traceinfile, (long)len, SEEK_CUR);
484 1.2.2.2 pgoyette if (status < 0) {
485 1.2.2.2 pgoyette log_error("can't seek past header: %m");
486 1.2.2.2 pgoyette goto out;
487 1.2.2.2 pgoyette }
488 1.2.2.2 pgoyette }
489 1.2.2.2 pgoyette
490 1.2.2.2 pgoyette tpkt = dmalloc((unsigned)tracefile_header.phlen, MDL);
491 1.2.2.2 pgoyette if (tpkt == NULL) {
492 1.2.2.2 pgoyette log_error ("can't allocate trace packet header.");
493 1.2.2.2 pgoyette goto out;
494 1.2.2.2 pgoyette }
495 1.2.2.2 pgoyette
496 1.2.2.2 pgoyette while ((result = trace_get_next_packet(&ttype, tpkt, &buf, &buflen,
497 1.2.2.2 pgoyette &bufmax)) == ISC_R_SUCCESS) {
498 1.2.2.2 pgoyette (*ttype->have_packet)(ttype, tpkt->length, buf);
499 1.2.2.2 pgoyette ttype = NULL;
500 1.2.2.2 pgoyette }
501 1.2.2.2 pgoyette out:
502 1.2.2.2 pgoyette fclose(traceinfile);
503 1.2.2.2 pgoyette if (buf != NULL)
504 1.2.2.2 pgoyette dfree(buf, MDL);
505 1.2.2.2 pgoyette if (tpkt != NULL)
506 1.2.2.2 pgoyette dfree(tpkt, MDL);
507 1.2.2.2 pgoyette }
508 1.2.2.2 pgoyette
509 1.2.2.2 pgoyette /* Get the next packet from the file. If ttp points to a nonzero pointer
510 1.2.2.2 pgoyette to a trace type structure, check the next packet to see if it's of the
511 1.2.2.2 pgoyette expected type, and back off if not. */
512 1.2.2.2 pgoyette
513 1.2.2.2 pgoyette isc_result_t trace_get_next_packet (trace_type_t **ttp,
514 1.2.2.2 pgoyette tracepacket_t *tpkt,
515 1.2.2.2 pgoyette char **buf, unsigned *buflen,
516 1.2.2.2 pgoyette unsigned *bufmax)
517 1.2.2.2 pgoyette {
518 1.2.2.2 pgoyette trace_type_t *ttype;
519 1.2.2.2 pgoyette unsigned paylen;
520 1.2.2.2 pgoyette int status, curposok = 0;
521 1.2.2.2 pgoyette fpos_t curpos;
522 1.2.2.2 pgoyette
523 1.2.2.2 pgoyette while(1) {
524 1.2.2.2 pgoyette curposok = 0;
525 1.2.2.2 pgoyette status = fgetpos(traceinfile, &curpos);
526 1.2.2.2 pgoyette if (status < 0) {
527 1.2.2.2 pgoyette log_error("Can't save tracefile position: %m");
528 1.2.2.2 pgoyette } else {
529 1.2.2.2 pgoyette curposok = 1;
530 1.2.2.2 pgoyette }
531 1.2.2.2 pgoyette
532 1.2.2.2 pgoyette status = fread(tpkt, 1, (size_t)tracefile_header.phlen,
533 1.2.2.2 pgoyette traceinfile);
534 1.2.2.2 pgoyette if (status < tracefile_header.phlen) {
535 1.2.2.2 pgoyette if (ferror(traceinfile))
536 1.2.2.2 pgoyette log_error("Error reading trace packet header: "
537 1.2.2.2 pgoyette "%m");
538 1.2.2.2 pgoyette else if (status == 0)
539 1.2.2.2 pgoyette return ISC_R_EOF;
540 1.2.2.2 pgoyette else
541 1.2.2.2 pgoyette log_error ("Short read on trace packet header:"
542 1.2.2.2 pgoyette " %ld %ld.",
543 1.2.2.2 pgoyette (long int)status,
544 1.2.2.2 pgoyette (long int)tracefile_header.phlen);
545 1.2.2.2 pgoyette return DHCP_R_PROTOCOLERROR;
546 1.2.2.2 pgoyette }
547 1.2.2.2 pgoyette
548 1.2.2.2 pgoyette /* Swap the packet. */
549 1.2.2.2 pgoyette tpkt->type_index = ntohl(tpkt -> type_index);
550 1.2.2.2 pgoyette tpkt->length = ntohl(tpkt -> length);
551 1.2.2.2 pgoyette tpkt->when = ntohl(tpkt -> when);
552 1.2.2.2 pgoyette
553 1.2.2.2 pgoyette /* See if there's a handler for this packet type. */
554 1.2.2.2 pgoyette if (tpkt->type_index < trace_type_count &&
555 1.2.2.2 pgoyette trace_types[tpkt->type_index])
556 1.2.2.2 pgoyette ttype = trace_types[tpkt->type_index];
557 1.2.2.2 pgoyette else {
558 1.2.2.2 pgoyette log_error ("Trace packet with unknown index %ld",
559 1.2.2.2 pgoyette (long int)tpkt->type_index);
560 1.2.2.2 pgoyette return DHCP_R_PROTOCOLERROR;
561 1.2.2.2 pgoyette }
562 1.2.2.2 pgoyette
563 1.2.2.2 pgoyette /*
564 1.2.2.2 pgoyette * Determine if we should try to expire any timer events.
565 1.2.2.2 pgoyette * We do so if:
566 1.2.2.2 pgoyette * we aren't looking for a specific type of packet
567 1.2.2.2 pgoyette * we have a hook to use to update the timer
568 1.2.2.2 pgoyette * the timestamp on the packet doesn't match the current time
569 1.2.2.2 pgoyette * When we do so we rewind the file to the beginning of this
570 1.2.2.2 pgoyette * packet and then try for a new packet. This allows
571 1.2.2.2 pgoyette * any code triggered by a timeout to get the current packet
572 1.2.2.2 pgoyette * while we get the next one.
573 1.2.2.2 pgoyette */
574 1.2.2.2 pgoyette
575 1.2.2.2 pgoyette if ((ttp != NULL) && (*ttp == NULL) &&
576 1.2.2.2 pgoyette (tpkt->when != cur_tv.tv_sec) &&
577 1.2.2.2 pgoyette (trace_set_time_hook != NULL)) {
578 1.2.2.2 pgoyette if (curposok == 0) {
579 1.2.2.2 pgoyette log_error("no curpos for fsetpos in "
580 1.2.2.2 pgoyette "tracefile");
581 1.2.2.2 pgoyette return DHCP_R_PROTOCOLERROR;
582 1.2.2.2 pgoyette }
583 1.2.2.2 pgoyette
584 1.2.2.2 pgoyette status = fsetpos(traceinfile, &curpos);
585 1.2.2.2 pgoyette if (status < 0) {
586 1.2.2.2 pgoyette log_error("fsetpos in tracefile failed: %m");
587 1.2.2.2 pgoyette return DHCP_R_PROTOCOLERROR;
588 1.2.2.2 pgoyette }
589 1.2.2.2 pgoyette
590 1.2.2.2 pgoyette (*trace_set_time_hook) (tpkt->when);
591 1.2.2.2 pgoyette continue;
592 1.2.2.2 pgoyette }
593 1.2.2.2 pgoyette break;
594 1.2.2.2 pgoyette }
595 1.2.2.2 pgoyette
596 1.2.2.2 pgoyette /* If we were supposed to get a particular kind of packet,
597 1.2.2.2 pgoyette check to see that we got the right kind. */
598 1.2.2.2 pgoyette if (ttp && *ttp && ttype != *ttp) {
599 1.2.2.2 pgoyette log_error ("Read packet type %s when expecting %s",
600 1.2.2.2 pgoyette ttype -> name, (*ttp) -> name);
601 1.2.2.2 pgoyette status = fsetpos (traceinfile, &curpos);
602 1.2.2.2 pgoyette if (status < 0) {
603 1.2.2.2 pgoyette log_error ("fsetpos in tracefile failed: %m");
604 1.2.2.2 pgoyette return DHCP_R_PROTOCOLERROR;
605 1.2.2.2 pgoyette }
606 1.2.2.2 pgoyette return ISC_R_UNEXPECTEDTOKEN;
607 1.2.2.2 pgoyette }
608 1.2.2.2 pgoyette
609 1.2.2.2 pgoyette paylen = tpkt -> length;
610 1.2.2.2 pgoyette if (paylen % 8)
611 1.2.2.2 pgoyette paylen += 8 - (tpkt -> length % 8);
612 1.2.2.2 pgoyette
613 1.2.2.2 pgoyette /* allocate a buffer if we need one or current buffer is too small */
614 1.2.2.2 pgoyette if ((*buf == NULL) || (paylen > (*bufmax))) {
615 1.2.2.2 pgoyette if ((*buf))
616 1.2.2.2 pgoyette dfree ((*buf), MDL);
617 1.2.2.2 pgoyette (*bufmax) = ((paylen + 1023) & ~1023U);
618 1.2.2.2 pgoyette (*buf) = dmalloc ((*bufmax), MDL);
619 1.2.2.2 pgoyette if (!(*buf)) {
620 1.2.2.2 pgoyette log_error ("Can't allocate input buffer sized %d",
621 1.2.2.2 pgoyette (*bufmax));
622 1.2.2.2 pgoyette return ISC_R_NOMEMORY;
623 1.2.2.2 pgoyette }
624 1.2.2.2 pgoyette }
625 1.2.2.2 pgoyette
626 1.2.2.2 pgoyette status = fread ((*buf), 1, paylen, traceinfile);
627 1.2.2.2 pgoyette if (status < paylen) {
628 1.2.2.2 pgoyette if (ferror (traceinfile))
629 1.2.2.2 pgoyette log_error ("Error reading trace payload: %m");
630 1.2.2.2 pgoyette else
631 1.2.2.2 pgoyette log_error ("Short read on trace payload: %d %d.",
632 1.2.2.2 pgoyette status, paylen);
633 1.2.2.2 pgoyette return DHCP_R_PROTOCOLERROR;
634 1.2.2.2 pgoyette }
635 1.2.2.2 pgoyette
636 1.2.2.2 pgoyette /* Store the actual length of the payload. */
637 1.2.2.2 pgoyette *buflen = tpkt -> length;
638 1.2.2.2 pgoyette
639 1.2.2.2 pgoyette if (ttp)
640 1.2.2.2 pgoyette *ttp = ttype;
641 1.2.2.2 pgoyette return ISC_R_SUCCESS;
642 1.2.2.2 pgoyette }
643 1.2.2.2 pgoyette
644 1.2.2.2 pgoyette isc_result_t trace_get_packet (trace_type_t **ttp,
645 1.2.2.2 pgoyette unsigned *buflen, char **buf)
646 1.2.2.2 pgoyette {
647 1.2.2.2 pgoyette tracepacket_t *tpkt;
648 1.2.2.2 pgoyette unsigned bufmax = 0;
649 1.2.2.2 pgoyette isc_result_t status;
650 1.2.2.2 pgoyette
651 1.2.2.2 pgoyette if (!buf || *buf)
652 1.2.2.2 pgoyette return DHCP_R_INVALIDARG;
653 1.2.2.2 pgoyette
654 1.2.2.2 pgoyette tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
655 1.2.2.2 pgoyette if (!tpkt) {
656 1.2.2.2 pgoyette log_error ("can't allocate trace packet header.");
657 1.2.2.2 pgoyette return ISC_R_NOMEMORY;
658 1.2.2.2 pgoyette }
659 1.2.2.2 pgoyette
660 1.2.2.2 pgoyette status = trace_get_next_packet (ttp, tpkt, buf, buflen, &bufmax);
661 1.2.2.2 pgoyette
662 1.2.2.2 pgoyette dfree (tpkt, MDL);
663 1.2.2.2 pgoyette return status;
664 1.2.2.2 pgoyette }
665 1.2.2.2 pgoyette
666 1.2.2.2 pgoyette /* Get a packet from the trace input file that contains a file with the
667 1.2.2.2 pgoyette specified name. We don't hunt for the packet - it should be the next
668 1.2.2.2 pgoyette packet in the tracefile. If it's not, or something else bad happens,
669 1.2.2.2 pgoyette return an error code. */
670 1.2.2.2 pgoyette
671 1.2.2.2 pgoyette isc_result_t trace_get_file (trace_type_t *ttype,
672 1.2.2.2 pgoyette const char *filename, unsigned *len, char **buf)
673 1.2.2.2 pgoyette {
674 1.2.2.2 pgoyette fpos_t curpos;
675 1.2.2.2 pgoyette unsigned max = 0;
676 1.2.2.2 pgoyette tracepacket_t *tpkt;
677 1.2.2.2 pgoyette int status;
678 1.2.2.2 pgoyette isc_result_t result;
679 1.2.2.2 pgoyette
680 1.2.2.2 pgoyette /* Disallow some obvious bogosities. */
681 1.2.2.2 pgoyette if (!buf || !len || *buf)
682 1.2.2.2 pgoyette return DHCP_R_INVALIDARG;
683 1.2.2.2 pgoyette
684 1.2.2.2 pgoyette /* Save file position in case of filename mismatch. */
685 1.2.2.2 pgoyette status = fgetpos (traceinfile, &curpos);
686 1.2.2.2 pgoyette if (status < 0)
687 1.2.2.2 pgoyette log_error ("Can't save tracefile position: %m");
688 1.2.2.2 pgoyette
689 1.2.2.2 pgoyette tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
690 1.2.2.2 pgoyette if (!tpkt) {
691 1.2.2.2 pgoyette log_error ("can't allocate trace packet header.");
692 1.2.2.2 pgoyette return ISC_R_NOMEMORY;
693 1.2.2.2 pgoyette }
694 1.2.2.2 pgoyette
695 1.2.2.2 pgoyette result = trace_get_next_packet (&ttype, tpkt, buf, len, &max);
696 1.2.2.2 pgoyette /* done with tpkt, free it */
697 1.2.2.2 pgoyette dfree (tpkt, MDL);
698 1.2.2.2 pgoyette if (result != ISC_R_SUCCESS) {
699 1.2.2.2 pgoyette if (*buf) {
700 1.2.2.2 pgoyette dfree (*buf, MDL);
701 1.2.2.2 pgoyette *buf = NULL;
702 1.2.2.2 pgoyette }
703 1.2.2.2 pgoyette return result;
704 1.2.2.2 pgoyette }
705 1.2.2.2 pgoyette
706 1.2.2.2 pgoyette /* Make sure the filename is right. */
707 1.2.2.2 pgoyette if (strcmp (filename, *buf)) {
708 1.2.2.2 pgoyette log_error ("Read file %s when expecting %s", *buf, filename);
709 1.2.2.2 pgoyette dfree (*buf, MDL);
710 1.2.2.2 pgoyette *buf = NULL;
711 1.2.2.2 pgoyette
712 1.2.2.2 pgoyette status = fsetpos (traceinfile, &curpos);
713 1.2.2.2 pgoyette if (status < 0) {
714 1.2.2.2 pgoyette log_error ("fsetpos in tracefile failed: %m");
715 1.2.2.2 pgoyette return DHCP_R_PROTOCOLERROR;
716 1.2.2.2 pgoyette }
717 1.2.2.2 pgoyette return ISC_R_UNEXPECTEDTOKEN;
718 1.2.2.2 pgoyette }
719 1.2.2.2 pgoyette
720 1.2.2.2 pgoyette return ISC_R_SUCCESS;
721 1.2.2.2 pgoyette }
722 1.2.2.2 pgoyette #endif /* TRACING */
723