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