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