]> git.saurik.com Git - apple/system_cmds.git/blob - gcore.tproj/main.c
4c2c3aa67b171a5c4d9ccd61d6ce17e2db07f4cf
[apple/system_cmds.git] / gcore.tproj / main.c
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"
10 #include "convert.h"
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
36 static char *
37 kern_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
62 static const struct proc_bsdinfo *
63 get_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
74 static char *
75 format_gcore_name(const char *fmt, pid_t pid, uid_t uid, const char *nm)
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
97 ptrdiff_t (^outint)(int value) = ^(int value) {
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':
127 outint(pid);
128 break;
129 case 'U':
130 outint(uid);
131 break;
132 case 'N':
133 outstr(nm);
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 }
151 done:
152 return out;
153 }
154
155 static char *
156 make_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
184 static 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
192 static 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 */
203 static void
204 change_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
213 static int
214 openout(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
251 static int
252 closeout(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
274 const char *pgm;
275 const struct options *opt;
276
277 static const size_t oneK = 1024;
278 static 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)
283
284 static 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,
297 };
298
299 static int
300 gcore_main(int argc, char *const *argv)
301 {
302 #define ZOPT_ALG (0)
303 #define ZOPT_CHSIZE (ZOPT_ALG + 1)
304
305 static char *const zoptkeys[] = {
306 [ZOPT_ALG] = "algorithm",
307 [ZOPT_CHSIZE] = "chunksize",
308 NULL
309 };
310
311 err_set_exit_b(^(int eval) {
312 if (EX_USAGE == eval) {
313 fprintf(stderr,
314 "usage:\t%s [-s] [-v] [[-o file] | [-c pathfmt ]] [-b size] "
315 #if DEBUG
316 #ifdef CONFIG_DEBUG
317 "[-d] "
318 #endif
319 "[-x] [-C] "
320 "[-Z compression-options] "
321 "[-t size] "
322 "[-F] "
323 #endif
324 "pid\n", pgm);
325 #if DEBUG
326 fprintf(stderr, "where compression-options:\n");
327 const char zvalfmt[] = "\t%s=%s\t\t%s\n";
328 fprintf(stderr, zvalfmt, zoptkeys[ZOPT_ALG], "alg",
329 "set compression algorithm");
330 fprintf(stderr, zvalfmt, zoptkeys[ZOPT_CHSIZE], "size",
331 "set compression chunksize, Mib");
332 #endif
333 }
334 });
335
336 char *corefmt = NULL;
337 char *corefname = NULL;
338
339 int c;
340 char *sopts, *value;
341
342 while ((c = getopt(argc, argv, "vdsxCFZ:o:c:b:t:")) != -1) {
343 switch (c) {
344
345 /*
346 * documented options
347 */
348 case 's': /* FreeBSD compat: stop while gathering */
349 options.suspend = 1;
350 break;
351 case 'o': /* Linux (& SunOS) compat: basic name */
352 corefname = strdup(optarg);
353 break;
354 case 'c': /* FreeBSD compat: basic name */
355 /* (also allows pattern-based naming) */
356 corefmt = strdup(optarg);
357 break;
358
359 case 'b': /* bound the size of the core file */
360 if (NULL != optarg) {
361 off_t bsize = atoi(optarg) * oneM;
362 if (bsize > 0)
363 options.sizebound = bsize;
364 else
365 errx(EX_USAGE, "invalid bound");
366 } else
367 errx(EX_USAGE, "no bound specified");
368 break;
369 case 'v': /* verbose output */
370 options.verbose++;
371 break;
372
373 /*
374 * dev and debugging help
375 */
376 #ifdef CONFIG_DEBUG
377 case 'd': /* debugging */
378 options.debug++;
379 options.verbose++;
380 options.preserve++;
381 break;
382 #endif
383 /*
384 * Remaining options are experimental and/or
385 * affect the content of the core file
386 */
387 case 'x': /* write extended format (small) core files */
388 options.extended++;
389 options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE;
390 break;
391 case 'C': /* forcibly corpsify rather than suspend */
392 options.corpsify++;
393 break;
394 case 'Z': /* control compression options */
395 /*
396 * Only LZFSE and LZ4 seem practical.
397 * (Default to LZ4 compression when the
398 * process is suspended, LZFSE when corpsed?)
399 */
400 if (0 == options.extended)
401 errx(EX_USAGE, "illegal flag combination");
402 sopts = optarg;
403 while (*sopts) {
404 size_t chsize;
405
406 switch (getsubopt(&sopts, zoptkeys, &value)) {
407 case ZOPT_ALG: /* change the algorithm */
408 if (NULL == value)
409 errx(EX_USAGE, "missing algorithm for "
410 "%s suboption",
411 zoptkeys[ZOPT_ALG]);
412 if (strcmp(value, "lz4") == 0)
413 options.calgorithm = COMPRESSION_LZ4;
414 else if (strcmp(value, "zlib") == 0)
415 options.calgorithm = COMPRESSION_ZLIB;
416 else if (strcmp(value, "lzma") == 0)
417 options.calgorithm = COMPRESSION_LZMA;
418 else if (strcmp(value, "lzfse") == 0)
419 options.calgorithm = COMPRESSION_LZFSE;
420 else
421 errx(EX_USAGE, "unknown algorithm '%s'"
422 " for %s suboption",
423 value, zoptkeys[ZOPT_ALG]);
424 break;
425 case ZOPT_CHSIZE: /* set the chunksize */
426 if (NULL == value)
427 errx(EX_USAGE, "no value specified for "
428 "%s suboption",
429 zoptkeys[ZOPT_CHSIZE]);
430 if ((chsize = atoi(value)) < 1)
431 errx(EX_USAGE, "chunksize %lu too small", chsize);
432 if (chsize > (LARGEST_CHUNKSIZE / oneM))
433 errx(EX_USAGE, "chunksize %lu too large", chsize);
434 options.chunksize = chsize * oneM;
435 break;
436 default:
437 if (suboptarg)
438 errx(EX_USAGE, "illegal suboption '%s'",
439 suboptarg);
440 else
441 errx(EX_USAGE, "missing suboption");
442 }
443 }
444 break;
445 case 't': /* set the F_NOCACHE threshold */
446 if (NULL != optarg) {
447 size_t tsize = atoi(optarg) * oneK;
448 if (tsize > 0)
449 options.ncthresh = tsize;
450 else
451 errx(EX_USAGE, "invalid nc threshold");
452 } else
453 errx(EX_USAGE, "no threshold specified");
454 break;
455 case 'F': /* maximize filerefs */
456 options.allfilerefs++;
457 break;
458 default:
459 errx(EX_USAGE, "unknown flag");
460 }
461 }
462
463 if (optind == argc)
464 errx(EX_USAGE, "no pid specified");
465 if (optind < argc-1)
466 errx(EX_USAGE, "too many arguments");
467
468 opt = &options;
469 if (NULL != corefname && NULL != corefmt)
470 errx(EX_USAGE, "specify only one of -o and -c");
471 if (!opt->extended && opt->allfilerefs)
472 errx(EX_USAGE, "unknown flag");
473
474 setpageshift();
475
476 if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host))
477 errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh);
478
479 const pid_t apid = atoi(argv[optind]);
480 pid_t pid = apid;
481 mach_port_t corpse = MACH_PORT_NULL;
482 kern_return_t ret;
483
484 if (0 == apid) {
485 /* look for corpse - dead or alive */
486 mach_port_array_t parray = NULL;
487 mach_msg_type_number_t pcount = 0;
488 ret = mach_ports_lookup(mach_task_self(), &parray, &pcount);
489 if (KERN_SUCCESS == ret && pcount > 0) {
490 task_t tcorpse = parray[0];
491 mig_deallocate((vm_address_t)parray, pcount * sizeof (*parray));
492 pid_t tpid = 0;
493 ret = pid_for_task(tcorpse, &tpid);
494 if (KERN_SUCCESS == ret && tpid != getpid()) {
495 corpse = tcorpse;
496 pid = tpid;
497 }
498 }
499 }
500
501 if (pid < 1 || getpid() == pid)
502 errx(EX_DATAERR, "invalid pid: %d", pid);
503
504 if (0 == apid && MACH_PORT_NULL == corpse)
505 errx(EX_DATAERR, "missing or bad corpse from parent");
506
507 task_t task = TASK_NULL;
508 const struct proc_bsdinfo *pbi = NULL;
509
510 if (-1 != kill(pid, 0)) {
511 /* process or corpse that responds to signals */
512 pbi = get_bsdinfo(pid);
513 if (NULL == pbi)
514 errx(EX_OSERR, "cannot get process info for %d", pid);
515
516 /* make our data model match the data model of the target */
517 if (-1 == reexec_to_match_lp64ness(pbi->pbi_flags & PROC_FLAG_LP64))
518 errc(1, errno, "cannot match data model of %d", pid);
519
520 if (!proc_same_data_model(pbi))
521 errx(EX_OSERR, "cannot match data model of %d", pid);
522
523 if (pbi->pbi_ruid != pbi->pbi_svuid ||
524 pbi->pbi_rgid != pbi->pbi_svgid)
525 errx(EX_NOPERM, "pid %d - not dumping a set-id process", pid);
526
527 if (NULL == corefname)
528 corefname = make_gcore_path(&corefmt, pbi->pbi_pid, pbi->pbi_uid, pbi->pbi_name[0] ? pbi->pbi_name : pbi->pbi_comm);
529
530 if (MACH_PORT_NULL == corpse) {
531 ret = task_for_pid(mach_task_self(), pid, &task);
532 if (KERN_SUCCESS != ret) {
533 if (KERN_FAILURE == ret)
534 errx(EX_NOPERM, "insufficient privilege");
535 else
536 errx(EX_NOPERM, "task_for_pid: %s", mach_error_string(ret));
537 }
538 }
539
540 /*
541 * Have either the corpse port or the task port so adopt the
542 * credentials of the target process, *before* opening the
543 * core file, and analyzing the address space.
544 *
545 * If we are unable to match the target credentials, bail out.
546 */
547 change_credentials(pbi->pbi_uid, pbi->pbi_gid);
548 } else {
549 if (MACH_PORT_NULL == corpse) {
550 switch (errno) {
551 case ESRCH:
552 errc(EX_DATAERR, errno, "no process with pid %d", pid);
553 default:
554 errc(EX_DATAERR, errno, "pid %d", pid);
555 }
556 }
557 /* a corpse with no live process backing it */
558
559 assert(0 == apid && TASK_NULL == task);
560
561 task_flags_info_data_t tfid;
562 mach_msg_type_number_t count = TASK_FLAGS_INFO_COUNT;
563 ret = task_info(corpse, TASK_FLAGS_INFO, (task_info_t)&tfid, &count);
564 if (KERN_SUCCESS != ret)
565 err_mach(ret, NULL, "task_info");
566 if (!task_same_data_model(&tfid))
567 errx(EX_OSERR, "data model mismatch for target corpse");
568
569 if (opt->suspend || opt->corpsify)
570 errx(EX_USAGE, "cannot use -s or -C option with a corpse");
571 if (NULL != corefmt)
572 errx(EX_USAGE, "cannot use -c with a corpse");
573 if (NULL == corefname)
574 corefname = make_gcore_path(&corefmt, pid, -2, "corpse");
575
576 /*
577 * Only have a corpse, thus no process credentials.
578 * Switch to nobody.
579 */
580 change_credentials(-2, -2);
581 }
582
583 struct stat cst;
584 char *coretname = NULL;
585 const int fd = openout(corefname, &coretname, &cst);
586
587 if (opt->verbose) {
588 printf("Dumping core ");
589 if (OPTIONS_DEBUG(opt, 1)) {
590 printf("(%s", opt->extended ? "extended" : "vanilla");
591 if (0 != opt->sizebound) {
592 hsize_str_t hstr;
593 printf(", <= %s", str_hsize(hstr, opt->sizebound));
594 }
595 printf(") ");
596 }
597 printf("for pid %d to %s\n", pid, corefname);
598 }
599
600 int ecode;
601
602 if (MACH_PORT_NULL == corpse) {
603 assert(TASK_NULL != task);
604
605 /*
606 * The "traditional" way to capture a consistent core dump is to
607 * suspend the process while examining it and writing it out.
608 * Yet suspending a large process for a long time can have
609 * unpleasant side-effects. Alternatively dumping from the live
610 * process can lead to an inconsistent state in the core file.
611 *
612 * Instead we can ask xnu to create a 'corpse' - the process is transiently
613 * suspended while a COW snapshot of the address space is constructed
614 * in the kernel and dump from that. This vastly reduces the suspend
615 * time, but it is more resource hungry and thus may fail.
616 *
617 * The -s flag (opt->suspend) causes a task_suspend/task_resume
618 * The -C flag (opt->corpse) causes a corpse be taken, exiting if that fails.
619 * Both flags can be specified, in which case corpse errors are ignored.
620 *
621 * With no flags, we imitate traditional behavior though more
622 * efficiently: we try to take a corpse-based dump, in the event that
623 * fails, dump the live process.
624 */
625
626 int trycorpse = 1; /* default: use corpses */
627 int badcorpse_is_fatal = 1; /* default: failure to create is an error */
628
629 if (!opt->suspend && !opt->corpsify) {
630 /* try a corpse dump, else dump the live process */
631 badcorpse_is_fatal = 0;
632 } else if (opt->suspend) {
633 trycorpse = opt->corpsify;
634 /* if suspended anyway, ignore corpse-creation errors */
635 badcorpse_is_fatal = 0;
636 }
637
638 if (opt->suspend)
639 task_suspend(task);
640
641 if (trycorpse) {
642 /*
643 * Create a corpse from the image before dumping it
644 */
645 ret = task_generate_corpse(task, &corpse);
646 switch (ret) {
647 case KERN_SUCCESS:
648 if (OPTIONS_DEBUG(opt, 1))
649 printf("Corpse generated on port %x, task %x\n",
650 corpse, task);
651 ecode = coredump(corpse, fd, pbi);
652 mach_port_deallocate(mach_task_self(), corpse);
653 break;
654 default:
655 if (badcorpse_is_fatal || opt->verbose) {
656 warnx("failed to snapshot pid %d: %s\n",
657 pid, mach_error_string(ret));
658 if (badcorpse_is_fatal) {
659 ecode = KERN_RESOURCE_SHORTAGE == ret ? EX_TEMPFAIL : EX_OSERR;
660 goto out;
661 }
662 }
663 ecode = coredump(task, fd, pbi);
664 break;
665 }
666 } else {
667 /*
668 * Examine the task directly
669 */
670 ecode = coredump(task, fd, pbi);
671 }
672
673 out:
674 if (opt->suspend)
675 task_resume(task);
676 } else {
677 /*
678 * Handed a corpse by our parent.
679 */
680 ecode = coredump(corpse, fd, pbi);
681 mach_port_deallocate(mach_task_self(), corpse);
682 }
683
684 ecode = closeout(fd, ecode, corefname, coretname, &cst);
685 if (ecode)
686 errx(ecode, "failed to dump core for pid %d", pid);
687 return 0;
688 }
689
690 #if defined(CONFIG_GCORE_FREF) || defined(CONFIG_GCORE_MAP) || defined(GCONFIG_GCORE_CONV)
691
692 static int
693 getcorefd(const char *infile)
694 {
695 const int fd = open(infile, O_RDONLY | O_CLOEXEC);
696 if (-1 == fd)
697 errc(EX_DATAERR, errno, "cannot open %s", infile);
698
699 struct mach_header mh;
700 if (-1 == pread(fd, &mh, sizeof (mh), 0))
701 errc(EX_OSERR, errno, "cannot read mach header from %s", infile);
702
703 static const char cant_match_data_model[] = "cannot match the data model of %s";
704
705 if (-1 == reexec_to_match_lp64ness(MH_MAGIC_64 == mh.magic))
706 errc(1, errno, cant_match_data_model, infile);
707
708 if (NATIVE_MH_MAGIC != mh.magic)
709 errx(EX_OSERR, cant_match_data_model, infile);
710 if (MH_CORE != mh.filetype)
711 errx(EX_DATAERR, "%s is not a mach core file", infile);
712 return fd;
713 }
714
715 #endif
716
717 #ifdef CONFIG_GCORE_FREF
718
719 static int
720 gcore_fref_main(int argc, char *argv[])
721 {
722 err_set_exit_b(^(int eval) {
723 if (EX_USAGE == eval) {
724 fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]);
725 }
726 });
727 if (2 == argc)
728 errx(EX_USAGE, "no input corefile");
729 if (argc > 3)
730 errx(EX_USAGE, "too many arguments");
731 opt = &options;
732 return gcore_fref(getcorefd(argv[2]));
733 }
734
735 #endif /* CONFIG_GCORE_FREF */
736
737 #ifdef CONFIG_GCORE_MAP
738
739 static int
740 gcore_map_main(int argc, char *argv[])
741 {
742 err_set_exit_b(^(int eval) {
743 if (EX_USAGE == eval) {
744 fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]);
745 }
746 });
747 if (2 == argc)
748 errx(EX_USAGE, "no input corefile");
749 if (argc > 3)
750 errx(EX_USAGE, "too many arguments");
751 opt = &options;
752 return gcore_map(getcorefd(argv[2]));
753 }
754
755 #endif
756
757 #ifdef CONFIG_GCORE_CONV
758
759 static int
760 gcore_conv_main(int argc, char *argv[])
761 {
762 err_set_exit_b(^(int eval) {
763 if (EX_USAGE == eval)
764 fprintf(stderr,
765 "usage:\t%s %s [-v] [-L searchpath] [-z] incore outcore\n", pgm, argv[1]);
766 });
767
768 char *searchpath = NULL;
769 bool zf = false;
770
771 int c;
772 optind = 2;
773 while ((c = getopt(argc, argv, "dzvL:")) != -1) {
774 switch (c) {
775 /*
776 * likely documented options
777 */
778 case 'L':
779 searchpath = strdup(optarg);
780 break;
781 case 'z':
782 zf = true;
783 break;
784 case 'v':
785 options.verbose++;
786 break;
787
788 /*
789 * dev and debugging help
790 */
791 #ifdef CONFIG_DEBUG
792 case 'd':
793 options.debug++;
794 options.verbose++;
795 options.preserve++;
796 break;
797 #endif
798 default:
799 errx(EX_USAGE, "unknown flag");
800 }
801 }
802 if (optind == argc)
803 errx(EX_USAGE, "no input corefile");
804 if (optind == argc - 1)
805 errx(EX_USAGE, "no output corefile");
806 if (optind < argc - 2)
807 errx(EX_USAGE, "too many arguments");
808
809 const char *incore = argv[optind];
810 char *corefname = strdup(argv[optind+1]);
811
812 opt = &options;
813
814 setpageshift();
815
816 if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host))
817 errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh);
818
819 const int infd = getcorefd(incore);
820 struct stat cst;
821 char *coretname = NULL;
822 const int fd = openout(corefname, &coretname, &cst);
823 int ecode = gcore_conv(infd, searchpath, zf, fd);
824 ecode = closeout(fd, ecode, corefname, coretname, &cst);
825 if (ecode)
826 errx(ecode, "failed to convert core file successfully");
827 return 0;
828 }
829 #endif
830
831 int
832 main(int argc, char *argv[])
833 {
834 if (NULL == (pgm = strrchr(*argv, '/')))
835 pgm = *argv;
836 else
837 pgm++;
838 #ifdef CONFIG_GCORE_FREF
839 if (argc > 1 && 0 == strcmp(argv[1], "fref")) {
840 return gcore_fref_main(argc, argv);
841 }
842 #endif
843 #ifdef CONFIG_GCORE_MAP
844 if (argc > 1 && 0 == strcmp(argv[1], "map")) {
845 return gcore_map_main(argc, argv);
846 }
847 #endif
848 #ifdef CONFIG_GCORE_CONV
849 if (argc > 1 && 0 == strcmp(argv[1], "conv")) {
850 return gcore_conv_main(argc, argv);
851 }
852 #endif
853 return gcore_main(argc, argv);
854 }