/* $OpenBSD: ktrstruct.c,v 1.15 2016/01/06 17:52:18 tedu Exp $ */ /*- * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdump.h" #include "kdump_subr.h" #define TIME_FORMAT "%b %e %T %Y" static void ktrsockaddr(struct sockaddr *sa) { /* TODO: Support additional address families #include struct sockaddr_mpls *mpls; */ char addr[64]; /* * note: ktrstruct() has already verified that sa points to a * buffer at least sizeof(struct sockaddr) bytes long and exactly * sa->sa_len bytes long. */ printf("struct sockaddr { "); sockfamilyname(sa->sa_family); printf(", "); #define check_sockaddr_len(n) \ if (sa_##n->s##n##_len < sizeof(struct sockaddr_##n)) { \ printf("invalid"); \ break; \ } switch(sa->sa_family) { case AF_INET: { struct sockaddr_in *sa_in; sa_in = (struct sockaddr_in *)sa; check_sockaddr_len(in); inet_ntop(AF_INET, &sa_in->sin_addr, addr, sizeof addr); printf("%s:%u", addr, ntohs(sa_in->sin_port)); break; } case AF_INET6: { struct sockaddr_in6 *sa_in6; sa_in6 = (struct sockaddr_in6 *)sa; check_sockaddr_len(in6); inet_ntop(AF_INET6, &sa_in6->sin6_addr, addr, sizeof addr); printf("[%s]:%u", addr, htons(sa_in6->sin6_port)); break; } #ifdef IPX case AF_IPX: { struct sockaddr_ipx *sa_ipx; sa_ipx = (struct sockaddr_ipx *)sa; check_sockaddr_len(ipx); /* XXX wish we had ipx_ntop */ printf("%s", ipx_ntoa(sa_ipx->sipx_addr)); break; } #endif case AF_UNIX: { struct sockaddr_un *sa_un; sa_un = (struct sockaddr_un *)sa; if (sa_un->sun_len <= offsetof(struct sockaddr_un, sun_path)) { printf("invalid"); break; } printf("\"%.*s\"", (int)(sa_un->sun_len - offsetof(struct sockaddr_un, sun_path)), sa_un->sun_path); break; } default: printf("unknown address family"); } printf(" }\n"); } static void print_time(time_t t, int relative, int have_subsec) { char timestr[PATH_MAX + 4]; struct tm *tm; if (t < 0 && have_subsec) { /* negative times with non-zero subsecs require care */ printf("-%jd", -(intmax_t)(t + 1)); } else printf("%jd", (intmax_t)t); if (!relative) { tm = localtime(&t); if (tm != NULL) { (void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); printf("<\"%s\">", timestr); } } } static void print_timespec(const struct timespec *tsp, int relative) { if (tsp->tv_nsec == UTIME_NOW) printf("UTIME_NOW"); else if (tsp->tv_nsec == UTIME_OMIT) printf("UTIME_OMIT"); else { print_time(tsp->tv_sec, relative, tsp->tv_nsec); if (tsp->tv_nsec != 0) printf(".%09ld", tsp->tv_sec >= 0 ? tsp->tv_nsec : 1000000000 - tsp->tv_nsec); } } void uidname(int uid) { const char *name; if (uid == -1) printf("-1"); else { printf("%u<", (unsigned)uid); if (uid > UID_MAX || (name = user_from_uid(uid, 1)) == NULL) printf("unknown>"); else printf("\"%s\">", name); } } void gidname(int gid) { const char *name; if (gid == -1) printf("-1"); else { printf("%u<", (unsigned)gid); if (gid > GID_MAX || (name = group_from_gid(gid, 1)) == NULL) printf("unknown>"); else printf("\"%s\">", name); } } static void ktrstat(const struct stat *statp) { char mode[12]; /* * note: ktrstruct() has already verified that statp points to a * buffer exactly sizeof(struct stat) bytes long. */ printf("struct stat { "); strmode(statp->st_mode, mode); printf("dev=%d, ino=%llu, mode=%s, nlink=%u, uid=", statp->st_dev, (unsigned long long)statp->st_ino, mode, statp->st_nlink); uidname(statp->st_uid); printf(", gid="); gidname(statp->st_gid); printf(", rdev=%d, ", statp->st_rdev); printf("atime="); print_timespec(&statp->st_atim, 0); printf(", mtime="); print_timespec(&statp->st_mtim, 0); printf(", ctime="); print_timespec(&statp->st_ctim, 0); printf(", size=%lld, blocks=%lld, blksize=%d, flags=0x%x, gen=0x%x", statp->st_size, statp->st_blocks, statp->st_blksize, statp->st_flags, statp->st_gen); printf(" }\n"); } static void ktrtimespec(const struct timespec *tsp, int relative) { printf("struct timespec { "); print_timespec(tsp, relative); printf(" }\n"); } static void print_timeval(const struct timeval *tvp, int relative) { print_time(tvp->tv_sec, relative, tvp->tv_usec); if (tvp->tv_usec != 0) printf(".%06ld", tvp->tv_sec >= 0 ? tvp->tv_usec : 1000000 - tvp->tv_usec); } static void ktrtimeval(const struct timeval *tvp, int relative) { printf("struct timeval { "); print_timeval(tvp, relative); printf(" }\n"); } static void ktrsigaction(const struct sigaction *sa) { /* * note: ktrstruct() has already verified that sa points to a * buffer exactly sizeof(struct sigaction) bytes long. */ printf("struct sigaction { "); if (sa->sa_handler == SIG_DFL) printf("handler=SIG_DFL"); else if (sa->sa_handler == SIG_IGN) printf("handler=SIG_IGN"); else if (sa->sa_flags & SA_SIGINFO) printf("sigaction=%p", (void *)sa->sa_sigaction); else printf("handler=%p", (void *)sa->sa_handler); printf(", mask="); sigset(sa->sa_mask); printf(", flags="); sigactionflagname(sa->sa_flags); printf(" }\n"); } static void print_rlim(rlim_t lim) { if (lim == RLIM_INFINITY) printf("infinite"); else printf("%llu", (unsigned long long)lim); } static void ktrrlimit(const struct rlimit *limp) { printf("struct rlimit { "); printf("cur="); print_rlim(limp->rlim_cur); printf(", max="); print_rlim(limp->rlim_max); printf(" }\n"); } static void ktrtfork(const struct __tfork *tf) { printf("struct __tfork { tcb=%p, tid=%p, stack=%p }\n", tf->tf_tcb, (void *)tf->tf_tid, tf->tf_stack); } static void ktrfdset(const struct fd_set *fds, int len) { int nfds, i, start = -1; char sep = ' '; nfds = len * NBBY; printf("struct fd_set {"); for (i = 0; i <= nfds; i++) if (i != nfds && FD_ISSET(i, fds)) { if (start == -1) start = i; } else if (start != -1) { putchar(sep); if (start == i - 1) printf("%d", start); else if (start == i - 2) printf("%d,%d", start, i - 1); else printf("%d-%d", start, i - 1); sep = ','; start = -1; } printf(" }\n"); } static void ktrrusage(const struct rusage *rup) { printf("struct rusage { utime="); print_timeval(&rup->ru_utime, 1); printf(", stime="); print_timeval(&rup->ru_stime, 1); printf(", maxrss=%ld, ixrss=%ld, idrss=%ld, isrss=%ld," " minflt=%ld, majflt=%ld, nswap=%ld, inblock=%ld," " oublock=%ld, msgsnd=%ld, msgrcv=%ld, nsignals=%ld," " nvcsw=%ld, nivcsw=%ld }\n", rup->ru_maxrss, rup->ru_ixrss, rup->ru_idrss, rup->ru_isrss, rup->ru_minflt, rup->ru_majflt, rup->ru_nswap, rup->ru_inblock, rup->ru_oublock, rup->ru_msgsnd, rup->ru_msgrcv, rup->ru_nsignals, rup->ru_nvcsw, rup->ru_nivcsw); } static void ktrquota(const struct dqblk *quota) { printf("struct dqblk { bhardlimit=%u, bsoftlimit=%u, curblocks=%u," " ihardlimit=%u, isoftlimit=%u, curinodes=%u, btime=", quota->dqb_bhardlimit, quota->dqb_bsoftlimit, quota->dqb_curblocks, quota->dqb_ihardlimit, quota->dqb_isoftlimit, quota->dqb_curinodes); print_time(quota->dqb_btime, 0, 0); printf(", itime="); print_time(quota->dqb_itime, 0, 0); printf(" }\n"); } static void ktrmsghdr(const struct msghdr *msg) { printf("struct msghdr { name=%p, namelen=%u, iov=%p, iovlen=%u," " control=%p, controllen=%u, flags=%d }\n", msg->msg_name, msg->msg_namelen, msg->msg_iov, msg->msg_iovlen, msg->msg_control, msg->msg_controllen, msg->msg_flags); } static void ktriovec(const char *data, int count) { struct iovec iov; int i; printf("struct iovec"); if (count > 1) printf(" [%d]", count); for (i = 0; i < count; i++) { memcpy(&iov, data, sizeof(iov)); data += sizeof(iov); printf(" { base=%p, len=%lu }", iov.iov_base, iov.iov_len); } printf("\n"); } static void ktrevent(const char *data, int count) { struct kevent kev; int i; printf("struct kevent"); if (count > 1) printf(" [%d]", count); for (i = 0; i < count; i++) { char flags[100]; const char *filter = ""; const char *comma = ""; memcpy(&kev, data, sizeof(kev)); data += sizeof(kev); switch (kev.filter) { case EVFILT_READ: filter = "read"; break; case EVFILT_WRITE: filter = "write"; break; case EVFILT_VNODE: filter = "vnode"; break; case EVFILT_PROC: filter = "proc"; break; case EVFILT_SIGNAL: filter = "signal"; break; case EVFILT_TIMER: filter = "timer"; break; } #define KEV_FLAG(value, name) \ do if (kev.flags & value) { \ strlcat(flags, comma, sizeof(flags)); \ strlcat(flags, name, sizeof(flags)); \ comma = ","; \ } while (0) KEV_FLAG(EV_ADD, "add"); KEV_FLAG(EV_ENABLE, "enable"); KEV_FLAG(EV_DISABLE, "disable"); KEV_FLAG(EV_DELETE, "delete"); KEV_FLAG(EV_ONESHOT, "oneshot"); KEV_FLAG(EV_CLEAR, "clear"); KEV_FLAG(EV_EOF, "EOF"); KEV_FLAG(EV_ERROR, "error"); printf(" { filter=%s flags=%s ident=%lu }", filter, flags, kev.ident); } printf("\n"); } static void ktrcmsghdr(char *data, socklen_t len) { struct msghdr msg; struct cmsghdr *cmsg; int i, count, *fds; msg.msg_control = data; msg.msg_controllen = len; /* count the control messages */ count = 0; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { count++; } printf("struct cmsghdr"); if (count > 1) printf(" [%d]", count); for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { printf(" { len=%u, level=", cmsg->cmsg_len); if (cmsg->cmsg_level == SOL_SOCKET) { printf("SOL_SOCKET, type="); switch (cmsg->cmsg_type) { case SCM_RIGHTS: printf("SCM_RIGHTS, data="); fds = (int *)CMSG_DATA(cmsg); for (i = 0; cmsg->cmsg_len > CMSG_LEN(sizeof(int) * i); i++) { printf("%s%d", i ? "," : "", fds[i]); } break; case SCM_TIMESTAMP: default: printf("%d", cmsg->cmsg_type); break; } } else { struct protoent *p = getprotobynumber(cmsg->cmsg_level); printf("%u<%s>, type=%d", cmsg->cmsg_level, p != NULL ? p->p_name : "unknown", cmsg->cmsg_type); } printf(" }"); } printf("\n"); } void ktrstruct(char *buf, size_t buflen) { char *name, *data; size_t namelen, datalen; int i; for (name = buf, namelen = 0; namelen < buflen && name[namelen] != '\0'; ++namelen) /* nothing */; if (namelen == buflen) goto invalid; if (name[namelen] != '\0') goto invalid; data = buf + namelen + 1; datalen = buflen - namelen - 1; if (datalen == 0) goto invalid; /* sanity check */ for (i = 0; i < namelen; ++i) if (!isalpha((unsigned char)name[i])) goto invalid; if (strcmp(name, "stat") == 0) { struct stat sb; if (datalen != sizeof(struct stat)) goto invalid; memcpy(&sb, data, datalen); ktrstat(&sb); } else if (strcmp(name, "sockaddr") == 0) { struct sockaddr_storage ss; if (datalen > sizeof(ss)) goto invalid; memcpy(&ss, data, datalen); if ((ss.ss_family != AF_UNIX && datalen < sizeof(struct sockaddr)) || datalen != ss.ss_len) goto invalid; ktrsockaddr((struct sockaddr *)&ss); } else if (strcmp(name, "abstimespec") == 0 || strcmp(name, "reltimespec") == 0) { struct timespec ts; if (datalen != sizeof(ts)) goto invalid; memcpy(&ts, data, datalen); ktrtimespec(&ts, name[0] == 'r'); } else if (strcmp(name, "abstimeval") == 0 || strcmp(name, "reltimeval") == 0) { struct timeval tv; if (datalen != sizeof(tv)) goto invalid; memcpy(&tv, data, datalen); ktrtimeval(&tv, name[0] == 'r'); } else if (strcmp(name, "sigaction") == 0) { struct sigaction sa; if (datalen != sizeof(sa)) goto invalid; memcpy(&sa, data, datalen); ktrsigaction(&sa); } else if (strcmp(name, "rlimit") == 0) { struct rlimit lim; if (datalen != sizeof(lim)) goto invalid; memcpy(&lim, data, datalen); ktrrlimit(&lim); } else if (strcmp(name, "rusage") == 0) { struct rusage ru; if (datalen != sizeof(ru)) goto invalid; memcpy(&ru, data, datalen); ktrrusage(&ru); } else if (strcmp(name, "tfork") == 0) { struct __tfork tf; if (datalen != sizeof(tf)) goto invalid; memcpy(&tf, data, datalen); ktrtfork(&tf); } else if (strcmp(name, "fdset") == 0) { struct fd_set *fds; if ((fds = malloc(datalen)) == NULL) err(1, "malloc"); memcpy(fds, data, datalen); ktrfdset(fds, datalen); free(fds); } else if (strcmp(name, "quota") == 0) { struct dqblk quota; if (datalen != sizeof(quota)) goto invalid; memcpy("a, data, datalen); ktrquota("a); } else if (strcmp(name, "msghdr") == 0) { struct msghdr msg; if (datalen != sizeof(msg)) goto invalid; memcpy(&msg, data, datalen); ktrmsghdr(&msg); } else if (strcmp(name, "iovec") == 0) { if (datalen % sizeof(struct iovec)) goto invalid; ktriovec(data, datalen / sizeof(struct iovec)); } else if (strcmp(name, "kevent") == 0) { if (datalen % sizeof(struct kevent)) goto invalid; ktrevent(data, datalen / sizeof(struct kevent)); } else if (strcmp(name, "cmsghdr") == 0) { char *cmsg; if ((cmsg = malloc(datalen)) == NULL) err(1, "malloc"); memcpy(cmsg, data, datalen); ktrcmsghdr(cmsg, datalen); free(cmsg); } else if (strcmp(name, "pledgereq") == 0) { printf("pledge request="); showbufc(basecol + sizeof("pledge request=") - 1, (unsigned char *)data, datalen); } else if (strcmp(name, "pledgepath") == 0) { printf("pledge path="); showbufc(basecol + sizeof("pledge path=") - 1, (unsigned char *)data, datalen); } else { printf("unknown structure %s\n", name); } return; invalid: printf("invalid record\n"); }