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