diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /system_cmds/gcore.tproj/main.c | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'system_cmds/gcore.tproj/main.c')
-rw-r--r-- | system_cmds/gcore.tproj/main.c | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/system_cmds/gcore.tproj/main.c b/system_cmds/gcore.tproj/main.c new file mode 100644 index 0000000..abefa14 --- /dev/null +++ b/system_cmds/gcore.tproj/main.c @@ -0,0 +1,863 @@ +/* + * Copyright (c) 2016 Apple Inc. All rights reserved. + */ + +#include "options.h" +#include "utils.h" +#include "corefile.h" +#include "vanilla.h" +#include "sparse.h" +#include "convert.h" + +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <libproc.h> + +#include <sys/kauth.h> + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdarg.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <assert.h> +#include <libutil.h> + +#include <mach/mach.h> + +static char * +kern_corefile(void) +{ + char *(^sysc_string)(const char *name) = ^(const char *name) { + char *p = NULL; + size_t len = 0; + + if (-1 == sysctlbyname(name, NULL, &len, NULL, 0)) { + warnc(errno, "sysctl: %s", name); + } else if (0 != len) { + p = malloc(len); + if (-1 == sysctlbyname(name, p, &len, NULL, 0)) { + warnc(errno, "sysctl: %s", name); + free(p); + p = NULL; + } + } + return p; + }; + + char *s = sysc_string("kern.corefile"); + if (NULL == s) + s = strdup("/cores/core.%P"); + return s; +} + +static const struct proc_bsdinfo * +get_bsdinfo(pid_t pid) +{ + if (0 == pid) + return NULL; + struct proc_bsdinfo *pbi = calloc(1, sizeof (*pbi)); + if (0 != proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, pbi, sizeof (*pbi))) + return pbi; + free(pbi); + return NULL; +} + +static char * +format_gcore_name(const char *fmt, pid_t pid, uid_t uid, const char *nm) +{ + __block size_t resid = MAXPATHLEN; + __block char *p = calloc(1, resid); + char *out = p; + + int (^outchar)(int c) = ^(int c) { + if (resid > 1) { + *p++ = (char)c; + resid--; + return 1; + } else + return 0; + }; + + ptrdiff_t (^outstr)(const char *str) = ^(const char *str) { + const char *s = str; + while (*s && 0 != outchar(*s++)) + ; + return s - str; + }; + + ptrdiff_t (^outint)(int value) = ^(int value) { + char id[11]; + snprintf(id, sizeof (id), "%u", value); + return outstr(id); + }; + + ptrdiff_t (^outtstamp)(void) = ^(void) { + time_t now; + time(&now); + struct tm tm; + gmtime_r(&now, &tm); + char tstamp[50]; + strftime(tstamp, sizeof (tstamp), "%Y%m%dT%H%M%SZ", &tm); + return outstr(tstamp); + }; + + int c; + + for (int i = 0; resid > 0; i++) + switch (c = fmt[i]) { + default: + outchar(c); + break; + case '%': + i++; + switch (c = fmt[i]) { + case '%': + outchar(c); + break; + case 'P': + outint(pid); + break; + case 'U': + outint(uid); + break; + case 'N': + outstr(nm); + break; + case 'T': + outtstamp(); // ISO 8601 format + break; + default: + if (isprint(c)) + err(EX_DATAERR, "unknown format char: %%%c", c); + else if (c != 0) + err(EX_DATAERR, "bad format char %%\\%03o", c); + else + err(EX_DATAERR, "bad format specifier"); + } + break; + case 0: + outchar(c); + goto done; + } +done: + return out; +} + +static char * +make_gcore_path(char **corefmtp, pid_t pid, uid_t uid, const char *nm) +{ + char *corefmt = *corefmtp; + if (NULL == corefmt) { + const char defcore[] = "%N-%P-%T"; + if (NULL == (corefmt = kern_corefile())) + corefmt = strdup(defcore); + else { + // use the same directory as kern.corefile + char *p = strrchr(corefmt, '/'); + if (NULL != p) { + *p = '\0'; + size_t len = strlen(corefmt) + strlen(defcore) + 2; + char *buf = malloc(len); + snprintf(buf, len, "%s/%s", corefmt, defcore); + free(corefmt); + corefmt = buf; + } + if (OPTIONS_DEBUG(opt, 3)) + printf("corefmt '%s'\n", corefmt); + } + } + char *path = format_gcore_name(corefmt, pid, uid, nm); + free(corefmt); + *corefmtp = NULL; + return path; +} + +static bool proc_same_data_model(const struct proc_bsdinfo *pbi) { +#if defined(__LP64__) + return (pbi->pbi_flags & PROC_FLAG_LP64) != 0; +#else + return (pbi->pbi_flags & PROC_FLAG_LP64) == 0; +#endif +} + +static bool task_same_data_model(const task_flags_info_data_t *tfid) { +#if defined(__LP64__) + return (tfid->flags & TF_LP64) != 0; +#else + return (tfid->flags & TF_LP64) == 0; +#endif +} + +/* + * Change credentials for writing out the file + */ +static void +change_credentials(gid_t uid, uid_t gid) +{ + if ((getgid() != gid && -1 == setgid(gid)) || + (getuid() != uid && -1 == setuid(uid))) + errc(EX_NOPERM, errno, "insufficient privilege"); + if (uid != getuid() || gid != getgid()) + err(EX_OSERR, "wrong credentials"); +} + +static int +openout(const char *corefname, char **coretname, struct stat *st) +{ + const int tfd = open(corefname, O_WRONLY); + if (-1 == tfd) { + if (ENOENT == errno) { + /* + * Arrange for a core file to appear "atomically": write the data + * to the file + ".tmp" suffix, then fchmod and rename it into + * place once the dump completes successfully. + */ + const size_t nmlen = strlen(corefname) + 4 + 1; + char *tnm = malloc(nmlen); + snprintf(tnm, nmlen, "%s.tmp", corefname); + const int fd = open(tnm, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (-1 == fd || -1 == fstat(fd, st)) + errc(EX_CANTCREAT, errno, "%s", tnm); + if (!S_ISREG(st->st_mode) || 1 != st->st_nlink) + errx(EX_CANTCREAT, "%s: invalid attributes", tnm); + *coretname = tnm; + return fd; + } else + errc(EX_CANTCREAT, errno, "%s", corefname); + } else if (-1 == fstat(tfd, st)) { + close(tfd); + errx(EX_CANTCREAT, "%s: fstat", corefname); + } else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { + /* + * Write dump to a device, no rename! + */ + *coretname = NULL; + return tfd; + } else { + close(tfd); + errc(EX_CANTCREAT, EEXIST, "%s", corefname); + } +} + +static int +closeout(int fd, int ecode, char *corefname, char *coretname, const struct stat *st) +{ + if (0 != ecode && !opt->preserve && S_ISREG(st->st_mode)) + ftruncate(fd, 0); // limit large file clutter + if (0 == ecode && S_ISREG(st->st_mode)) + fchmod(fd, 0400); // protect core files + if (-1 == close(fd)) { + warnc(errno, "%s: close", coretname ? coretname : corefname); + ecode = EX_OSERR; + } + if (NULL != coretname) { + if (0 == ecode && -1 == rename(coretname, corefname)) { + warnc(errno, "cannot rename %s to %s", coretname, corefname); + ecode = EX_NOPERM; + } + free(coretname); + } + if (corefname) + free(corefname); + return ecode; +} + +const char *pgm; +const struct options *opt; + +static const size_t oneK = 1024; +static const size_t oneM = oneK * oneK; + +#define LARGEST_CHUNKSIZE INT32_MAX +#define DEFAULT_COMPRESSION_CHUNKSIZE (16 * oneM) +#define DEFAULT_NC_THRESHOLD (17 * oneK) + +static struct options options = { + .corpsify = 0, + .suspend = 0, + .preserve = 0, + .verbose = 0, +#ifdef CONFIG_DEBUG + .debug = 0, +#endif + .extended = 0, + .sizebound = 0, + .chunksize = 0, + .calgorithm = COMPRESSION_LZFSE, + .ncthresh = DEFAULT_NC_THRESHOLD, + .dsymforuuid = 0, +}; + +static int +gcore_main(int argc, char *const *argv) +{ +#define ZOPT_ALG (0) +#define ZOPT_CHSIZE (ZOPT_ALG + 1) + + static char *const zoptkeys[] = { + [ZOPT_ALG] = "algorithm", + [ZOPT_CHSIZE] = "chunksize", + NULL + }; + + err_set_exit_b(^(int eval) { + if (EX_USAGE == eval) { + fprintf(stderr, + "usage:\t%s [-s] [-v] [[-o file] | [-c pathfmt ]] [-b size] " +#if DEBUG +#ifdef CONFIG_DEBUG + "[-d] " +#endif + "[-x] [-C] " + "[-Z compression-options] " + "[-t size] " + "[-F] " +#endif + "pid\n", pgm); +#if DEBUG + fprintf(stderr, "where compression-options:\n"); + const char zvalfmt[] = "\t%s=%s\t\t%s\n"; + fprintf(stderr, zvalfmt, zoptkeys[ZOPT_ALG], "alg", + "set compression algorithm"); + fprintf(stderr, zvalfmt, zoptkeys[ZOPT_CHSIZE], "size", + "set compression chunksize, Mib"); +#endif + } + }); + + char *corefmt = NULL; + char *corefname = NULL; + + int c; + char *sopts, *value; + + while ((c = getopt(argc, argv, "vdsxCFZ:o:c:b:t:")) != -1) { + switch (c) { + + /* + * documented options + */ + case 's': /* FreeBSD compat: stop while gathering */ + options.suspend = 1; + break; + case 'o': /* Linux (& SunOS) compat: basic name */ + corefname = strdup(optarg); + break; + case 'c': /* FreeBSD compat: basic name */ + /* (also allows pattern-based naming) */ + corefmt = strdup(optarg); + break; + + case 'b': /* bound the size of the core file */ + if (NULL != optarg) { + off_t bsize = atoi(optarg) * oneM; + if (bsize > 0) + options.sizebound = bsize; + else + errx(EX_USAGE, "invalid bound"); + } else + errx(EX_USAGE, "no bound specified"); + break; + case 'v': /* verbose output */ + options.verbose++; + break; + + /* + * dev and debugging help + */ +#ifdef CONFIG_DEBUG + case 'd': /* debugging */ + options.debug++; + options.verbose++; + options.preserve++; + break; +#endif + /* + * Remaining options are experimental and/or + * affect the content of the core file + */ + case 'x': /* write extended format (small) core files */ + options.extended++; + options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE; + break; + case 'C': /* forcibly corpsify rather than suspend */ + options.corpsify++; + break; + case 'Z': /* control compression options */ + /* + * Only LZFSE and LZ4 seem practical. + * (Default to LZ4 compression when the + * process is suspended, LZFSE when corpsed?) + */ + if (0 == options.extended) + errx(EX_USAGE, "illegal flag combination"); + sopts = optarg; + while (*sopts) { + size_t chsize; + + switch (getsubopt(&sopts, zoptkeys, &value)) { + case ZOPT_ALG: /* change the algorithm */ + if (NULL == value) + errx(EX_USAGE, "missing algorithm for " + "%s suboption", + zoptkeys[ZOPT_ALG]); + if (strcmp(value, "lz4") == 0) + options.calgorithm = COMPRESSION_LZ4; + else if (strcmp(value, "zlib") == 0) + options.calgorithm = COMPRESSION_ZLIB; + else if (strcmp(value, "lzma") == 0) + options.calgorithm = COMPRESSION_LZMA; + else if (strcmp(value, "lzfse") == 0) + options.calgorithm = COMPRESSION_LZFSE; + else + errx(EX_USAGE, "unknown algorithm '%s'" + " for %s suboption", + value, zoptkeys[ZOPT_ALG]); + break; + case ZOPT_CHSIZE: /* set the chunksize */ + if (NULL == value) + errx(EX_USAGE, "no value specified for " + "%s suboption", + zoptkeys[ZOPT_CHSIZE]); + if ((chsize = atoi(value)) < 1) + errx(EX_USAGE, "chunksize %lu too small", chsize); + if (chsize > (LARGEST_CHUNKSIZE / oneM)) + errx(EX_USAGE, "chunksize %lu too large", chsize); + options.chunksize = chsize * oneM; + break; + default: + if (suboptarg) + errx(EX_USAGE, "illegal suboption '%s'", + suboptarg); + else + errx(EX_USAGE, "missing suboption"); + } + } + break; + case 't': /* set the F_NOCACHE threshold */ + if (NULL != optarg) { + size_t tsize = atoi(optarg) * oneK; + if (tsize > 0) + options.ncthresh = tsize; + else + errx(EX_USAGE, "invalid nc threshold"); + } else + errx(EX_USAGE, "no threshold specified"); + break; + case 'F': /* maximize filerefs */ + options.allfilerefs++; + break; + default: + errx(EX_USAGE, "unknown flag"); + } + } + + if (optind == argc) + errx(EX_USAGE, "no pid specified"); + if (optind < argc-1) + errx(EX_USAGE, "too many arguments"); + + opt = &options; + if (NULL != corefname && NULL != corefmt) + errx(EX_USAGE, "specify only one of -o and -c"); + if (!opt->extended && opt->allfilerefs) + errx(EX_USAGE, "unknown flag"); + + setpageshift(); + + if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host)) + errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh); + + const pid_t apid = atoi(argv[optind]); + pid_t pid = apid; + mach_port_t corpse = MACH_PORT_NULL; + kern_return_t ret; + + if (0 == apid) { + /* look for corpse - dead or alive */ + mach_port_array_t parray = NULL; + mach_msg_type_number_t pcount = 0; + ret = mach_ports_lookup(mach_task_self(), &parray, &pcount); + if (KERN_SUCCESS == ret && pcount > 0) { + task_t tcorpse = parray[0]; + mig_deallocate((vm_address_t)parray, pcount * sizeof (*parray)); + pid_t tpid = 0; + ret = pid_for_task(tcorpse, &tpid); + if (KERN_SUCCESS == ret && tpid != getpid()) { + corpse = tcorpse; + pid = tpid; + } + } + } + + if (pid < 1 || getpid() == pid) + errx(EX_DATAERR, "invalid pid: %d", pid); + + if (0 == apid && MACH_PORT_NULL == corpse) + errx(EX_DATAERR, "missing or bad corpse from parent"); + + task_t task = TASK_NULL; + const struct proc_bsdinfo *pbi = NULL; + const int rc = kill(pid, 0); + + if (rc == 0) { + /* process or corpse that may respond to signals */ + pbi = get_bsdinfo(pid); + } + + if (rc == 0 && pbi != NULL) { + /* process or corpse that responds to signals */ + + /* make our data model match the data model of the target */ + if (-1 == reexec_to_match_lp64ness(pbi->pbi_flags & PROC_FLAG_LP64)) + errc(1, errno, "cannot match data model of %d", pid); + + if (!proc_same_data_model(pbi)) + errx(EX_OSERR, "cannot match data model of %d", pid); + + if (pbi->pbi_ruid != pbi->pbi_svuid || + pbi->pbi_rgid != pbi->pbi_svgid) + errx(EX_NOPERM, "pid %d - not dumping a set-id process", pid); + + if (NULL == corefname) + corefname = make_gcore_path(&corefmt, pbi->pbi_pid, pbi->pbi_uid, pbi->pbi_name[0] ? pbi->pbi_name : pbi->pbi_comm); + + if (MACH_PORT_NULL == corpse) { + ret = task_for_pid(mach_task_self(), pid, &task); + if (KERN_SUCCESS != ret) { + if (KERN_FAILURE == ret) + errx(EX_NOPERM, "insufficient privilege"); + else + errx(EX_NOPERM, "task_for_pid: %s", mach_error_string(ret)); + } + } + + /* + * Have either the corpse port or the task port so adopt the + * credentials of the target process, *before* opening the + * core file, and analyzing the address space. + * + * If we are unable to match the target credentials, bail out. + */ + change_credentials(pbi->pbi_uid, pbi->pbi_gid); + } else { + if (MACH_PORT_NULL == corpse) { + if (rc == 0) { + errx(EX_OSERR, "cannot get process info for %d", pid); + } + switch (errno) { + case ESRCH: + errc(EX_DATAERR, errno, "no process with pid %d", pid); + default: + errc(EX_DATAERR, errno, "pid %d", pid); + } + } + /* a corpse with no live process backing it */ + + assert(0 == apid && TASK_NULL == task); + + task_flags_info_data_t tfid; + mach_msg_type_number_t count = TASK_FLAGS_INFO_COUNT; + ret = task_info(corpse, TASK_FLAGS_INFO, (task_info_t)&tfid, &count); + if (KERN_SUCCESS != ret) + err_mach(ret, NULL, "task_info"); + if (!task_same_data_model(&tfid)) + errx(EX_OSERR, "data model mismatch for target corpse"); + + if (opt->suspend || opt->corpsify) + errx(EX_USAGE, "cannot use -s or -C option with a corpse"); + if (NULL != corefmt) + errx(EX_USAGE, "cannot use -c with a corpse"); + if (NULL == corefname) + corefname = make_gcore_path(&corefmt, pid, -2, "corpse"); + + /* + * Only have a corpse, thus no process credentials. + * Switch to nobody. + */ + change_credentials(-2, -2); + } + + struct stat cst; + char *coretname = NULL; + const int fd = openout(corefname, &coretname, &cst); + + if (opt->verbose) { + printf("Dumping core "); + if (OPTIONS_DEBUG(opt, 1)) { + printf("(%s", opt->extended ? "extended" : "vanilla"); + if (0 != opt->sizebound) { + hsize_str_t hstr; + printf(", <= %s", str_hsize(hstr, opt->sizebound)); + } + printf(") "); + } + printf("for pid %d to %s\n", pid, corefname); + } + + int ecode; + + if (MACH_PORT_NULL == corpse) { + assert(TASK_NULL != task); + + /* + * The "traditional" way to capture a consistent core dump is to + * suspend the process while examining it and writing it out. + * Yet suspending a large process for a long time can have + * unpleasant side-effects. Alternatively dumping from the live + * process can lead to an inconsistent state in the core file. + * + * Instead we can ask xnu to create a 'corpse' - the process is transiently + * suspended while a COW snapshot of the address space is constructed + * in the kernel and dump from that. This vastly reduces the suspend + * time, but it is more resource hungry and thus may fail. + * + * The -s flag (opt->suspend) causes a task_suspend/task_resume + * The -C flag (opt->corpse) causes a corpse be taken, exiting if that fails. + * Both flags can be specified, in which case corpse errors are ignored. + * + * With no flags, we imitate traditional behavior though more + * efficiently: we try to take a corpse-based dump, in the event that + * fails, dump the live process. + */ + + int trycorpse = 1; /* default: use corpses */ + int badcorpse_is_fatal = 1; /* default: failure to create is an error */ + + if (!opt->suspend && !opt->corpsify) { + /* try a corpse dump, else dump the live process */ + badcorpse_is_fatal = 0; + } else if (opt->suspend) { + trycorpse = opt->corpsify; + /* if suspended anyway, ignore corpse-creation errors */ + badcorpse_is_fatal = 0; + } + + if (opt->suspend) + task_suspend(task); + + if (trycorpse) { + /* + * Create a corpse from the image before dumping it + */ + ret = task_generate_corpse(task, &corpse); + switch (ret) { + case KERN_SUCCESS: + if (OPTIONS_DEBUG(opt, 1)) + printf("Corpse generated on port %x, task %x\n", + corpse, task); + ecode = coredump(corpse, fd, pbi); + mach_port_deallocate(mach_task_self(), corpse); + break; + default: + if (badcorpse_is_fatal || opt->verbose) { + warnx("failed to snapshot pid %d: %s\n", + pid, mach_error_string(ret)); + if (badcorpse_is_fatal) { + ecode = KERN_RESOURCE_SHORTAGE == ret ? EX_TEMPFAIL : EX_OSERR; + goto out; + } + } + ecode = coredump(task, fd, pbi); + break; + } + } else { + /* + * Examine the task directly + */ + ecode = coredump(task, fd, pbi); + } + + out: + if (opt->suspend) + task_resume(task); + } else { + /* + * Handed a corpse by our parent. + */ + ecode = coredump(corpse, fd, pbi); + mach_port_deallocate(mach_task_self(), corpse); + } + + ecode = closeout(fd, ecode, corefname, coretname, &cst); + if (ecode) + errx(ecode, "failed to dump core for pid %d", pid); + return 0; +} + +#if defined(CONFIG_GCORE_FREF) || defined(CONFIG_GCORE_MAP) || defined(GCONFIG_GCORE_CONV) + +static int +getcorefd(const char *infile) +{ + const int fd = open(infile, O_RDONLY | O_CLOEXEC); + if (-1 == fd) + errc(EX_DATAERR, errno, "cannot open %s", infile); + + struct mach_header mh; + if (-1 == pread(fd, &mh, sizeof (mh), 0)) + errc(EX_OSERR, errno, "cannot read mach header from %s", infile); + + static const char cant_match_data_model[] = "cannot match the data model of %s"; + + if (-1 == reexec_to_match_lp64ness(MH_MAGIC_64 == mh.magic)) + errc(1, errno, cant_match_data_model, infile); + + if (NATIVE_MH_MAGIC != mh.magic) + errx(EX_OSERR, cant_match_data_model, infile); + if (MH_CORE != mh.filetype) + errx(EX_DATAERR, "%s is not a mach core file", infile); + return fd; +} + +#endif + +#ifdef CONFIG_GCORE_FREF + +static int +gcore_fref_main(int argc, char *argv[]) +{ + err_set_exit_b(^(int eval) { + if (EX_USAGE == eval) { + fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]); + } + }); + if (2 == argc) + errx(EX_USAGE, "no input corefile"); + if (argc > 3) + errx(EX_USAGE, "too many arguments"); + opt = &options; + return gcore_fref(getcorefd(argv[2])); +} + +#endif /* CONFIG_GCORE_FREF */ + +#ifdef CONFIG_GCORE_MAP + +static int +gcore_map_main(int argc, char *argv[]) +{ + err_set_exit_b(^(int eval) { + if (EX_USAGE == eval) { + fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]); + } + }); + if (2 == argc) + errx(EX_USAGE, "no input corefile"); + if (argc > 3) + errx(EX_USAGE, "too many arguments"); + opt = &options; + return gcore_map(getcorefd(argv[2])); +} + +#endif + +#ifdef CONFIG_GCORE_CONV + +static int +gcore_conv_main(int argc, char *argv[]) +{ + err_set_exit_b(^(int eval) { + if (EX_USAGE == eval) + fprintf(stderr, + "usage:\t%s %s [-v] [-L searchpath] [-z] [-s] incore outcore\n", pgm, argv[1]); + }); + + char *searchpath = NULL; + bool zf = false; + + int c; + optind = 2; + while ((c = getopt(argc, argv, "dzvL:s")) != -1) { + switch (c) { + /* + * likely documented options + */ + case 'L': + searchpath = strdup(optarg); + break; + case 'z': + zf = true; + break; + case 'v': + options.verbose++; + break; + case 's': + options.dsymforuuid++; + break; + /* + * dev and debugging help + */ +#ifdef CONFIG_DEBUG + case 'd': + options.debug++; + options.verbose++; + options.preserve++; + break; +#endif + default: + errx(EX_USAGE, "unknown flag"); + } + } + if (optind == argc) + errx(EX_USAGE, "no input corefile"); + if (optind == argc - 1) + errx(EX_USAGE, "no output corefile"); + if (optind < argc - 2) + errx(EX_USAGE, "too many arguments"); + + const char *incore = argv[optind]; + char *corefname = strdup(argv[optind+1]); + + opt = &options; + + setpageshift(); + + if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host)) + errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh); + + const int infd = getcorefd(incore); + struct stat cst; + char *coretname = NULL; + const int fd = openout(corefname, &coretname, &cst); + int ecode = gcore_conv(infd, searchpath, zf, fd); + ecode = closeout(fd, ecode, corefname, coretname, &cst); + if (ecode) + errx(ecode, "failed to convert core file successfully"); + return 0; +} +#endif + +int +main(int argc, char *argv[]) +{ + if (NULL == (pgm = strrchr(*argv, '/'))) + pgm = *argv; + else + pgm++; +#ifdef CONFIG_GCORE_FREF + if (argc > 1 && 0 == strcmp(argv[1], "fref")) { + return gcore_fref_main(argc, argv); + } +#endif +#ifdef CONFIG_GCORE_MAP + if (argc > 1 && 0 == strcmp(argv[1], "map")) { + return gcore_map_main(argc, argv); + } +#endif +#ifdef CONFIG_GCORE_CONV + if (argc > 1 && 0 == strcmp(argv[1], "conv")) { + return gcore_conv_main(argc, argv); + } +#endif + return gcore_main(argc, argv); +} |