Annotation of src/usr.bin/kdump/kdump.c, Revision 1.29
1.29 ! miod 1: /* $OpenBSD: kdump.c,v 1.28 2005/09/10 21:06:21 deraadt Exp $ */
1.4 deraadt 2:
1.1 deraadt 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.
1.21 millert 15: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 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: #ifndef lint
33: static char copyright[] =
34: "@(#) Copyright (c) 1988, 1993\n\
35: The Regents of the University of California. All rights reserved.\n";
36: #endif /* not lint */
37:
38: #ifndef lint
39: #if 0
40: static char sccsid[] = "@(#)kdump.c 8.4 (Berkeley) 4/28/95";
41: #endif
1.29 ! miod 42: static char *rcsid = "$OpenBSD: kdump.c,v 1.28 2005/09/10 21:06:21 deraadt Exp $";
1.1 deraadt 43: #endif /* not lint */
44:
45: #include <sys/param.h>
46: #include <sys/time.h>
47: #include <sys/uio.h>
48: #include <sys/ktrace.h>
49: #include <sys/ioctl.h>
50: #include <sys/ptrace.h>
1.28 deraadt 51: #include <sys/sysctl.h>
1.1 deraadt 52: #define _KERNEL
53: #include <sys/errno.h>
54: #undef _KERNEL
55:
56: #include <err.h>
57: #include <signal.h>
58: #include <stdio.h>
59: #include <stdlib.h>
60: #include <string.h>
61: #include <unistd.h>
62: #include <vis.h>
63:
64: #include "ktrace.h"
1.22 deraadt 65: #include "kdump.h"
1.12 espie 66: #include "extern.h"
1.1 deraadt 67:
68: int timestamp, decimal, fancy = 1, tail, maxdata;
69: char *tracefile = DEF_TRACEFILE;
70: struct ktr_header ktr_header;
1.17 deraadt 71: pid_t pid = -1;
1.1 deraadt 72:
73: #define eqs(s1, s2) (strcmp((s1), (s2)) == 0)
74:
75: #include <sys/syscall.h>
76:
1.25 mickey 77: #include <compat/bsdos/bsdos_syscall.h>
78: #include <compat/freebsd/freebsd_syscall.h>
79: #include <compat/netbsd/netbsd_syscall.h>
1.26 deraadt 80: #if defined(__hppa__) || defined(__m68k__)
1.25 mickey 81: #include <compat/hpux/hpux_syscall.h>
1.26 deraadt 82: #endif
1.25 mickey 83: #include <compat/ibcs2/ibcs2_syscall.h>
84: #include <compat/linux/linux_syscall.h>
85: #include <compat/osf1/osf1_syscall.h>
86: #include <compat/sunos/sunos_syscall.h>
87: #include <compat/svr4/svr4_syscall.h>
88: #include <compat/ultrix/ultrix_syscall.h>
1.1 deraadt 89:
90: #define KTRACE
1.19 mickey 91: #define PTRACE
1.7 deraadt 92: #define NFSCLIENT
93: #define NFSSERVER
94: #define SYSVSEM
95: #define SYSVMSG
96: #define SYSVSHM
97: #define LFS
1.25 mickey 98: #include <kern/syscalls.c>
1.1 deraadt 99:
1.25 mickey 100: #include <compat/bsdos/bsdos_syscalls.c>
101: #include <compat/freebsd/freebsd_syscalls.c>
102: #include <compat/netbsd/netbsd_syscalls.c>
1.26 deraadt 103: #if defined(__hppa__) || defined(__m68k__)
1.25 mickey 104: #include <compat/hpux/hpux_syscalls.c>
1.26 deraadt 105: #endif
1.25 mickey 106: #include <compat/ibcs2/ibcs2_syscalls.c>
107: #include <compat/linux/linux_syscalls.c>
108: #include <compat/osf1/osf1_syscalls.c>
109: #include <compat/sunos/sunos_syscalls.c>
110: #include <compat/svr4/svr4_syscalls.c>
111: #include <compat/ultrix/ultrix_syscalls.c>
1.1 deraadt 112: #undef KTRACE
1.19 mickey 113: #undef PTRACE
1.7 deraadt 114: #undef NFSCLIENT
115: #undef NFSSERVER
116: #undef SYSVSEM
117: #undef SYSVMSG
118: #undef SYSVSHM
119: #undef LFS
1.1 deraadt 120:
121: struct emulation {
122: char *name; /* Emulation name */
123: char **sysnames; /* Array of system call names */
124: int nsysnames; /* Number of */
125: };
126:
127: static struct emulation emulations[] = {
1.9 deraadt 128: { "native", syscallnames, SYS_MAXSYSCALL },
1.26 deraadt 129: #if defined(__hppa__) || defined(__m68k__)
1.9 deraadt 130: { "hpux", hpux_syscallnames, HPUX_SYS_MAXSYSCALL },
1.26 deraadt 131: #endif
1.9 deraadt 132: { "ibcs2", ibcs2_syscallnames, IBCS2_SYS_MAXSYSCALL },
133: { "linux", linux_syscallnames, LINUX_SYS_MAXSYSCALL },
134: { "osf1", osf1_syscallnames, OSF1_SYS_MAXSYSCALL },
135: { "sunos", sunos_syscallnames, SUNOS_SYS_MAXSYSCALL },
136: { "svr4", svr4_syscallnames, SVR4_SYS_MAXSYSCALL },
137: { "ultrix", ultrix_syscallnames, ULTRIX_SYS_MAXSYSCALL },
138: { "bsdos", bsdos_syscallnames, BSDOS_SYS_MAXSYSCALL },
139: { "freebsd", freebsd_syscallnames, FREEBSD_SYS_MAXSYSCALL },
1.10 kstailey 140: { "netbsd", netbsd_syscallnames, NETBSD_SYS_MAXSYSCALL },
1.9 deraadt 141: { NULL, NULL, NULL }
1.1 deraadt 142: };
143:
144: struct emulation *current;
145:
146:
147: static char *ptrace_ops[] = {
148: "PT_TRACE_ME", "PT_READ_I", "PT_READ_D", "PT_READ_U",
149: "PT_WRITE_I", "PT_WRITE_D", "PT_WRITE_U", "PT_CONTINUE",
1.15 art 150: "PT_KILL", "PT_ATTACH", "PT_DETACH", "PT_IO",
1.1 deraadt 151: };
152:
1.13 millert 153: static int fread_tail(void *, int, int);
154: static void dumpheader(struct ktr_header *);
155: static void ktrcsw(struct ktr_csw *);
156: static void ktremul(char *, int);
157: static void ktrgenio(struct ktr_genio *, int);
158: static void ktrnamei(const char *, int);
159: static void ktrpsig(struct ktr_psig *);
160: static void ktrsyscall(struct ktr_syscall *);
161: static void ktrsysret(struct ktr_sysret *);
162: static void setemul(const char *);
163: static void usage(void);
1.12 espie 164:
1.1 deraadt 165: int
1.17 deraadt 166: main(int argc, char *argv[])
1.1 deraadt 167: {
1.17 deraadt 168: int ch, ktrlen, size, silent;
169: int trpoints = ALL_POINTS;
1.12 espie 170: void *m;
1.1 deraadt 171:
1.3 deraadt 172: current = &emulations[0]; /* native */
1.1 deraadt 173:
1.17 deraadt 174: while ((ch = getopt(argc, argv, "e:f:dlm:nRp:Tt:")) != -1)
1.1 deraadt 175: switch (ch) {
176: case 'e':
177: setemul(optarg);
178: break;
179: case 'f':
180: tracefile = optarg;
181: break;
182: case 'd':
183: decimal = 1;
184: break;
185: case 'l':
186: tail = 1;
187: break;
188: case 'm':
189: maxdata = atoi(optarg);
190: break;
191: case 'n':
192: fancy = 0;
193: break;
1.17 deraadt 194: case 'p':
195: pid = atoi(optarg);
196: break;
1.1 deraadt 197: case 'R':
198: timestamp = 2; /* relative timestamp */
199: break;
200: case 'T':
201: timestamp = 1;
202: break;
203: case 't':
204: trpoints = getpoints(optarg);
205: if (trpoints < 0)
206: errx(1, "unknown trace point in %s", optarg);
207: break;
208: default:
209: usage();
210: }
1.5 deraadt 211: if (argc > optind)
1.1 deraadt 212: usage();
213:
214: m = (void *)malloc(size = 1025);
215: if (m == NULL)
216: errx(1, "%s", strerror(ENOMEM));
217: if (!freopen(tracefile, "r", stdin))
218: err(1, "%s", tracefile);
219: while (fread_tail(&ktr_header, sizeof(struct ktr_header), 1)) {
1.17 deraadt 220: silent = 0;
221: if (pid != -1 && pid != ktr_header.ktr_pid)
222: silent = 1;
223: if (silent == 0 && trpoints & (1<<ktr_header.ktr_type))
1.1 deraadt 224: dumpheader(&ktr_header);
225: if ((ktrlen = ktr_header.ktr_len) < 0)
226: errx(1, "bogus length 0x%x", ktrlen);
227: if (ktrlen > size) {
1.23 tedu 228: void *newm;
229:
230: newm = realloc(m, ktrlen+1);
231: if (newm == NULL)
1.1 deraadt 232: errx(1, "%s", strerror(ENOMEM));
1.23 tedu 233: m = newm;
1.1 deraadt 234: size = ktrlen;
235: }
236: if (ktrlen && fread_tail(m, ktrlen, 1) == 0)
237: errx(1, "data too short");
1.17 deraadt 238: if (silent)
239: continue;
1.1 deraadt 240: if ((trpoints & (1<<ktr_header.ktr_type)) == 0)
241: continue;
242: switch (ktr_header.ktr_type) {
243: case KTR_SYSCALL:
244: ktrsyscall((struct ktr_syscall *)m);
245: break;
246: case KTR_SYSRET:
247: ktrsysret((struct ktr_sysret *)m);
248: break;
249: case KTR_NAMEI:
250: ktrnamei(m, ktrlen);
251: break;
252: case KTR_GENIO:
253: ktrgenio((struct ktr_genio *)m, ktrlen);
254: break;
255: case KTR_PSIG:
256: ktrpsig((struct ktr_psig *)m);
257: break;
258: case KTR_CSW:
259: ktrcsw((struct ktr_csw *)m);
260: break;
261: case KTR_EMUL:
262: ktremul(m, ktrlen);
263: break;
264: }
265: if (tail)
266: (void)fflush(stdout);
267: }
1.12 espie 268: exit(0);
1.1 deraadt 269: }
270:
1.12 espie 271: static int
1.17 deraadt 272: fread_tail(void *buf, int size, int num)
1.1 deraadt 273: {
274: int i;
275:
276: while ((i = fread(buf, size, num, stdin)) == 0 && tail) {
277: (void)sleep(1);
278: clearerr(stdin);
279: }
280: return (i);
281: }
282:
1.12 espie 283: static void
1.17 deraadt 284: dumpheader(struct ktr_header *kth)
1.1 deraadt 285: {
1.17 deraadt 286: static struct timeval prevtime;
1.1 deraadt 287: char unknown[64], *type;
288: struct timeval temp;
289:
290: switch (kth->ktr_type) {
291: case KTR_SYSCALL:
292: type = "CALL";
293: break;
294: case KTR_SYSRET:
295: type = "RET ";
296: break;
297: case KTR_NAMEI:
298: type = "NAMI";
299: break;
300: case KTR_GENIO:
301: type = "GIO ";
302: break;
303: case KTR_PSIG:
304: type = "PSIG";
305: break;
306: case KTR_CSW:
307: type = "CSW";
308: break;
309: case KTR_EMUL:
310: type = "EMUL";
311: break;
312: default:
1.17 deraadt 313: (void)snprintf(unknown, sizeof unknown, "UNKNOWN(%d)",
314: kth->ktr_type);
1.1 deraadt 315: type = unknown;
316: }
317:
1.16 mpech 318: (void)printf("%6ld %-8.*s ", (long)kth->ktr_pid, MAXCOMLEN,
319: kth->ktr_comm);
1.1 deraadt 320: if (timestamp) {
321: if (timestamp == 2) {
322: timersub(&kth->ktr_time, &prevtime, &temp);
323: prevtime = kth->ktr_time;
324: } else
325: temp = kth->ktr_time;
326: (void)printf("%ld.%06ld ", temp.tv_sec, temp.tv_usec);
327: }
328: (void)printf("%s ", type);
329: }
330:
1.12 espie 331: static void
1.17 deraadt 332: ioctldecode(u_long cmd)
1.2 deraadt 333: {
334: char dirbuf[4], *dir = dirbuf;
335:
1.6 deraadt 336: if (cmd & IOC_IN)
337: *dir++ = 'W';
1.2 deraadt 338: if (cmd & IOC_OUT)
339: *dir++ = 'R';
340: *dir = '\0';
341:
342: printf(decimal ? ",_IO%s('%c',%ld" : ",_IO%s('%c',%#lx",
343: dirbuf, (cmd >> 8) & 0xff, cmd & 0xff);
344: if ((cmd & IOC_VOID) == 0)
345: printf(decimal ? ",%ld)" : ",%#lx)", (cmd >> 16) & 0xff);
346: else
347: printf(")");
348: }
1.1 deraadt 349:
1.12 espie 350: static void
1.17 deraadt 351: ktrsyscall(struct ktr_syscall *ktr)
1.1 deraadt 352: {
1.12 espie 353: int argsize = ktr->ktr_argsize;
354: register_t *ap;
1.1 deraadt 355:
356: if (ktr->ktr_code >= current->nsysnames || ktr->ktr_code < 0)
357: (void)printf("[%d]", ktr->ktr_code);
358: else
359: (void)printf("%s", current->sysnames[ktr->ktr_code]);
360: ap = (register_t *)((char *)ktr + sizeof(struct ktr_syscall));
1.27 mickey 361: (void)putchar('(');
1.1 deraadt 362: if (argsize) {
1.27 mickey 363: char c = '\0';
1.1 deraadt 364: if (fancy) {
365: if (ktr->ktr_code == SYS_ioctl) {
1.12 espie 366: const char *cp;
1.17 deraadt 367:
1.1 deraadt 368: if (decimal)
1.27 mickey 369: (void)printf("%ld", (long)*ap);
1.1 deraadt 370: else
1.27 mickey 371: (void)printf("%#lx", (long)*ap);
1.1 deraadt 372: ap++;
373: argsize -= sizeof(register_t);
374: if ((cp = ioctlname(*ap)) != NULL)
375: (void)printf(",%s", cp);
1.2 deraadt 376: else
377: ioctldecode(*ap);
1.1 deraadt 378: c = ',';
379: ap++;
380: argsize -= sizeof(register_t);
1.27 mickey 381: } else if (ktr->ktr_code == SYS___sysctl) {
382: int *np, n;
383:
384: n = ap[1];
1.28 deraadt 385: if (n > CTL_MAXNAME)
386: n = CTL_MAXNAME;
1.27 mickey 387: np = (int *)(ap + 6);
388: for (; n--; np++) {
389: if (c)
390: putchar(c);
391: printf("%d", *np);
392: c = '.';
393: }
394:
395: c = ',';
396: ap += 2;
397: argsize -= 2 * sizeof(register_t);
1.1 deraadt 398: } else if (ktr->ktr_code == SYS_ptrace) {
1.24 miod 399: if (*ap >= 0 && *ap <
1.1 deraadt 400: sizeof(ptrace_ops) / sizeof(ptrace_ops[0]))
1.27 mickey 401: (void)printf("%s", ptrace_ops[*ap]);
1.24 miod 402: else switch(*ap) {
403: #ifdef PT_GETFPREGS
404: case PT_GETFPREGS:
1.27 mickey 405: (void)printf("PT_GETFPREGS");
1.24 miod 406: break;
407: #endif
408: case PT_GETREGS:
1.27 mickey 409: (void)printf("PT_GETREGS");
1.24 miod 410: break;
411: #ifdef PT_SETFPREGS
412: case PT_SETFPREGS:
1.27 mickey 413: (void)printf("PT_SETFPREGS");
1.24 miod 414: break;
415: #endif
416: case PT_SETREGS:
1.27 mickey 417: (void)printf("PT_SETREGS");
1.24 miod 418: break;
419: #ifdef PT_STEP
420: case PT_STEP:
1.27 mickey 421: (void)printf("PT_STEP");
1.24 miod 422: break;
423: #endif
424: #ifdef PT_WCOOKIE
425: case PT_WCOOKIE:
1.27 mickey 426: (void)printf("PT_WCOOKIE");
1.24 miod 427: break;
428: #endif
429: default:
1.27 mickey 430: (void)printf("%ld", (long)*ap);
1.24 miod 431: break;
432: }
1.1 deraadt 433: c = ',';
434: ap++;
435: argsize -= sizeof(register_t);
436: }
437: }
438: while (argsize) {
1.27 mickey 439: if (c)
440: putchar(c);
1.1 deraadt 441: if (decimal)
1.27 mickey 442: (void)printf("%ld", (long)*ap);
1.1 deraadt 443: else
1.27 mickey 444: (void)printf("%#lx", (long)*ap);
1.1 deraadt 445: c = ',';
446: ap++;
447: argsize -= sizeof(register_t);
448: }
449: }
1.27 mickey 450: (void)printf(")\n");
1.1 deraadt 451: }
452:
1.12 espie 453: static void
1.17 deraadt 454: ktrsysret(struct ktr_sysret *ktr)
1.1 deraadt 455: {
1.12 espie 456: int ret = ktr->ktr_retval;
457: int error = ktr->ktr_error;
458: int code = ktr->ktr_code;
1.1 deraadt 459:
460: if (code >= current->nsysnames || code < 0)
461: (void)printf("[%d] ", code);
462: else
463: (void)printf("%s ", current->sysnames[code]);
464:
465: if (error == 0) {
466: if (fancy) {
467: (void)printf("%d", ret);
468: if (ret < 0 || ret > 9)
469: (void)printf("/%#x", ret);
470: } else {
471: if (decimal)
472: (void)printf("%d", ret);
473: else
474: (void)printf("%#x", ret);
475: }
476: } else if (error == ERESTART)
477: (void)printf("RESTART");
478: else if (error == EJUSTRETURN)
479: (void)printf("JUSTRETURN");
480: else {
481: (void)printf("-1 errno %d", ktr->ktr_error);
482: if (fancy)
483: (void)printf(" %s", strerror(ktr->ktr_error));
484: }
485: (void)putchar('\n');
486: }
487:
1.12 espie 488: static void
1.17 deraadt 489: ktrnamei(const char *cp, int len)
1.1 deraadt 490: {
491: (void)printf("\"%.*s\"\n", len, cp);
492: }
493:
1.12 espie 494: static void
1.17 deraadt 495: ktremul(char *cp, int len)
1.1 deraadt 496: {
497: char name[1024];
498:
499: if (len >= sizeof(name))
500: errx(1, "Emulation name too long");
501:
502: strncpy(name, cp, len);
503: name[len] = '\0';
504: (void)printf("\"%s\"\n", name);
505:
506: setemul(name);
507: }
508:
1.12 espie 509: static void
1.17 deraadt 510: ktrgenio(struct ktr_genio *ktr, int len)
1.1 deraadt 511: {
1.17 deraadt 512: char *dp = (char *)ktr + sizeof (struct ktr_genio);
1.12 espie 513: int datalen = len - sizeof (struct ktr_genio);
514: static int screenwidth = 0;
1.17 deraadt 515: int col = 0, width;
516: char visbuf[5], *cp;
1.1 deraadt 517:
518: if (screenwidth == 0) {
519: struct winsize ws;
520:
521: if (fancy && ioctl(fileno(stderr), TIOCGWINSZ, &ws) != -1 &&
522: ws.ws_col > 8)
523: screenwidth = ws.ws_col;
524: else
525: screenwidth = 80;
526: }
527: printf("fd %d %s %d bytes\n", ktr->ktr_fd,
528: ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen);
529: if (maxdata && datalen > maxdata)
530: datalen = maxdata;
531: (void)printf(" \"");
532: col = 8;
533: for (; datalen > 0; datalen--, dp++) {
534: (void) vis(visbuf, *dp, VIS_CSTYLE, *(dp+1));
535: cp = visbuf;
1.17 deraadt 536:
1.1 deraadt 537: /*
538: * Keep track of printables and
539: * space chars (like fold(1)).
540: */
541: if (col == 0) {
542: (void)putchar('\t');
543: col = 8;
544: }
1.17 deraadt 545: switch (*cp) {
1.1 deraadt 546: case '\n':
547: col = 0;
548: (void)putchar('\n');
549: continue;
550: case '\t':
551: width = 8 - (col&07);
552: break;
553: default:
554: width = strlen(cp);
555: }
556: if (col + width > (screenwidth-2)) {
557: (void)printf("\\\n\t");
558: col = 8;
559: }
560: col += width;
561: do {
562: (void)putchar(*cp++);
563: } while (*cp);
564: }
565: if (col == 0)
566: (void)printf(" ");
567: (void)printf("\"\n");
568: }
569:
1.12 espie 570: static void
1.17 deraadt 571: ktrpsig(struct ktr_psig *psig)
1.1 deraadt 572: {
573: (void)printf("SIG%s ", sys_signame[psig->signo]);
574: if (psig->action == SIG_DFL)
1.14 deraadt 575: (void)printf("SIG_DFL code %d", psig->code);
1.1 deraadt 576: else
1.14 deraadt 577: (void)printf("caught handler=0x%lx mask=0x%x",
578: (u_long)psig->action, psig->mask);
579: switch (psig->signo) {
580: case SIGSEGV:
581: case SIGILL:
582: case SIGBUS:
583: case SIGFPE:
584: printf(" addr=%p trapno=%d", psig->si.si_addr,
585: psig->si.si_trapno);
586: break;
587: default:
588: break;
589: }
590: printf("\n");
1.1 deraadt 591: }
592:
1.12 espie 593: static void
1.17 deraadt 594: ktrcsw(struct ktr_csw *cs)
1.1 deraadt 595: {
596: (void)printf("%s %s\n", cs->out ? "stop" : "resume",
597: cs->user ? "user" : "kernel");
598: }
599:
1.12 espie 600: static void
1.17 deraadt 601: usage(void)
1.1 deraadt 602: {
603:
1.19 mickey 604: extern char *__progname;
605: fprintf(stderr, "usage: %s "
606: "[-dnlRT] [-e emulation] [-p pid] [-f trfile] [-m maxdata] "
1.20 deraadt 607: "[-t [ceinsw]]\n", __progname);
1.1 deraadt 608: exit(1);
609: }
610:
1.12 espie 611: static void
1.17 deraadt 612: setemul(const char *name)
1.1 deraadt 613: {
614: int i;
1.17 deraadt 615:
1.1 deraadt 616: for (i = 0; emulations[i].name != NULL; i++)
617: if (strcmp(emulations[i].name, name) == 0) {
618: current = &emulations[i];
619: return;
620: }
621: warnx("Emulation `%s' unknown", name);
622: }