]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/gcore.tproj/main.c
Merge branch 'apple'
[apple_cmds.git] / system_cmds / gcore.tproj / main.c
1 /*
2 * Copyright (c) 2016 Apple Inc. All rights reserved.
3 */
4
5 typedef char *kobject_description_t[512];
6 #include "options.h"
7 #include "utils.h"
8 #include "corefile.h"
9 #include "vanilla.h"
10 #include "sparse.h"
11 #include "convert.h"
12
13 #include <sys/types.h>
14 #include <sys/sysctl.h>
15 #include <sys/stat.h>
16 #include <sys/mman.h>
17 #include <libproc.h>
18
19 #include <sys/kauth.h>
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <strings.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <signal.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <fcntl.h>
32 #include <assert.h>
33 #include <libutil.h>
34
35 #include <mach/mach.h>
36
37 static char *
38 kern_corefile(void)
39 {
40 char *(^sysc_string)(const char *name) = ^(const char *name) {
41 char *p = NULL;
42 size_t len = 0;
43
44 if (-1 == sysctlbyname(name, NULL, &len, NULL, 0)) {
45 warnc(errno, "sysctl: %s", name);
46 } else if (0 != len) {
47 p = malloc(len);
48 if (-1 == sysctlbyname(name, p, &len, NULL, 0)) {
49 warnc(errno, "sysctl: %s", name);
50 free(p);
51 p = NULL;
52 }
53 }
54 return p;
55 };
56
57 char *s = sysc_string("kern.corefile");
58 if (NULL == s)
59 s = strdup("/cores/core.%P");
60 return s;
61 }
62
63 static const struct proc_bsdinfo *
64 get_bsdinfo(pid_t pid)
65 {
66 if (0 == pid)
67 return NULL;
68 struct proc_bsdinfo *pbi = calloc(1, sizeof (*pbi));
69 if (0 != proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, pbi, sizeof (*pbi)))
70 return pbi;
71 free(pbi);
72 return NULL;
73 }
74
75 static char *
76 format_gcore_name(const char *fmt, pid_t pid, uid_t uid, const char *nm)
77 {
78 __block size_t resid = MAXPATHLEN;
79 __block char *p = calloc(1, resid);
80 char *out = p;
81
82 int (^outchar)(int c) = ^(int c) {
83 if (resid > 1) {
84 *p++ = (char)c;
85 resid--;
86 return 1;
87 } else
88 return 0;
89 };
90
91 ptrdiff_t (^outstr)(const char *str) = ^(const char *str) {
92 const char *s = str;
93 while (*s && 0 != outchar(*s++))
94 ;
95 return s - str;
96 };
97
98 ptrdiff_t (^outint)(int value) = ^(int value) {
99 char id[11];
100 snprintf(id, sizeof (id), "%u", value);
101 return outstr(id);
102 };
103
104 ptrdiff_t (^outtstamp)(void) = ^(void) {
105 time_t now;
106 time(&now);
107 struct tm tm;
108 gmtime_r(&now, &tm);
109 char tstamp[50];
110 strftime(tstamp, sizeof (tstamp), "%Y%m%dT%H%M%SZ", &tm);
111 return outstr(tstamp);
112 };
113
114 int c;
115
116 for (int i = 0; resid > 0; i++)
117 switch (c = fmt[i]) {
118 default:
119 outchar(c);
120 break;
121 case '%':
122 i++;
123 switch (c = fmt[i]) {
124 case '%':
125 outchar(c);
126 break;
127 case 'P':
128 outint(pid);
129 break;
130 case 'U':
131 outint(uid);
132 break;
133 case 'N':
134 outstr(nm);
135 break;
136 case 'T':
137 outtstamp(); // ISO 8601 format
138 break;
139 default:
140 if (isprint(c))
141 err(EX_DATAERR, "unknown format char: %%%c", c);
142 else if (c != 0)
143 err(EX_DATAERR, "bad format char %%\\%03o", c);
144 else
145 err(EX_DATAERR, "bad format specifier");
146 }
147 break;
148 case 0:
149 outchar(c);
150 goto done;
151 }
152 done:
153 return out;
154 }
155
156 static char *
157 make_gcore_path(char **corefmtp, pid_t pid, uid_t uid, const char *nm)
158 {
159 char *corefmt = *corefmtp;
160 if (NULL == corefmt) {
161 const char defcore[] = "%N-%P-%T";
162 if (NULL == (corefmt = kern_corefile()))
163 corefmt = strdup(defcore);
164 else {
165 // use the same directory as kern.corefile
166 char *p = strrchr(corefmt, '/');
167 if (NULL != p) {
168 *p = '\0';
169 size_t len = strlen(corefmt) + strlen(defcore) + 2;
170 char *buf = malloc(len);
171 snprintf(buf, len, "%s/%s", corefmt, defcore);
172 free(corefmt);
173 corefmt = buf;
174 }
175 if (OPTIONS_DEBUG(opt, 3))
176 printf("corefmt '%s'\n", corefmt);
177 }
178 }
179 char *path = format_gcore_name(corefmt, pid, uid, nm);
180 free(corefmt);
181 *corefmtp = NULL;
182 return path;
183 }
184
185 static bool proc_same_data_model(const struct proc_bsdinfo *pbi) {
186 #if defined(__LP64__)
187 return (pbi->pbi_flags & PROC_FLAG_LP64) != 0;
188 #else
189 return (pbi->pbi_flags & PROC_FLAG_LP64) == 0;
190 #endif
191 }
192
193 static bool task_same_data_model(const task_flags_info_data_t *tfid) {
194 #if defined(__LP64__)
195 return (tfid->flags & TF_LP64) != 0;
196 #else
197 return (tfid->flags & TF_LP64) == 0;
198 #endif
199 }
200
201 /*
202 * Change credentials for writing out the file
203 */
204 static void
205 change_credentials(gid_t uid, uid_t gid)
206 {
207 if ((getgid() != gid && -1 == setgid(gid)) ||
208 (getuid() != uid && -1 == setuid(uid)))
209 errc(EX_NOPERM, errno, "insufficient privilege");
210 if (uid != getuid() || gid != getgid())
211 err(EX_OSERR, "wrong credentials");
212 }
213
214 static int
215 openout(const char *corefname, char **coretname, struct stat *st)
216 {
217 const int tfd = open(corefname, O_WRONLY);
218 if (-1 == tfd) {
219 if (ENOENT == errno) {
220 /*
221 * Arrange for a core file to appear "atomically": write the data
222 * to the file + ".tmp" suffix, then fchmod and rename it into
223 * place once the dump completes successfully.
224 */
225 const size_t nmlen = strlen(corefname) + 4 + 1;
226 char *tnm = malloc(nmlen);
227 snprintf(tnm, nmlen, "%s.tmp", corefname);
228 const int fd = open(tnm, O_WRONLY | O_CREAT | O_TRUNC, 0600);
229 if (-1 == fd || -1 == fstat(fd, st))
230 errc(EX_CANTCREAT, errno, "%s", tnm);
231 if (!S_ISREG(st->st_mode) || 1 != st->st_nlink)
232 errx(EX_CANTCREAT, "%s: invalid attributes", tnm);
233 *coretname = tnm;
234 return fd;
235 } else
236 errc(EX_CANTCREAT, errno, "%s", corefname);
237 } else if (-1 == fstat(tfd, st)) {
238 close(tfd);
239 errx(EX_CANTCREAT, "%s: fstat", corefname);
240 } else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
241 /*
242 * Write dump to a device, no rename!
243 */
244 *coretname = NULL;
245 return tfd;
246 } else {
247 close(tfd);
248 errc(EX_CANTCREAT, EEXIST, "%s", corefname);
249 }
250 }
251
252 static int
253 closeout(int fd, int ecode, char *corefname, char *coretname, const struct stat *st)
254 {
255 if (0 != ecode && !opt->preserve && S_ISREG(st->st_mode))
256 ftruncate(fd, 0); // limit large file clutter
257 if (0 == ecode && S_ISREG(st->st_mode))
258 fchmod(fd, 0400); // protect core files
259 if (-1 == close(fd)) {
260 warnc(errno, "%s: close", coretname ? coretname : corefname);
261 ecode = EX_OSERR;
262 }
263 if (NULL != coretname) {
264 if (0 == ecode && -1 == rename(coretname, corefname)) {
265 warnc(errno, "cannot rename %s to %s", coretname, corefname);
266 ecode = EX_NOPERM;
267 }
268 free(coretname);
269 }
270 if (corefname)
271 free(corefname);
272 return ecode;
273 }
274
275 const char *pgm;
276 const struct options *opt;
277
278 static const size_t oneK = 1024;
279 static const size_t oneM = oneK * oneK;
280
281 #define LARGEST_CHUNKSIZE INT32_MAX
282 #define DEFAULT_COMPRESSION_CHUNKSIZE (16 * oneM)
283 #define DEFAULT_NC_THRESHOLD (17 * oneK)
284
285 static struct options options = {
286 .corpsify = 0,
287 .suspend = 0,
288 .preserve = 0,
289 .verbose = 0,
290 #ifdef CONFIG_DEBUG
291 .debug = 0,
292 #endif
293 .extended = 0,
294 .sizebound = 0,
295 .chunksize = 0,
296 .calgorithm = COMPRESSION_LZFSE,
297 .ncthresh = DEFAULT_NC_THRESHOLD,
298 .dsymforuuid = 0,
299 };
300
301 static int
302 gcore_main(int argc, char *const *argv)
303 {
304 #define ZOPT_ALG (0)
305 #define ZOPT_CHSIZE (ZOPT_ALG + 1)
306
307 static char *const zoptkeys[] = {
308 [ZOPT_ALG] = "algorithm",
309 [ZOPT_CHSIZE] = "chunksize",
310 NULL
311 };
312
313 err_set_exit_b(^(int eval) {
314 if (EX_USAGE == eval) {
315 fprintf(stderr,
316 "usage:\t%s [-s] [-v] [[-o file] | [-c pathfmt ]] [-b size] "
317 #if DEBUG
318 #ifdef CONFIG_DEBUG
319 "[-d] "
320 #endif
321 "[-x] [-C] "
322 "[-Z compression-options] "
323 "[-t size] "
324 "[-F] "
325 #endif
326 "pid\n", pgm);
327 #if DEBUG
328 fprintf(stderr, "where compression-options:\n");
329 const char zvalfmt[] = "\t%s=%s\t\t%s\n";
330 fprintf(stderr, zvalfmt, zoptkeys[ZOPT_ALG], "alg",
331 "set compression algorithm");
332 fprintf(stderr, zvalfmt, zoptkeys[ZOPT_CHSIZE], "size",
333 "set compression chunksize, Mib");
334 #endif
335 }
336 });
337
338 char *corefmt = NULL;
339 char *corefname = NULL;
340
341 int c;
342 char *sopts, *value;
343
344 while ((c = getopt(argc, argv, "vdsxCFZ:o:c:b:t:")) != -1) {
345 switch (c) {
346
347 /*
348 * documented options
349 */
350 case 's': /* FreeBSD compat: stop while gathering */
351 options.suspend = 1;
352 break;
353 case 'o': /* Linux (& SunOS) compat: basic name */
354 corefname = strdup(optarg);
355 break;
356 case 'c': /* FreeBSD compat: basic name */
357 /* (also allows pattern-based naming) */
358 corefmt = strdup(optarg);
359 break;
360
361 case 'b': /* bound the size of the core file */
362 if (NULL != optarg) {
363 off_t bsize = atoi(optarg) * oneM;
364 if (bsize > 0)
365 options.sizebound = bsize;
366 else
367 errx(EX_USAGE, "invalid bound");
368 } else
369 errx(EX_USAGE, "no bound specified");
370 break;
371 case 'v': /* verbose output */
372 options.verbose++;
373 break;
374
375 /*
376 * dev and debugging help
377 */
378 #ifdef CONFIG_DEBUG
379 case 'd': /* debugging */
380 options.debug++;
381 options.verbose++;
382 options.preserve++;
383 break;
384 #endif
385 /*
386 * Remaining options are experimental and/or
387 * affect the content of the core file
388 */
389 case 'x': /* write extended format (small) core files */
390 options.extended++;
391 options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE;
392 break;
393 case 'C': /* forcibly corpsify rather than suspend */
394 options.corpsify++;
395 break;
396 case 'Z': /* control compression options */
397 /*
398 * Only LZFSE and LZ4 seem practical.
399 * (Default to LZ4 compression when the
400 * process is suspended, LZFSE when corpsed?)
401 */
402 if (0 == options.extended)
403 errx(EX_USAGE, "illegal flag combination");
404 sopts = optarg;
405 while (*sopts) {
406 size_t chsize;
407
408 switch (getsubopt(&sopts, zoptkeys, &value)) {
409 case ZOPT_ALG: /* change the algorithm */
410 if (NULL == value)
411 errx(EX_USAGE, "missing algorithm for "
412 "%s suboption",
413 zoptkeys[ZOPT_ALG]);
414 if (strcmp(value, "lz4") == 0)
415 options.calgorithm = COMPRESSION_LZ4;
416 else if (strcmp(value, "zlib") == 0)
417 options.calgorithm = COMPRESSION_ZLIB;
418 else if (strcmp(value, "lzma") == 0)
419 options.calgorithm = COMPRESSION_LZMA;
420 else if (strcmp(value, "lzfse") == 0)
421 options.calgorithm = COMPRESSION_LZFSE;
422 else
423 errx(EX_USAGE, "unknown algorithm '%s'"
424 " for %s suboption",
425 value, zoptkeys[ZOPT_ALG]);
426 break;
427 case ZOPT_CHSIZE: /* set the chunksize */
428 if (NULL == value)
429 errx(EX_USAGE, "no value specified for "
430 "%s suboption",
431 zoptkeys[ZOPT_CHSIZE]);
432 if ((chsize = atoi(value)) < 1)
433 errx(EX_USAGE, "chunksize %lu too small", chsize);
434 if (chsize > (LARGEST_CHUNKSIZE / oneM))
435 errx(EX_USAGE, "chunksize %lu too large", chsize);
436 options.chunksize = chsize * oneM;
437 break;
438 default:
439 if (suboptarg)
440 errx(EX_USAGE, "illegal suboption '%s'",
441 suboptarg);
442 else
443 errx(EX_USAGE, "missing suboption");
444 }
445 }
446 break;
447 case 't': /* set the F_NOCACHE threshold */
448 if (NULL != optarg) {
449 size_t tsize = atoi(optarg) * oneK;
450 if (tsize > 0)
451 options.ncthresh = tsize;
452 else
453 errx(EX_USAGE, "invalid nc threshold");
454 } else
455 errx(EX_USAGE, "no threshold specified");
456 break;
457 case 'F': /* maximize filerefs */
458 options.allfilerefs++;
459 break;
460 default:
461 errx(EX_USAGE, "unknown flag");
462 }
463 }
464
465 if (optind == argc)
466 errx(EX_USAGE, "no pid specified");
467 if (optind < argc-1)
468 errx(EX_USAGE, "too many arguments");
469
470 opt = &options;
471 if (NULL != corefname && NULL != corefmt)
472 errx(EX_USAGE, "specify only one of -o and -c");
473 if (!opt->extended && opt->allfilerefs)
474 errx(EX_USAGE, "unknown flag");
475
476 setpageshift();
477
478 if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host))
479 errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh);
480
481 const pid_t apid = atoi(argv[optind]);
482 pid_t pid = apid;
483 mach_port_t corpse = MACH_PORT_NULL;
484 kern_return_t ret;
485
486 if (0 == apid) {
487 /* look for corpse - dead or alive */
488 mach_port_array_t parray = NULL;
489 mach_msg_type_number_t pcount = 0;
490 ret = mach_ports_lookup(mach_task_self(), &parray, &pcount);
491 if (KERN_SUCCESS == ret && pcount > 0) {
492 task_t tcorpse = parray[0];
493 mig_deallocate((vm_address_t)parray, pcount * sizeof (*parray));
494 pid_t tpid = 0;
495 ret = pid_for_task(tcorpse, &tpid);
496 if (KERN_SUCCESS == ret && tpid != getpid()) {
497 corpse = tcorpse;
498 pid = tpid;
499 }
500 }
501 }
502
503 if (pid < 1 || getpid() == pid)
504 errx(EX_DATAERR, "invalid pid: %d", pid);
505
506 if (0 == apid && MACH_PORT_NULL == corpse)
507 errx(EX_DATAERR, "missing or bad corpse from parent");
508
509 task_t task = TASK_NULL;
510 const struct proc_bsdinfo *pbi = NULL;
511 const int rc = kill(pid, 0);
512
513 if (rc == 0) {
514 /* process or corpse that may respond to signals */
515 pbi = get_bsdinfo(pid);
516 }
517
518 if (rc == 0 && pbi != NULL) {
519 /* process or corpse that responds to signals */
520
521 /* make our data model match the data model of the target */
522 if (-1 == reexec_to_match_lp64ness(pbi->pbi_flags & PROC_FLAG_LP64))
523 errc(1, errno, "cannot match data model of %d", pid);
524
525 if (!proc_same_data_model(pbi))
526 errx(EX_OSERR, "cannot match data model of %d", pid);
527
528 if (pbi->pbi_ruid != pbi->pbi_svuid ||
529 pbi->pbi_rgid != pbi->pbi_svgid)
530 errx(EX_NOPERM, "pid %d - not dumping a set-id process", pid);
531
532 if (NULL == corefname)
533 corefname = make_gcore_path(&corefmt, pbi->pbi_pid, pbi->pbi_uid, pbi->pbi_name[0] ? pbi->pbi_name : pbi->pbi_comm);
534
535 if (MACH_PORT_NULL == corpse) {
536 ret = task_for_pid(mach_task_self(), pid, &task);
537 if (KERN_SUCCESS != ret) {
538 if (KERN_FAILURE == ret)
539 errx(EX_NOPERM, "insufficient privilege");
540 else
541 errx(EX_NOPERM, "task_for_pid: %s", mach_error_string(ret));
542 }
543 }
544
545 /*
546 * Have either the corpse port or the task port so adopt the
547 * credentials of the target process, *before* opening the
548 * core file, and analyzing the address space.
549 *
550 * If we are unable to match the target credentials, bail out.
551 */
552 change_credentials(pbi->pbi_uid, pbi->pbi_gid);
553 } else {
554 if (MACH_PORT_NULL == corpse) {
555 if (rc == 0) {
556 errx(EX_OSERR, "cannot get process info for %d", pid);
557 }
558 switch (errno) {
559 case ESRCH:
560 errc(EX_DATAERR, errno, "no process with pid %d", pid);
561 default:
562 errc(EX_DATAERR, errno, "pid %d", pid);
563 }
564 }
565 /* a corpse with no live process backing it */
566
567 assert(0 == apid && TASK_NULL == task);
568
569 task_flags_info_data_t tfid;
570 mach_msg_type_number_t count = TASK_FLAGS_INFO_COUNT;
571 ret = task_info(corpse, TASK_FLAGS_INFO, (task_info_t)&tfid, &count);
572 if (KERN_SUCCESS != ret)
573 err_mach(ret, NULL, "task_info");
574 if (!task_same_data_model(&tfid))
575 errx(EX_OSERR, "data model mismatch for target corpse");
576
577 if (opt->suspend || opt->corpsify)
578 errx(EX_USAGE, "cannot use -s or -C option with a corpse");
579 if (NULL != corefmt)
580 errx(EX_USAGE, "cannot use -c with a corpse");
581 if (NULL == corefname)
582 corefname = make_gcore_path(&corefmt, pid, -2, "corpse");
583
584 /*
585 * Only have a corpse, thus no process credentials.
586 * Switch to nobody.
587 */
588 change_credentials(-2, -2);
589 }
590
591 struct stat cst;
592 char *coretname = NULL;
593 const int fd = openout(corefname, &coretname, &cst);
594
595 if (opt->verbose) {
596 printf("Dumping core ");
597 if (OPTIONS_DEBUG(opt, 1)) {
598 printf("(%s", opt->extended ? "extended" : "vanilla");
599 if (0 != opt->sizebound) {
600 hsize_str_t hstr;
601 printf(", <= %s", str_hsize(hstr, opt->sizebound));
602 }
603 printf(") ");
604 }
605 printf("for pid %d to %s\n", pid, corefname);
606 }
607
608 int ecode;
609
610 if (MACH_PORT_NULL == corpse) {
611 assert(TASK_NULL != task);
612
613 /*
614 * The "traditional" way to capture a consistent core dump is to
615 * suspend the process while examining it and writing it out.
616 * Yet suspending a large process for a long time can have
617 * unpleasant side-effects. Alternatively dumping from the live
618 * process can lead to an inconsistent state in the core file.
619 *
620 * Instead we can ask xnu to create a 'corpse' - the process is transiently
621 * suspended while a COW snapshot of the address space is constructed
622 * in the kernel and dump from that. This vastly reduces the suspend
623 * time, but it is more resource hungry and thus may fail.
624 *
625 * The -s flag (opt->suspend) causes a task_suspend/task_resume
626 * The -C flag (opt->corpse) causes a corpse be taken, exiting if that fails.
627 * Both flags can be specified, in which case corpse errors are ignored.
628 *
629 * With no flags, we imitate traditional behavior though more
630 * efficiently: we try to take a corpse-based dump, in the event that
631 * fails, dump the live process.
632 */
633
634 int trycorpse = 1; /* default: use corpses */
635 int badcorpse_is_fatal = 1; /* default: failure to create is an error */
636
637 if (!opt->suspend && !opt->corpsify) {
638 /* try a corpse dump, else dump the live process */
639 badcorpse_is_fatal = 0;
640 } else if (opt->suspend) {
641 trycorpse = opt->corpsify;
642 /* if suspended anyway, ignore corpse-creation errors */
643 badcorpse_is_fatal = 0;
644 }
645
646 if (opt->suspend)
647 task_suspend(task);
648
649 if (trycorpse) {
650 /*
651 * Create a corpse from the image before dumping it
652 */
653 ret = task_generate_corpse(task, &corpse);
654 switch (ret) {
655 case KERN_SUCCESS:
656 if (OPTIONS_DEBUG(opt, 1))
657 printf("Corpse generated on port %x, task %x\n",
658 corpse, task);
659 ecode = coredump(corpse, fd, pbi);
660 mach_port_deallocate(mach_task_self(), corpse);
661 break;
662 default:
663 if (badcorpse_is_fatal || opt->verbose) {
664 warnx("failed to snapshot pid %d: %s\n",
665 pid, mach_error_string(ret));
666 if (badcorpse_is_fatal) {
667 ecode = KERN_RESOURCE_SHORTAGE == ret ? EX_TEMPFAIL : EX_OSERR;
668 goto out;
669 }
670 }
671 ecode = coredump(task, fd, pbi);
672 break;
673 }
674 } else {
675 /*
676 * Examine the task directly
677 */
678 ecode = coredump(task, fd, pbi);
679 }
680
681 out:
682 if (opt->suspend)
683 task_resume(task);
684 } else {
685 /*
686 * Handed a corpse by our parent.
687 */
688 ecode = coredump(corpse, fd, pbi);
689 mach_port_deallocate(mach_task_self(), corpse);
690 }
691
692 ecode = closeout(fd, ecode, corefname, coretname, &cst);
693 if (ecode)
694 errx(ecode, "failed to dump core for pid %d", pid);
695 return 0;
696 }
697
698 #if defined(CONFIG_GCORE_FREF) || defined(CONFIG_GCORE_MAP) || defined(GCONFIG_GCORE_CONV)
699
700 static int
701 getcorefd(const char *infile)
702 {
703 const int fd = open(infile, O_RDONLY | O_CLOEXEC);
704 if (-1 == fd)
705 errc(EX_DATAERR, errno, "cannot open %s", infile);
706
707 struct mach_header mh;
708 if (-1 == pread(fd, &mh, sizeof (mh), 0))
709 errc(EX_OSERR, errno, "cannot read mach header from %s", infile);
710
711 static const char cant_match_data_model[] = "cannot match the data model of %s";
712
713 if (-1 == reexec_to_match_lp64ness(MH_MAGIC_64 == mh.magic))
714 errc(1, errno, cant_match_data_model, infile);
715
716 if (NATIVE_MH_MAGIC != mh.magic)
717 errx(EX_OSERR, cant_match_data_model, infile);
718 if (MH_CORE != mh.filetype)
719 errx(EX_DATAERR, "%s is not a mach core file", infile);
720 return fd;
721 }
722
723 #endif
724
725 #ifdef CONFIG_GCORE_FREF
726
727 static int
728 gcore_fref_main(int argc, char *argv[])
729 {
730 err_set_exit_b(^(int eval) {
731 if (EX_USAGE == eval) {
732 fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]);
733 }
734 });
735 if (2 == argc)
736 errx(EX_USAGE, "no input corefile");
737 if (argc > 3)
738 errx(EX_USAGE, "too many arguments");
739 opt = &options;
740 return gcore_fref(getcorefd(argv[2]));
741 }
742
743 #endif /* CONFIG_GCORE_FREF */
744
745 #ifdef CONFIG_GCORE_MAP
746
747 static int
748 gcore_map_main(int argc, char *argv[])
749 {
750 err_set_exit_b(^(int eval) {
751 if (EX_USAGE == eval) {
752 fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]);
753 }
754 });
755 if (2 == argc)
756 errx(EX_USAGE, "no input corefile");
757 if (argc > 3)
758 errx(EX_USAGE, "too many arguments");
759 opt = &options;
760 return gcore_map(getcorefd(argv[2]));
761 }
762
763 #endif
764
765 #ifdef CONFIG_GCORE_CONV
766
767 static int
768 gcore_conv_main(int argc, char *argv[])
769 {
770 err_set_exit_b(^(int eval) {
771 if (EX_USAGE == eval)
772 fprintf(stderr,
773 "usage:\t%s %s [-v] [-L searchpath] [-z] [-s] incore outcore\n", pgm, argv[1]);
774 });
775
776 char *searchpath = NULL;
777 bool zf = false;
778
779 int c;
780 optind = 2;
781 while ((c = getopt(argc, argv, "dzvL:s")) != -1) {
782 switch (c) {
783 /*
784 * likely documented options
785 */
786 case 'L':
787 searchpath = strdup(optarg);
788 break;
789 case 'z':
790 zf = true;
791 break;
792 case 'v':
793 options.verbose++;
794 break;
795 case 's':
796 options.dsymforuuid++;
797 break;
798 /*
799 * dev and debugging help
800 */
801 #ifdef CONFIG_DEBUG
802 case 'd':
803 options.debug++;
804 options.verbose++;
805 options.preserve++;
806 break;
807 #endif
808 default:
809 errx(EX_USAGE, "unknown flag");
810 }
811 }
812 if (optind == argc)
813 errx(EX_USAGE, "no input corefile");
814 if (optind == argc - 1)
815 errx(EX_USAGE, "no output corefile");
816 if (optind < argc - 2)
817 errx(EX_USAGE, "too many arguments");
818
819 const char *incore = argv[optind];
820 char *corefname = strdup(argv[optind+1]);
821
822 opt = &options;
823
824 setpageshift();
825
826 if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host))
827 errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh);
828
829 const int infd = getcorefd(incore);
830 struct stat cst;
831 char *coretname = NULL;
832 const int fd = openout(corefname, &coretname, &cst);
833 int ecode = gcore_conv(infd, searchpath, zf, fd);
834 ecode = closeout(fd, ecode, corefname, coretname, &cst);
835 if (ecode)
836 errx(ecode, "failed to convert core file successfully");
837 return 0;
838 }
839 #endif
840
841 int
842 main(int argc, char *argv[])
843 {
844 if (NULL == (pgm = strrchr(*argv, '/')))
845 pgm = *argv;
846 else
847 pgm++;
848 #ifdef CONFIG_GCORE_FREF
849 if (argc > 1 && 0 == strcmp(argv[1], "fref")) {
850 return gcore_fref_main(argc, argv);
851 }
852 #endif
853 #ifdef CONFIG_GCORE_MAP
854 if (argc > 1 && 0 == strcmp(argv[1], "map")) {
855 return gcore_map_main(argc, argv);
856 }
857 #endif
858 #ifdef CONFIG_GCORE_CONV
859 if (argc > 1 && 0 == strcmp(argv[1], "conv")) {
860 return gcore_conv_main(argc, argv);
861 }
862 #endif
863 return gcore_main(argc, argv);
864 }