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) 2004 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 2001-2003 by Internet Software Consortium
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Internet Systems Consortium, Inc.
25 * Redwood City, CA 94063
29 * This software has been written for Internet Systems Consortium
30 * by Ted Lemon, as part of a project for Nominum, Inc. To learn more
31 * about Internet Systems Consortium, see http://www.isc.org/. To
32 * learn more about Nominum, Inc., see ``http://www.nominum.com''.
35 #include <omapip/omapip_p.h>
38 void (*trace_set_time_hook
) (u_int32_t
);
39 static int tracing_stopped
;
40 static int traceoutfile
;
41 static int traceindex
;
42 static trace_type_t
**trace_types
;
43 static int trace_type_count
;
44 static int trace_type_max
;
45 static trace_type_t
*new_trace_types
;
46 static FILE *traceinfile
;
47 static tracefile_header_t tracefile_header
;
48 static int trace_playback_flag
;
49 trace_type_t trace_time_marker
;
51 #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
52 extern omapi_array_t
*trace_listeners
;
53 extern omapi_array_t
*omapi_connections
;
55 void trace_free_all ()
61 new_trace_types
= tp
-> next
;
63 dfree (tp
-> name
, MDL
);
64 tp
-> name
= (char *)0;
69 for (i
= 0; i
< trace_type_count
; i
++) {
70 if (trace_types
[i
]) {
71 if (trace_types
[i
] -> name
)
72 dfree (trace_types
[i
] -> name
, MDL
);
73 dfree (trace_types
[i
], MDL
);
76 dfree (trace_types
, MDL
);
77 trace_types
= (trace_type_t
**)0;
78 trace_type_count
= trace_type_max
= 0;
80 omapi_array_free (&trace_listeners
, MDL
);
81 omapi_array_free (&omapi_connections
, MDL
);
85 static isc_result_t
trace_type_record (trace_type_t
*,
86 unsigned, const char *, int);
90 return trace_playback_flag
;
95 if (traceoutfile
&& !tracing_stopped
)
100 isc_result_t
trace_init (void (*set_time
) (u_int32_t
),
101 const char *file
, int line
)
103 trace_type_t
*root_type
;
104 static int root_setup
= 0;
107 return ISC_R_SUCCESS
;
109 trace_set_time_hook
= set_time
;
111 root_type
= trace_type_register ("trace-index-mapping",
112 (void *)0, trace_index_map_input
,
113 trace_index_stop_tracing
, file
, line
);
115 return ISC_R_UNEXPECTED
;
116 if (new_trace_types
== root_type
)
117 new_trace_types
= new_trace_types
-> next
;
118 root_type
-> index
= 0;
119 trace_type_stash (root_type
);
122 return ISC_R_SUCCESS
;
125 isc_result_t
trace_begin (const char *filename
,
126 const char *file
, int line
)
128 tracefile_header_t tfh
;
130 trace_type_t
*tptr
, *next
;
134 log_error ("%s(%d): trace_begin called twice",
136 return ISC_R_INVALIDARG
;
139 traceoutfile
= open (filename
, O_CREAT
| O_WRONLY
| O_EXCL
, 0644);
140 if (traceoutfile
< 0) {
141 log_error ("%s(%d): trace_begin: %s: %m",
142 file
, line
, filename
);
143 return ISC_R_UNEXPECTED
;
145 #if defined (HAVE_SETFD)
146 if (fcntl (traceoutfile
, F_SETFD
, 1) < 0)
147 log_error ("Can't set close-on-exec on %s: %m", filename
);
150 tfh
.magic
= htonl (TRACEFILE_MAGIC
);
151 tfh
.version
= htonl (TRACEFILE_VERSION
);
152 tfh
.hlen
= htonl (sizeof (tracefile_header_t
));
153 tfh
.phlen
= htonl (sizeof (tracepacket_t
));
155 status
= write (traceoutfile
, &tfh
, sizeof tfh
);
157 log_error ("%s(%d): trace_begin write failed: %m", file
, line
);
158 return ISC_R_UNEXPECTED
;
159 } else if (status
!= sizeof tfh
) {
160 log_error ("%s(%d): trace_begin: short write (%d:%ld)",
161 file
, line
, status
, (long)(sizeof tfh
));
163 return ISC_R_UNEXPECTED
;
166 /* Stash all the types that have already been set up. */
167 if (new_trace_types
) {
168 next
= new_trace_types
;
169 new_trace_types
= (trace_type_t
*)0;
170 for (tptr
= next
; tptr
; tptr
= next
) {
172 if (tptr
-> index
!= 0) {
173 result
= (trace_type_record
175 strlen (tptr
-> name
), file
, line
));
176 if (result
!= ISC_R_SUCCESS
)
182 return ISC_R_SUCCESS
;
185 isc_result_t
trace_write_packet (trace_type_t
*ttype
, unsigned length
,
186 const char *buf
, const char *file
, int line
)
192 return trace_write_packet_iov (ttype
, 1, &iov
, file
, line
);
195 isc_result_t
trace_write_packet_iov (trace_type_t
*ttype
,
196 int count
, trace_iov_t
*iov
,
197 const char *file
, int line
)
204 /* Really shouldn't get called here, but it may be hard to turn off
205 tracing midstream if the trace file write fails or something. */
210 log_error ("%s(%d): trace_write_packet with null trace type",
211 file
? file
: "<unknown file>", line
);
212 return ISC_R_INVALIDARG
;
215 log_error ("%s(%d): trace_write_packet with no tracefile.",
216 file
? file
: "<unknown file>", line
);
217 return ISC_R_INVALIDARG
;
220 /* Compute the total length of the iov. */
222 for (i
= 0; i
< count
; i
++)
223 length
+= iov
[i
].len
;
225 /* We have to swap out the data, because it may be read back on a
226 machine of different endianness. */
227 tmp
.type_index
= htonl (ttype
-> index
);
228 tmp
.when
= htonl (time ((time_t *)0)); /* XXX */
229 tmp
.length
= htonl (length
);
231 status
= write (traceoutfile
, &tmp
, sizeof tmp
);
233 log_error ("%s(%d): trace_write_packet write failed: %m",
235 return ISC_R_UNEXPECTED
;
236 } else if (status
!= sizeof tmp
) {
237 log_error ("%s(%d): trace_write_packet: short write (%d:%ld)",
238 file
, line
, status
, (long)(sizeof tmp
));
242 for (i
= 0; i
< count
; i
++) {
243 status
= write (traceoutfile
, iov
[i
].buf
, iov
[i
].len
);
245 log_error ("%s(%d): %s write failed: %m",
246 file
, line
, "trace_write_packet");
247 return ISC_R_UNEXPECTED
;
248 } else if (status
!= iov
[i
].len
) {
249 log_error ("%s(%d): %s: short write (%d:%d)",
251 "trace_write_packet", status
, length
);
256 /* Write padding on the end of the packet to align the next
257 packet to an 8-byte boundary. This is in case we decide to
258 use mmap in some clever way later on. */
260 static char zero
[] = { 0, 0, 0, 0, 0, 0, 0 };
261 unsigned padl
= 8 - (length
% 8);
263 status
= write (traceoutfile
, zero
, padl
);
265 log_error ("%s(%d): trace_write_packet write failed: %m",
267 return ISC_R_UNEXPECTED
;
268 } else if (status
!= padl
) {
269 log_error ("%s(%d): trace_write_packet: short write (%d:%d)",
270 file
, line
, status
, padl
);
275 return ISC_R_SUCCESS
;
278 void trace_type_stash (trace_type_t
*tptr
)
282 if (trace_type_max
<= tptr
-> index
) {
283 delta
= tptr
-> index
- trace_type_max
+ 10;
284 vec
= dmalloc (((trace_type_max
+ delta
) *
285 sizeof (trace_type_t
*)), MDL
);
288 memset (&vec
[trace_type_max
], 0,
289 (sizeof (trace_type_t
*)) * delta
);
290 trace_type_max
+= delta
;
292 memcpy (vec
, trace_types
,
293 trace_type_count
* sizeof (trace_type_t
*));
294 dfree (trace_types
, MDL
);
298 trace_types
[tptr
-> index
] = tptr
;
299 if (tptr
-> index
>= trace_type_count
)
300 trace_type_count
= tptr
-> index
+ 1;
303 trace_type_t
*trace_type_register (const char *name
,
305 void (*have_packet
) (trace_type_t
*,
307 void (*stop_tracing
) (trace_type_t
*),
308 const char *file
, int line
)
311 // trace_type_t *tptr;
312 unsigned slen
= strlen (name
);
315 ttmp
= dmalloc (sizeof *ttmp
, file
, line
);
319 ttmp
-> name
= dmalloc (slen
+ 1, file
, line
);
321 dfree (ttmp
, file
, line
);
322 return (trace_type_t
*)0;
324 strcpy (ttmp
-> name
, name
);
325 ttmp
-> have_packet
= have_packet
;
326 ttmp
-> stop_tracing
= stop_tracing
;
329 status
= trace_type_record (ttmp
, slen
, file
, line
);
330 if (status
!= ISC_R_SUCCESS
) {
331 dfree (ttmp
-> name
, file
, line
);
332 dfree (ttmp
, file
, line
);
333 return (trace_type_t
*)0;
336 ttmp
-> next
= new_trace_types
;
337 new_trace_types
= ttmp
;
343 static isc_result_t
trace_type_record (trace_type_t
*ttmp
, unsigned slen
,
344 const char *file
, int line
)
346 trace_index_mapping_t
*tim
;
349 tim
= dmalloc (slen
+ TRACE_INDEX_MAPPING_SIZE
, file
, line
);
351 return ISC_R_NOMEMORY
;
352 ttmp
-> index
= ++traceindex
;
353 trace_type_stash (ttmp
);
354 tim
-> index
= htonl (ttmp
-> index
);
355 memcpy (tim
-> name
, ttmp
-> name
, slen
);
356 status
= trace_write_packet (trace_types
[0],
357 slen
+ TRACE_INDEX_MAPPING_SIZE
,
358 (char *)tim
, file
, line
);
359 dfree (tim
, file
, line
);
363 /* Stop all registered trace types from trying to trace. */
365 void trace_stop (void)
369 for (i
= 0; i
< trace_type_count
; i
++)
370 if (trace_types
[i
] -> stop_tracing
)
371 (*(trace_types
[i
] -> stop_tracing
))
376 void trace_index_map_input (trace_type_t
*ttype
, unsigned length
, char *buf
)
378 trace_index_mapping_t
*tmap
;
380 trace_type_t
*tptr
, **prev
;
382 if (length
< TRACE_INDEX_MAPPING_SIZE
) {
383 log_error ("short trace index mapping");
386 tmap
= (trace_index_mapping_t
*)buf
;
388 prev
= &new_trace_types
;
389 for (tptr
= new_trace_types
; tptr
; tptr
= tptr
-> next
) {
390 len
= strlen (tptr
-> name
);
391 if (len
== length
- TRACE_INDEX_MAPPING_SIZE
&&
392 !memcmp (tptr
-> name
, tmap
-> name
, len
)) {
393 tptr
-> index
= ntohl (tmap
-> index
);
394 trace_type_stash (tptr
);
395 *prev
= tptr
-> next
;
398 prev
= &tptr
-> next
;
401 log_error ("No registered trace type for type name %.*s",
402 (int)length
- TRACE_INDEX_MAPPING_SIZE
, tmap
-> name
);
406 void trace_index_stop_tracing (trace_type_t
*ttype
) { }
408 void trace_replay_init (void)
410 trace_playback_flag
= 1;
413 void trace_file_replay (const char *filename
)
415 tracepacket_t
*tpkt
= (tracepacket_t
*)0;
417 char *buf
= (char *)0;
420 trace_type_t
*ttype
= (trace_type_t
*)0;
424 traceinfile
= fopen (filename
, "r");
426 log_error ("Can't open tracefile %s: %m", filename
);
429 #if defined (HAVE_SETFD)
430 if (fcntl (fileno (traceinfile
), F_SETFD
, 1) < 0)
431 log_error ("Can't set close-on-exec on %s: %m", filename
);
433 status
= fread (&tracefile_header
, 1,
434 sizeof tracefile_header
, traceinfile
);
435 if (status
< sizeof tracefile_header
) {
436 if (ferror (traceinfile
))
437 log_error ("Error reading trace file header: %m");
439 log_error ("Short read on trace file header: %d %ld.",
440 status
, (long)(sizeof tracefile_header
));
443 tracefile_header
.magic
= ntohl (tracefile_header
.magic
);
444 tracefile_header
.version
= ntohl (tracefile_header
.version
);
445 tracefile_header
.hlen
= ntohl (tracefile_header
.hlen
);
446 tracefile_header
.phlen
= ntohl (tracefile_header
.phlen
);
448 if (tracefile_header
.magic
!= TRACEFILE_MAGIC
) {
449 log_error ("%s: not a dhcp trace file.", filename
);
452 if (tracefile_header
.version
> TRACEFILE_VERSION
) {
453 log_error ("tracefile version %ld > current %ld.",
454 (long int)tracefile_header
.version
,
455 (long int)TRACEFILE_VERSION
);
458 if (tracefile_header
.phlen
< sizeof *tpkt
) {
459 log_error ("tracefile packet size too small - %ld < %ld",
460 (long int)tracefile_header
.phlen
,
461 (long int)sizeof *tpkt
);
464 len
= (sizeof tracefile_header
) - tracefile_header
.hlen
;
466 log_error ("tracefile header size too small - %ld < %ld",
467 (long int)tracefile_header
.hlen
,
468 (long int)sizeof tracefile_header
);
472 status
= fseek (traceinfile
, (long)len
, SEEK_CUR
);
474 log_error ("can't seek past header: %m");
479 tpkt
= dmalloc ((unsigned)tracefile_header
.phlen
, MDL
);
481 log_error ("can't allocate trace packet header.");
485 while ((result
= trace_get_next_packet (&ttype
, tpkt
, &buf
, &buflen
,
486 &bufmax
)) == ISC_R_SUCCESS
) {
487 (*ttype
-> have_packet
) (ttype
, tpkt
-> length
, buf
);
488 ttype
= (trace_type_t
*)0;
491 fclose (traceinfile
);
498 /* Get the next packet from the file. If ttp points to a nonzero pointer
499 to a trace type structure, check the next packet to see if it's of the
500 expected type, and back off if not. */
502 isc_result_t
trace_get_next_packet (trace_type_t
**ttp
,
504 char **buf
, unsigned *buflen
,
513 status
= fgetpos (traceinfile
, &curpos
);
515 log_error ("Can't save tracefile position: %m");
517 status
= fread (tpkt
, 1, (size_t)tracefile_header
.phlen
, traceinfile
);
518 if (status
< tracefile_header
.phlen
) {
519 if (ferror (traceinfile
))
520 log_error ("Error reading trace packet header: %m");
521 else if (status
== 0)
524 log_error ("Short read on trace packet header: "
527 (long int)tracefile_header
.phlen
);
528 return ISC_R_PROTOCOLERROR
;
531 /* Swap the packet. */
532 tpkt
-> type_index
= ntohl (tpkt
-> type_index
);
533 tpkt
-> length
= ntohl (tpkt
-> length
);
534 tpkt
-> when
= ntohl (tpkt
-> when
);
536 /* See if there's a handler for this packet type. */
537 if (tpkt
-> type_index
< trace_type_count
&&
538 trace_types
[tpkt
-> type_index
])
539 ttype
= trace_types
[tpkt
-> type_index
];
541 log_error ("Trace packet with unknown index %ld",
542 (long int)tpkt
-> type_index
);
543 return ISC_R_PROTOCOLERROR
;
546 /* If we were just hunting for the time marker, we've found it,
547 so back up to the beginning of the packet and return its
549 if (ttp
&& *ttp
== &trace_time_marker
) {
551 status
= fsetpos (traceinfile
, &curpos
);
553 log_error ("fsetpos in tracefile failed: %m");
554 return ISC_R_PROTOCOLERROR
;
559 /* If we were supposed to get a particular kind of packet,
560 check to see that we got the right kind. */
561 if (ttp
&& *ttp
&& ttype
!= *ttp
) {
562 log_error ("Read packet type %s when expecting %s",
563 ttype
-> name
, (*ttp
) -> name
);
564 status
= fsetpos (traceinfile
, &curpos
);
566 log_error ("fsetpos in tracefile failed: %m");
567 return ISC_R_PROTOCOLERROR
;
569 return ISC_R_UNEXPECTEDTOKEN
;
572 paylen
= tpkt
-> length
;
574 paylen
+= 8 - (tpkt
-> length
% 8);
575 if (paylen
> (*bufmax
)) {
578 (*bufmax
) = ((paylen
+ 1023) & ~1023U);
579 (*buf
) = dmalloc ((*bufmax
), MDL
);
581 log_error ("Can't allocate input buffer sized %d",
583 return ISC_R_NOMEMORY
;
587 status
= fread ((*buf
), 1, paylen
, traceinfile
);
588 if (status
< paylen
) {
589 if (ferror (traceinfile
))
590 log_error ("Error reading trace payload: %m");
592 log_error ("Short read on trace payload: %d %d.",
594 return ISC_R_PROTOCOLERROR
;
597 /* Store the actual length of the payload. */
598 *buflen
= tpkt
-> length
;
600 if (trace_set_time_hook
)
601 (*trace_set_time_hook
) (tpkt
-> when
);
605 return ISC_R_SUCCESS
;
608 isc_result_t
trace_get_packet (trace_type_t
**ttp
,
609 unsigned *buflen
, char **buf
)
616 return ISC_R_INVALIDARG
;
618 tpkt
= dmalloc ((unsigned)tracefile_header
.phlen
, MDL
);
620 log_error ("can't allocate trace packet header.");
621 return ISC_R_NOMEMORY
;
624 status
= trace_get_next_packet (ttp
, tpkt
, buf
, buflen
, &bufmax
);
630 time_t trace_snoop_time (trace_type_t
**ptp
)
635 char *buf
= (char *)0;
636 // isc_result_t status;
643 tpkt
= dmalloc ((unsigned)tracefile_header
.phlen
, MDL
);
645 log_error ("can't allocate trace packet header.");
646 return ISC_R_NOMEMORY
;
649 *ptp
= &trace_time_marker
;
650 trace_get_next_packet (ptp
, tpkt
, &buf
, &buflen
, &bufmax
);
651 result
= tpkt
-> when
;
657 /* Get a packet from the trace input file that contains a file with the
658 specified name. We don't hunt for the packet - it should be the next
659 packet in the tracefile. If it's not, or something else bad happens,
660 return an error code. */
662 isc_result_t
trace_get_file (trace_type_t
*ttype
,
663 const char *filename
, unsigned *len
, char **buf
)
671 /* Disallow some obvious bogosities. */
672 if (!buf
|| !len
|| *buf
)
673 return ISC_R_INVALIDARG
;
675 /* Save file position in case of filename mismatch. */
676 status
= fgetpos (traceinfile
, &curpos
);
678 log_error ("Can't save tracefile position: %m");
680 tpkt
= dmalloc ((unsigned)tracefile_header
.phlen
, MDL
);
682 log_error ("can't allocate trace packet header.");
683 return ISC_R_NOMEMORY
;
686 result
= trace_get_next_packet (&ttype
, tpkt
, buf
, len
, &max
);
687 if (result
!= ISC_R_SUCCESS
) {
694 /* Make sure the filename is right. */
695 if (strcmp (filename
, *buf
)) {
696 log_error ("Read file %s when expecting %s", *buf
, filename
);
697 status
= fsetpos (traceinfile
, &curpos
);
699 log_error ("fsetpos in tracefile failed: %m");
702 return ISC_R_PROTOCOLERROR
;
704 return ISC_R_UNEXPECTEDTOKEN
;
708 return ISC_R_SUCCESS
;