1 /* $OpenBSD: ktrstruct.c,v 1.28 2018/11/17 20:46:12 claudio Exp $ */ 2 3 /*- 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/resource.h> 34 #include <sys/socket.h> 35 #include <sys/select.h> 36 #include <sys/stat.h> 37 #include <sys/time.h> 38 #include <sys/event.h> 39 #include <sys/un.h> 40 #include <sys/fcntl.h> 41 #include <ufs/ufs/quota.h> 42 #include <netinet/in.h> 43 #include <arpa/inet.h> 44 45 #include <ctype.h> 46 #include <err.h> 47 #include <limits.h> 48 #include <netdb.h> 49 #include <poll.h> 50 #include <signal.h> 51 #include <stddef.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <stdint.h> 55 #include <string.h> 56 #include <grp.h> 57 #include <pwd.h> 58 #include <unistd.h> 59 #include <vis.h> 60 61 #include "kdump.h" 62 #include "kdump_subr.h" 63 64 #define TIME_FORMAT "%b %e %T %Y" 65 66 static void 67 ktrsockaddr(struct sockaddr *sa) 68 { 69 /* 70 * TODO: Support additional address families 71 * #include <netmpls/mpls.h> 72 * struct sockaddr_mpls *mpls; 73 */ 74 75 /* 76 * note: ktrstruct() has already verified that sa points to a 77 * buffer at least sizeof(struct sockaddr) bytes long and exactly 78 * sa->sa_len bytes long. 79 */ 80 printf("struct sockaddr { "); 81 sockfamilyname(sa->sa_family); 82 printf(", "); 83 84 #define check_sockaddr_len(n) \ 85 if (sa_##n->s##n##_len < sizeof(struct sockaddr_##n)) { \ 86 printf("invalid"); \ 87 break; \ 88 } 89 90 switch(sa->sa_family) { 91 case AF_INET: { 92 struct sockaddr_in *sa_in; 93 char addr[64]; 94 95 sa_in = (struct sockaddr_in *)sa; 96 check_sockaddr_len(in); 97 inet_ntop(AF_INET, &sa_in->sin_addr, addr, sizeof addr); 98 printf("%s:%u", addr, ntohs(sa_in->sin_port)); 99 break; 100 } 101 case AF_INET6: { 102 struct sockaddr_in6 *sa_in6; 103 char addr[64]; 104 105 sa_in6 = (struct sockaddr_in6 *)sa; 106 check_sockaddr_len(in6); 107 inet_ntop(AF_INET6, &sa_in6->sin6_addr, addr, sizeof addr); 108 printf("[%s]:%u", addr, htons(sa_in6->sin6_port)); 109 break; 110 } 111 case AF_UNIX: { 112 struct sockaddr_un *sa_un; 113 char path[4 * sizeof(sa_un->sun_path) + 1]; 114 size_t len; 115 116 sa_un = (struct sockaddr_un *)sa; 117 len = sa_un->sun_len; 118 if (len <= offsetof(struct sockaddr_un, sun_path)) { 119 printf("invalid"); 120 break; 121 } 122 len -= offsetof(struct sockaddr_un, sun_path); 123 if (len > sizeof(sa_un->sun_path)) { 124 printf("too long"); 125 break; 126 } 127 /* format, stopping at first NUL */ 128 len = strnlen(sa_un->sun_path, len); 129 strvisx(path, sa_un->sun_path, len, 130 VIS_CSTYLE | VIS_DQ | VIS_TAB | VIS_NL); 131 printf("\"%s\"", path); 132 break; 133 } 134 default: 135 printf("unknown address family"); 136 } 137 printf(" }\n"); 138 } 139 140 static void 141 print_time(time_t t, int relative, int have_subsec) 142 { 143 char timestr[PATH_MAX + 4]; 144 struct tm *tm; 145 146 if (t < 0 && have_subsec) { 147 /* negative times with non-zero subsecs require care */ 148 printf("-%jd", -(intmax_t)(t + 1)); 149 } else 150 printf("%jd", (intmax_t)t); 151 152 /* 1970s times are probably relative */ 153 if (!relative && t > (10 * 365 * 24 * 3600)) { 154 tm = localtime(&t); 155 if (tm != NULL) { 156 (void)strftime(timestr, sizeof(timestr), TIME_FORMAT, 157 tm); 158 printf("<\"%s\">", timestr); 159 } 160 } 161 } 162 163 static void 164 print_timespec(const struct timespec *tsp, int relative) 165 { 166 if (tsp->tv_nsec == UTIME_NOW) 167 printf("UTIME_NOW"); 168 else if (tsp->tv_nsec == UTIME_OMIT) 169 printf("UTIME_OMIT"); 170 else { 171 print_time(tsp->tv_sec, relative, tsp->tv_nsec); 172 if (tsp->tv_nsec != 0) 173 printf(".%09ld", tsp->tv_sec >= 0 ? tsp->tv_nsec : 174 1000000000 - tsp->tv_nsec); 175 } 176 } 177 178 void 179 uidname(int uid) 180 { 181 const char *name; 182 183 if (uid == -1) 184 printf("-1"); 185 else { 186 printf("%u<", (unsigned)uid); 187 if (uid > UID_MAX || (name = user_from_uid(uid, 1)) == NULL) 188 printf("unknown>"); 189 else 190 printf("\"%s\">", name); 191 } 192 } 193 194 void 195 gidname(int gid) 196 { 197 const char *name; 198 199 if (gid == -1) 200 printf("-1"); 201 else { 202 printf("%u<", (unsigned)gid); 203 if (gid > GID_MAX || (name = group_from_gid(gid, 1)) == NULL) 204 printf("unknown>"); 205 else 206 printf("\"%s\">", name); 207 } 208 } 209 210 static void 211 ktrstat(const struct stat *statp) 212 { 213 char mode[12]; 214 215 /* 216 * note: ktrstruct() has already verified that statp points to a 217 * buffer exactly sizeof(struct stat) bytes long. 218 */ 219 printf("struct stat { "); 220 strmode(statp->st_mode, mode); 221 printf("dev=%d, ino=%llu, mode=%s, nlink=%u, uid=", 222 statp->st_dev, (unsigned long long)statp->st_ino, 223 mode, statp->st_nlink); 224 uidname(statp->st_uid); 225 printf(", gid="); 226 gidname(statp->st_gid); 227 printf(", rdev=%d, ", statp->st_rdev); 228 printf("atime="); 229 print_timespec(&statp->st_atim, 0); 230 printf(", mtime="); 231 print_timespec(&statp->st_mtim, 0); 232 printf(", ctime="); 233 print_timespec(&statp->st_ctim, 0); 234 printf(", size=%lld, blocks=%lld, blksize=%d, flags=0x%x, gen=0x%x", 235 statp->st_size, statp->st_blocks, statp->st_blksize, 236 statp->st_flags, statp->st_gen); 237 printf(" }\n"); 238 } 239 240 static void 241 ktrtimespec(const struct timespec *tsp, int relative) 242 { 243 printf("struct timespec { "); 244 print_timespec(tsp, relative); 245 printf(" }\n"); 246 } 247 248 static void 249 print_timeval(const struct timeval *tvp, int relative) 250 { 251 print_time(tvp->tv_sec, relative, tvp->tv_usec); 252 if (tvp->tv_usec != 0) 253 printf(".%06ld", tvp->tv_sec >= 0 ? tvp->tv_usec : 254 1000000 - tvp->tv_usec); 255 } 256 257 static void 258 ktrtimeval(const struct timeval *tvp, int relative) 259 { 260 printf("struct timeval { "); 261 print_timeval(tvp, relative); 262 printf(" }\n"); 263 } 264 265 static void 266 ktrsigaction(const struct sigaction *sa) 267 { 268 /* 269 * note: ktrstruct() has already verified that sa points to a 270 * buffer exactly sizeof(struct sigaction) bytes long. 271 */ 272 /* 273 * Fuck! Comparison of function pointers on hppa assumes you can 274 * dereference them if they're plabels! Cast everything to void * 275 * to suppress that extra logic; sorry folks, the address we report 276 * here might not match what you see in your executable... 277 */ 278 printf("struct sigaction { "); 279 if ((void *)sa->sa_handler == (void *)SIG_DFL) 280 printf("handler=SIG_DFL"); 281 else if ((void *)sa->sa_handler == (void *)SIG_IGN) 282 printf("handler=SIG_IGN"); 283 else if (sa->sa_flags & SA_SIGINFO) 284 printf("sigaction=%p", (void *)sa->sa_sigaction); 285 else 286 printf("handler=%p", (void *)sa->sa_handler); 287 printf(", mask="); 288 sigset(sa->sa_mask); 289 printf(", flags="); 290 sigactionflagname(sa->sa_flags); 291 printf(" }\n"); 292 } 293 294 static void 295 print_rlim(rlim_t lim) 296 { 297 if (lim == RLIM_INFINITY) 298 printf("infinite"); 299 else 300 printf("%llu", (unsigned long long)lim); 301 } 302 303 static void 304 ktrrlimit(const struct rlimit *limp) 305 { 306 printf("struct rlimit { "); 307 printf("cur="); 308 print_rlim(limp->rlim_cur); 309 printf(", max="); 310 print_rlim(limp->rlim_max); 311 printf(" }\n"); 312 } 313 314 static void 315 ktrtfork(const struct __tfork *tf) 316 { 317 printf("struct __tfork { tcb=%p, tid=%p, stack=%p }\n", 318 tf->tf_tcb, (void *)tf->tf_tid, tf->tf_stack); 319 } 320 321 static void 322 ktrfds(const char *data, size_t count) 323 { 324 size_t i; 325 int fd; 326 327 printf("int"); 328 if (count > 1) 329 printf(" [%zu] { ", count); 330 for (i = 0; i < count; i++) { 331 memcpy(&fd, &data[i * sizeof(fd)], sizeof(fd)); 332 printf("%d%s", fd, i < count - 1 ? ", " : ""); 333 } 334 if (count > 1) 335 printf(" }"); 336 printf("\n"); 337 } 338 339 static void 340 ktrfdset(struct fd_set *fds, int len) 341 { 342 int nfds, i, start = -1; 343 char sep = ' '; 344 345 nfds = len * NBBY; 346 printf("struct fd_set {"); 347 for (i = 0; i <= nfds; i++) 348 if (i != nfds && FD_ISSET(i, fds)) { 349 if (start == -1) 350 start = i; 351 } else if (start != -1) { 352 putchar(sep); 353 if (start == i - 1) 354 printf("%d", start); 355 else if (start == i - 2) 356 printf("%d,%d", start, i - 1); 357 else 358 printf("%d-%d", start, i - 1); 359 sep = ','; 360 start = -1; 361 } 362 363 printf(" }\n"); 364 } 365 366 static void 367 ktrrusage(const struct rusage *rup) 368 { 369 printf("struct rusage { utime="); 370 print_timeval(&rup->ru_utime, 1); 371 printf(", stime="); 372 print_timeval(&rup->ru_stime, 1); 373 printf(", maxrss=%ld, ixrss=%ld, idrss=%ld, isrss=%ld," 374 " minflt=%ld, majflt=%ld, nswap=%ld, inblock=%ld," 375 " oublock=%ld, msgsnd=%ld, msgrcv=%ld, nsignals=%ld," 376 " nvcsw=%ld, nivcsw=%ld }\n", 377 rup->ru_maxrss, rup->ru_ixrss, rup->ru_idrss, rup->ru_isrss, 378 rup->ru_minflt, rup->ru_majflt, rup->ru_nswap, rup->ru_inblock, 379 rup->ru_oublock, rup->ru_msgsnd, rup->ru_msgrcv, rup->ru_nsignals, 380 rup->ru_nvcsw, rup->ru_nivcsw); 381 } 382 383 static void 384 ktrquota(const struct dqblk *quota) 385 { 386 printf("struct dqblk { bhardlimit=%u, bsoftlimit=%u, curblocks=%u," 387 " ihardlimit=%u, isoftlimit=%u, curinodes=%u, btime=", 388 quota->dqb_bhardlimit, quota->dqb_bsoftlimit, 389 quota->dqb_curblocks, quota->dqb_ihardlimit, 390 quota->dqb_isoftlimit, quota->dqb_curinodes); 391 print_time(quota->dqb_btime, 0, 0); 392 printf(", itime="); 393 print_time(quota->dqb_itime, 0, 0); 394 printf(" }\n"); 395 } 396 397 static void 398 ktrmsghdr(const struct msghdr *msg) 399 { 400 printf("struct msghdr { name=%p, namelen=%u, iov=%p, iovlen=%u," 401 " control=%p, controllen=%u, flags=", 402 msg->msg_name, msg->msg_namelen, msg->msg_iov, msg->msg_iovlen, 403 msg->msg_control, msg->msg_controllen); 404 sendrecvflagsname(msg->msg_flags); 405 printf(" }\n"); 406 } 407 408 static void 409 ktriovec(const char *data, int count) 410 { 411 struct iovec iov; 412 int i; 413 414 printf("struct iovec"); 415 if (count > 1) 416 printf(" [%d]", count); 417 for (i = 0; i < count; i++) { 418 memcpy(&iov, data, sizeof(iov)); 419 data += sizeof(iov); 420 printf(" { base=%p, len=%lu }", iov.iov_base, iov.iov_len); 421 } 422 printf("\n"); 423 } 424 425 static void 426 ktrevent(const char *data, int count) 427 { 428 struct kevent kev; 429 int i; 430 431 printf("struct kevent"); 432 if (count > 1) 433 printf(" [%d]", count); 434 for (i = 0; i < count; i++) { 435 memcpy(&kev, data, sizeof(kev)); 436 data += sizeof(kev); 437 printf(" { ident=%lu, filter=", kev.ident); 438 evfiltername(kev.filter); 439 printf(", flags="); 440 evflagsname(kev.flags); 441 printf(", fflags="); 442 evfflagsname(kev.filter, kev.fflags); 443 printf(", data=%llu", kev.data); 444 if ((kev.flags & EV_ERROR) && fancy) { 445 printf("<\"%s\">", strerror(kev.data)); 446 } 447 printf(", udata=%p }", kev.udata); 448 } 449 printf("\n"); 450 } 451 452 static void 453 ktrpollfd(const char *data, int count) 454 { 455 struct pollfd pfd; 456 int i; 457 458 printf("struct pollfd"); 459 if (count > 1) 460 printf(" [%d]", count); 461 for (i = 0; i < count; i++) { 462 memcpy(&pfd, data, sizeof(pfd)); 463 data += sizeof(pfd); 464 printf(" { fd=%d, events=", pfd.fd); 465 pollfdeventname(pfd.events); 466 printf(", revents="); 467 pollfdeventname(pfd.revents); 468 printf(" }"); 469 } 470 printf("\n"); 471 } 472 473 static void 474 ktrcmsghdr(char *data, socklen_t len) 475 { 476 struct msghdr msg; 477 struct cmsghdr *cmsg; 478 int i, count, *fds; 479 480 msg.msg_control = data; 481 msg.msg_controllen = len; 482 483 /* count the control messages */ 484 count = 0; 485 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 486 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 487 count++; 488 } 489 490 printf("struct cmsghdr"); 491 if (count > 1) 492 printf(" [%d]", count); 493 494 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 495 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 496 printf(" { len=%u, level=", cmsg->cmsg_len); 497 if (cmsg->cmsg_level == SOL_SOCKET) { 498 printf("SOL_SOCKET, type="); 499 switch (cmsg->cmsg_type) { 500 case SCM_RIGHTS: 501 printf("SCM_RIGHTS, data="); 502 fds = (int *)CMSG_DATA(cmsg); 503 for (i = 0; 504 cmsg->cmsg_len > CMSG_LEN(sizeof(int) * i) 505 && (char *)fds + (i + 1) * sizeof(int) <= 506 data + len; 507 i++) { 508 printf("%s%d", i ? "," : "", fds[i]); 509 } 510 break; 511 case SCM_TIMESTAMP: 512 default: 513 printf("%d", cmsg->cmsg_type); 514 break; 515 } 516 } else { 517 struct protoent *p = getprotobynumber(cmsg->cmsg_level); 518 519 printf("%u<%s>, type=%d", cmsg->cmsg_level, 520 p != NULL ? p->p_name : "unknown", cmsg->cmsg_type); 521 } 522 printf(" }"); 523 } 524 printf("\n"); 525 } 526 527 static void 528 ktrflock(const struct flock *fl) 529 { 530 printf("struct flock { start=%lld, len=%lld, pid=%d, type=", 531 fl->l_start, fl->l_len, fl->l_pid); 532 flocktypename(fl->l_type); 533 printf(", whence="); 534 whencename(fl->l_whence); 535 printf(" }\n"); 536 } 537 538 void 539 ktrstruct(char *buf, size_t buflen) 540 { 541 char *name, *data; 542 size_t namelen, datalen; 543 int i; 544 545 for (name = buf, namelen = 0; namelen < buflen && name[namelen] != '\0'; 546 ++namelen) 547 /* nothing */; 548 if (namelen == buflen) 549 goto invalid; 550 if (name[namelen] != '\0') 551 goto invalid; 552 data = buf + namelen + 1; 553 datalen = buflen - namelen - 1; 554 555 /* sanity check */ 556 for (i = 0; i < namelen; ++i) 557 if (!isalpha((unsigned char)name[i])) 558 goto invalid; 559 if (strcmp(name, "stat") == 0) { 560 struct stat sb; 561 562 if (datalen != sizeof(struct stat)) 563 goto invalid; 564 memcpy(&sb, data, datalen); 565 ktrstat(&sb); 566 } else if (strcmp(name, "sockaddr") == 0) { 567 struct sockaddr_storage ss; 568 569 if (datalen > sizeof(ss)) 570 goto invalid; 571 if (datalen < offsetof(struct sockaddr_storage, ss_len) + 572 sizeof(ss.ss_len)) 573 goto invalid; 574 memcpy(&ss, data, datalen); 575 if ((ss.ss_family != AF_UNIX && 576 datalen < sizeof(struct sockaddr)) || datalen != ss.ss_len) 577 goto invalid; 578 ktrsockaddr((struct sockaddr *)&ss); 579 } else if (strcmp(name, "abstimespec") == 0 || 580 strcmp(name, "reltimespec") == 0) { 581 struct timespec ts; 582 583 if (datalen != sizeof(ts)) 584 goto invalid; 585 memcpy(&ts, data, datalen); 586 ktrtimespec(&ts, name[0] == 'r'); 587 } else if (strcmp(name, "abstimeval") == 0 || 588 strcmp(name, "reltimeval") == 0) { 589 struct timeval tv; 590 591 if (datalen != sizeof(tv)) 592 goto invalid; 593 memcpy(&tv, data, datalen); 594 ktrtimeval(&tv, name[0] == 'r'); 595 } else if (strcmp(name, "sigaction") == 0) { 596 struct sigaction sa; 597 598 if (datalen != sizeof(sa)) 599 goto invalid; 600 memcpy(&sa, data, datalen); 601 ktrsigaction(&sa); 602 } else if (strcmp(name, "rlimit") == 0) { 603 struct rlimit lim; 604 605 if (datalen != sizeof(lim)) 606 goto invalid; 607 memcpy(&lim, data, datalen); 608 ktrrlimit(&lim); 609 } else if (strcmp(name, "rusage") == 0) { 610 struct rusage ru; 611 612 if (datalen != sizeof(ru)) 613 goto invalid; 614 memcpy(&ru, data, datalen); 615 ktrrusage(&ru); 616 } else if (strcmp(name, "tfork") == 0) { 617 struct __tfork tf; 618 619 if (datalen != sizeof(tf)) 620 goto invalid; 621 memcpy(&tf, data, datalen); 622 ktrtfork(&tf); 623 } else if (strcmp(name, "fds") == 0) { 624 if (datalen % sizeof(int)) 625 goto invalid; 626 ktrfds(data, datalen / sizeof(int)); 627 } else if (strcmp(name, "fdset") == 0) { 628 struct fd_set *fds; 629 630 if ((fds = malloc(datalen)) == NULL) 631 err(1, "malloc"); 632 memcpy(fds, data, datalen); 633 ktrfdset(fds, datalen); 634 free(fds); 635 } else if (strcmp(name, "quota") == 0) { 636 struct dqblk quota; 637 638 if (datalen != sizeof(quota)) 639 goto invalid; 640 memcpy("a, data, datalen); 641 ktrquota("a); 642 } else if (strcmp(name, "msghdr") == 0) { 643 struct msghdr msg; 644 645 if (datalen != sizeof(msg)) 646 goto invalid; 647 memcpy(&msg, data, datalen); 648 ktrmsghdr(&msg); 649 } else if (strcmp(name, "iovec") == 0) { 650 if (datalen % sizeof(struct iovec)) 651 goto invalid; 652 ktriovec(data, datalen / sizeof(struct iovec)); 653 } else if (strcmp(name, "kevent") == 0) { 654 if (datalen % sizeof(struct kevent)) 655 goto invalid; 656 ktrevent(data, datalen / sizeof(struct kevent)); 657 } else if (strcmp(name, "pollfd") == 0) { 658 if (datalen % sizeof(struct pollfd)) 659 goto invalid; 660 ktrpollfd(data, datalen / sizeof(struct pollfd)); 661 } else if (strcmp(name, "cmsghdr") == 0) { 662 char *cmsg; 663 664 if (datalen == 0) 665 goto invalid; 666 667 if ((cmsg = malloc(datalen)) == NULL) 668 err(1, "malloc"); 669 memcpy(cmsg, data, datalen); 670 ktrcmsghdr(cmsg, datalen); 671 free(cmsg); 672 } else if (strcmp(name, "pledgereq") == 0) { 673 printf("promise="); 674 showbufc(basecol + sizeof("promise=") - 1, 675 (unsigned char *)data, datalen, VIS_DQ | VIS_TAB | VIS_NL); 676 } else if (strcmp(name, "pledgeexecreq") == 0) { 677 printf("execpromise="); 678 showbufc(basecol + sizeof("execpromise=") - 1, 679 (unsigned char *)data, datalen, VIS_DQ | VIS_TAB | VIS_NL); 680 } else if (strcmp(name, "unveil") == 0) { 681 printf("flags="); 682 showbufc(basecol + sizeof("flags=") - 1, 683 (unsigned char *)data, datalen, VIS_DQ | VIS_TAB | VIS_NL); 684 } else if (strcmp(name, "flock") == 0) { 685 struct flock fl; 686 687 if (datalen != sizeof(fl)) 688 goto invalid; 689 memcpy(&fl, data, datalen); 690 ktrflock(&fl); 691 } else { 692 printf("unknown structure %s\n", name); 693 } 694 return; 695 invalid: 696 printf("invalid record\n"); 697 } 698