3 Subroutines that support tracing of OMAPI wire transactions and
4 provide a mechanism for programs using OMAPI to trace their own
8 * Copyright (c) 2001 Internet Software Consortium.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of The Internet Software Consortium nor the names
21 * of its contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * This software has been written for the Internet Software Consortium
39 * by Ted Lemon, as part of a project for Nominum, Inc. To learn more
40 * about the Internet Software Consortium, see http://www.isc.org/. To
41 * learn more about Nominum, Inc., see ``http://www.nominum.com''.
44 #include <omapip/omapip_p.h>
47 void (*trace_set_time_hook) (u_int32_t);
48 static int tracing_stopped;
49 static int traceoutfile;
50 static int traceindex;
51 static trace_type_t **trace_types;
52 static int trace_type_count;
53 static int trace_type_max;
54 static trace_type_t *new_trace_types;
55 static FILE *traceinfile;
56 static tracefile_header_t tracefile_header;
57 static int trace_playback_flag;
58 trace_type_t trace_time_marker;
60 #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
61 extern omapi_array_t *trace_listeners;
62 extern omapi_array_t *omapi_connections;
64 void trace_free_all ()
70 new_trace_types = tp -> next;
72 dfree (tp -> name, MDL);
73 tp -> name = (char *)0;
78 for (i = 0; i < trace_type_count; i++) {
79 if (trace_types [i]) {
80 if (trace_types [i] -> name)
81 dfree (trace_types [i] -> name, MDL);
82 dfree (trace_types [i], MDL);
85 dfree (trace_types, MDL);
86 trace_types = (trace_type_t **)0;
87 trace_type_count = trace_type_max = 0;
89 omapi_array_free (&trace_listeners, MDL);
90 omapi_array_free (&omapi_connections, MDL);
94 static isc_result_t trace_type_record (trace_type_t *,
95 unsigned, const char *, int);
99 return trace_playback_flag;
104 if (traceoutfile && !tracing_stopped)
109 isc_result_t trace_init (void (*set_time) (u_int32_t),
110 const char *file, int line)
112 trace_type_t *root_type;
113 static int root_setup = 0;
116 return ISC_R_SUCCESS;
118 trace_set_time_hook = set_time;
120 root_type = trace_type_register ("trace-index-mapping",
121 (void *)0, trace_index_map_input,
122 trace_index_stop_tracing, file, line);
124 return ISC_R_UNEXPECTED;
125 if (new_trace_types == root_type)
126 new_trace_types = new_trace_types -> next;
127 root_type -> index = 0;
128 trace_type_stash (root_type);
131 return ISC_R_SUCCESS;
134 isc_result_t trace_begin (const char *filename,
135 const char *file, int line)
137 tracefile_header_t tfh;
139 trace_type_t *tptr, *next;
143 log_error ("%s(%d): trace_begin called twice",
145 return ISC_R_INVALIDARG;
148 traceoutfile = open (filename, O_CREAT | O_WRONLY | O_EXCL, 0644);
149 if (traceoutfile < 0) {
150 log_error ("%s(%d): trace_begin: %s: %m",
151 file, line, filename);
152 return ISC_R_UNEXPECTED;
154 #if defined (HAVE_SETFD)
155 if (fcntl (traceoutfile, F_SETFD, 1) < 0)
156 log_error ("Can't set close-on-exec on %s: %m", filename);
159 tfh.magic = htonl (TRACEFILE_MAGIC);
160 tfh.version = htonl (TRACEFILE_VERSION);
161 tfh.hlen = htonl (sizeof (tracefile_header_t));
162 tfh.phlen = htonl (sizeof (tracepacket_t));
164 status = write (traceoutfile, &tfh, sizeof tfh);
166 log_error ("%s(%d): trace_begin write failed: %m", file, line);
167 return ISC_R_UNEXPECTED;
168 } else if (status != sizeof tfh) {
169 log_error ("%s(%d): trace_begin: short write (%d:%ld)",
170 file, line, status, (long)(sizeof tfh));
172 return ISC_R_UNEXPECTED;
175 /* Stash all the types that have already been set up. */
176 if (new_trace_types) {
177 next = new_trace_types;
178 new_trace_types = (trace_type_t *)0;
179 for (tptr = next; tptr; tptr = next) {
181 if (tptr -> index != 0) {
182 result = (trace_type_record
184 strlen (tptr -> name), file, line));
185 if (result != ISC_R_SUCCESS)
191 return ISC_R_SUCCESS;
194 isc_result_t trace_write_packet (trace_type_t *ttype, unsigned length,
195 const char *buf, const char *file, int line)
201 return trace_write_packet_iov (ttype, 1, &iov, file, line);
204 isc_result_t trace_write_packet_iov (trace_type_t *ttype,
205 int count, trace_iov_t *iov,
206 const char *file, int line)
213 /* Really shouldn't get called here, but it may be hard to turn off
214 tracing midstream if the trace file write fails or something. */
219 log_error ("%s(%d): trace_write_packet with null trace type",
220 file ? file : "<unknown file>", line);
221 return ISC_R_INVALIDARG;
224 log_error ("%s(%d): trace_write_packet with no tracefile.",
225 file ? file : "<unknown file>", line);
226 return ISC_R_INVALIDARG;
229 /* Compute the total length of the iov. */
231 for (i = 0; i < count; i++)
232 length += iov [i].len;
234 /* We have to swap out the data, because it may be read back on a
235 machine of different endianness. */
236 tmp.type_index = htonl (ttype -> index);
237 tmp.when = htonl (time ((time_t *)0)); /* XXX */
238 tmp.length = htonl (length);
240 status = write (traceoutfile, &tmp, sizeof tmp);
242 log_error ("%s(%d): trace_write_packet write failed: %m",
244 return ISC_R_UNEXPECTED;
245 } else if (status != sizeof tmp) {
246 log_error ("%s(%d): trace_write_packet: short write (%d:%ld)",
247 file, line, status, (long)(sizeof tmp));
251 for (i = 0; i < count; i++) {
252 status = write (traceoutfile, iov [i].buf, iov [i].len);
254 log_error ("%s(%d): %s write failed: %m",
255 file, line, "trace_write_packet");
256 return ISC_R_UNEXPECTED;
257 } else if (status != iov [i].len) {
258 log_error ("%s(%d): %s: short write (%d:%d)",
260 "trace_write_packet", status, length);
265 /* Write padding on the end of the packet to align the next
266 packet to an 8-byte boundary. This is in case we decide to
267 use mmap in some clever way later on. */
269 static char zero [] = { 0, 0, 0, 0, 0, 0, 0 };
270 unsigned padl = 8 - (length % 8);
272 status = write (traceoutfile, zero, padl);
274 log_error ("%s(%d): trace_write_packet write failed: %m",
276 return ISC_R_UNEXPECTED;
277 } else if (status != padl) {
278 log_error ("%s(%d): trace_write_packet: short write (%d:%d)",
279 file, line, status, padl);
284 return ISC_R_SUCCESS;
287 void trace_type_stash (trace_type_t *tptr)
291 if (trace_type_max <= tptr -> index) {
292 delta = tptr -> index - trace_type_max + 10;
293 vec = dmalloc (((trace_type_max + delta) *
294 sizeof (trace_type_t *)), MDL);
297 memset (&vec [trace_type_max], 0,
298 (sizeof (trace_type_t *)) * delta);
299 trace_type_max += delta;
301 memcpy (vec, trace_types,
302 trace_type_count * sizeof (trace_type_t *));
303 dfree (trace_types, MDL);
307 trace_types [tptr -> index] = tptr;
308 if (tptr -> index >= trace_type_count)
309 trace_type_count = tptr -> index + 1;
312 trace_type_t *trace_type_register (const char *name,
314 void (*have_packet) (trace_type_t *,
316 void (*stop_tracing) (trace_type_t *),
317 const char *file, int line)
319 trace_type_t *ttmp, *tptr;
320 unsigned slen = strlen (name);
323 ttmp = dmalloc (sizeof *ttmp, file, line);
327 ttmp -> name = dmalloc (slen + 1, file, line);
329 dfree (ttmp, file, line);
330 return (trace_type_t *)0;
332 strcpy (ttmp -> name, name);
333 ttmp -> have_packet = have_packet;
334 ttmp -> stop_tracing = stop_tracing;
337 status = trace_type_record (ttmp, slen, file, line);
338 if (status != ISC_R_SUCCESS) {
339 dfree (ttmp -> name, file, line);
340 dfree (ttmp, file, line);
341 return (trace_type_t *)0;
344 ttmp -> next = new_trace_types;
345 new_trace_types = ttmp;
351 static isc_result_t trace_type_record (trace_type_t *ttmp, unsigned slen,
352 const char *file, int line)
354 trace_index_mapping_t *tim;
357 tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line);
359 return ISC_R_NOMEMORY;
360 ttmp -> index = ++traceindex;
361 trace_type_stash (ttmp);
362 tim -> index = htonl (ttmp -> index);
363 memcpy (tim -> name, ttmp -> name, slen);
364 status = trace_write_packet (trace_types [0],
365 slen + TRACE_INDEX_MAPPING_SIZE,
366 (char *)tim, file, line);
367 dfree (tim, file, line);
371 /* Stop all registered trace types from trying to trace. */
373 void trace_stop (void)
377 for (i = 0; i < trace_type_count; i++)
378 if (trace_types [i] -> stop_tracing)
379 (*(trace_types [i] -> stop_tracing))
384 void trace_index_map_input (trace_type_t *ttype, unsigned length, char *buf)
386 trace_index_mapping_t *tmap;
388 trace_type_t *tptr, **prev;
390 if (length < TRACE_INDEX_MAPPING_SIZE) {
391 log_error ("short trace index mapping");
394 tmap = (trace_index_mapping_t *)buf;
396 prev = &new_trace_types;
397 for (tptr = new_trace_types; tptr; tptr = tptr -> next) {
398 len = strlen (tptr -> name);
399 if (len == length - TRACE_INDEX_MAPPING_SIZE &&
400 !memcmp (tptr -> name, tmap -> name, len)) {
401 tptr -> index = ntohl (tmap -> index);
402 trace_type_stash (tptr);
403 *prev = tptr -> next;
406 prev = &tptr -> next;
409 log_error ("No registered trace type for type name %.*s",
410 (int)length - TRACE_INDEX_MAPPING_SIZE, tmap -> name);
414 void trace_index_stop_tracing (trace_type_t *ttype) { }
416 void trace_replay_init (void)
418 trace_playback_flag = 1;
421 void trace_file_replay (const char *filename)
423 tracepacket_t *tpkt = (tracepacket_t *)0;
425 char *buf = (char *)0;
428 trace_type_t *ttype = (trace_type_t *)0;
432 traceinfile = fopen (filename, "r");
434 log_error ("Can't open tracefile %s: %m", filename);
437 #if defined (HAVE_SETFD)
438 if (fcntl (fileno (traceinfile), F_SETFD, 1) < 0)
439 log_error ("Can't set close-on-exec on %s: %m", filename);
441 status = fread (&tracefile_header, 1,
442 sizeof tracefile_header, traceinfile);
443 if (status < sizeof tracefile_header) {
444 if (ferror (traceinfile))
445 log_error ("Error reading trace file header: %m");
447 log_error ("Short read on trace file header: %d %ld.",
448 status, (long)(sizeof tracefile_header));
451 tracefile_header.magic = ntohl (tracefile_header.magic);
452 tracefile_header.version = ntohl (tracefile_header.version);
453 tracefile_header.hlen = ntohl (tracefile_header.hlen);
454 tracefile_header.phlen = ntohl (tracefile_header.phlen);
456 if (tracefile_header.magic != TRACEFILE_MAGIC) {
457 log_error ("%s: not a dhcp trace file.", filename);
460 if (tracefile_header.version > TRACEFILE_VERSION) {
461 log_error ("tracefile version %ld > current %ld.",
462 (long int)tracefile_header.version,
463 (long int)TRACEFILE_VERSION);
466 if (tracefile_header.phlen < sizeof *tpkt) {
467 log_error ("tracefile packet size too small - %ld < %ld",
468 (long int)tracefile_header.phlen,
469 (long int)sizeof *tpkt);
472 len = (sizeof tracefile_header) - tracefile_header.hlen;
474 log_error ("tracefile header size too small - %ld < %ld",
475 (long int)tracefile_header.hlen,
476 (long int)sizeof tracefile_header);
480 status = fseek (traceinfile, (long)len, SEEK_CUR);
482 log_error ("can't seek past header: %m");
487 tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
489 log_error ("can't allocate trace packet header.");
493 while ((result = trace_get_next_packet (&ttype, tpkt, &buf, &buflen,
494 &bufmax)) == ISC_R_SUCCESS) {
495 (*ttype -> have_packet) (ttype, tpkt -> length, buf);
496 ttype = (trace_type_t *)0;
499 fclose (traceinfile);
506 /* Get the next packet from the file. If ttp points to a nonzero pointer
507 to a trace type structure, check the next packet to see if it's of the
508 expected type, and back off if not. */
510 isc_result_t trace_get_next_packet (trace_type_t **ttp,
512 char **buf, unsigned *buflen,
521 status = fgetpos (traceinfile, &curpos);
523 log_error ("Can't save tracefile position: %m");
525 status = fread (tpkt, 1, (size_t)tracefile_header.phlen, traceinfile);
526 if (status < tracefile_header.phlen) {
527 if (ferror (traceinfile))
528 log_error ("Error reading trace packet header: %m");
529 else if (status == 0)
532 log_error ("Short read on trace packet header: "
535 (long int)tracefile_header.phlen);
536 return ISC_R_PROTOCOLERROR;
539 /* Swap the packet. */
540 tpkt -> type_index = ntohl (tpkt -> type_index);
541 tpkt -> length = ntohl (tpkt -> length);
542 tpkt -> when = ntohl (tpkt -> when);
544 /* See if there's a handler for this packet type. */
545 if (tpkt -> type_index < trace_type_count &&
546 trace_types [tpkt -> type_index])
547 ttype = trace_types [tpkt -> type_index];
549 log_error ("Trace packet with unknown index %ld",
550 (long int)tpkt -> type_index);
551 return ISC_R_PROTOCOLERROR;
554 /* If we were just hunting for the time marker, we've found it,
555 so back up to the beginning of the packet and return its
557 if (ttp && *ttp == &trace_time_marker) {
559 status = fsetpos (traceinfile, &curpos);
561 log_error ("fsetpos in tracefile failed: %m");
562 return ISC_R_PROTOCOLERROR;
567 /* If we were supposed to get a particular kind of packet,
568 check to see that we got the right kind. */
569 if (ttp && *ttp && ttype != *ttp) {
570 log_error ("Read packet type %s when expecting %s",
571 ttype -> name, (*ttp) -> name);
572 status = fsetpos (traceinfile, &curpos);
574 log_error ("fsetpos in tracefile failed: %m");
575 return ISC_R_PROTOCOLERROR;
577 return ISC_R_UNEXPECTEDTOKEN;
580 paylen = tpkt -> length;
582 paylen += 8 - (tpkt -> length % 8);
583 if (paylen > (*bufmax)) {
586 (*bufmax) = ((paylen + 1023) & ~1023U);
587 (*buf) = dmalloc ((*bufmax), MDL);
589 log_error ("Can't allocate input buffer sized %d",
591 return ISC_R_NOMEMORY;
595 status = fread ((*buf), 1, paylen, traceinfile);
596 if (status < paylen) {
597 if (ferror (traceinfile))
598 log_error ("Error reading trace payload: %m");
600 log_error ("Short read on trace payload: %d %d.",
602 return ISC_R_PROTOCOLERROR;
605 /* Store the actual length of the payload. */
606 *buflen = tpkt -> length;
608 if (trace_set_time_hook)
609 (*trace_set_time_hook) (tpkt -> when);
613 return ISC_R_SUCCESS;
616 isc_result_t trace_get_packet (trace_type_t **ttp,
617 unsigned *buflen, char **buf)
624 return ISC_R_INVALIDARG;
626 tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
628 log_error ("can't allocate trace packet header.");
629 return ISC_R_NOMEMORY;
632 status = trace_get_next_packet (ttp, tpkt, buf, buflen, &bufmax);
638 time_t trace_snoop_time (trace_type_t **ptp)
643 char *buf = (char *)0;
651 tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
653 log_error ("can't allocate trace packet header.");
654 return ISC_R_NOMEMORY;
657 *ptp = &trace_time_marker;
658 trace_get_next_packet (ptp, tpkt, &buf, &buflen, &bufmax);
659 result = tpkt -> when;
665 /* Get a packet from the trace input file that contains a file with the
666 specified name. We don't hunt for the packet - it should be the next
667 packet in the tracefile. If it's not, or something else bad happens,
668 return an error code. */
670 isc_result_t trace_get_file (trace_type_t *ttype,
671 const char *filename, unsigned *len, char **buf)
679 /* Disallow some obvious bogosities. */
680 if (!buf || !len || *buf)
681 return ISC_R_INVALIDARG;
683 /* Save file position in case of filename mismatch. */
684 status = fgetpos (traceinfile, &curpos);
686 log_error ("Can't save tracefile position: %m");
688 tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
690 log_error ("can't allocate trace packet header.");
691 return ISC_R_NOMEMORY;
694 result = trace_get_next_packet (&ttype, tpkt, buf, len, &max);
695 if (result != ISC_R_SUCCESS) {
702 /* Make sure the filename is right. */
703 if (strcmp (filename, *buf)) {
704 log_error ("Read file %s when expecting %s", *buf, filename);
705 status = fsetpos (traceinfile, &curpos);
707 log_error ("fsetpos in tracefile failed: %m");
710 return ISC_R_PROTOCOLERROR;
712 return ISC_R_UNEXPECTEDTOKEN;
716 return ISC_R_SUCCESS;