2 * Copyright (c) 2016 Apple Inc. All rights reserved.
11 #include <sys/types.h>
12 #include <sys/sysctl.h>
17 #include <sys/kauth.h>
33 #include <mach/mach.h>
38 char *(^sysc_string
)(const char *name
) = ^(const char *name
) {
42 if (-1 == sysctlbyname(name
, NULL
, &len
, NULL
, 0)) {
43 warnc(errno
, "sysctl: %s", name
);
44 } else if (0 != len
) {
46 if (-1 == sysctlbyname(name
, p
, &len
, NULL
, 0)) {
47 warnc(errno
, "sysctl: %s", name
);
55 char *s
= sysc_string("kern.corefile");
57 s
= strdup("/cores/core.%P");
61 static const struct proc_bsdinfo
*
62 get_bsdinfo(pid_t pid
)
66 struct proc_bsdinfo
*pbi
= calloc(1, sizeof (*pbi
));
67 if (0 != proc_pidinfo(pid
, PROC_PIDTBSDINFO
, 0, pbi
, sizeof (*pbi
)))
74 format_gcore_name(const char *fmt
, const struct proc_bsdinfo
*pbi
)
76 __block
size_t resid
= MAXPATHLEN
;
77 __block
char *p
= calloc(1, resid
);
80 int (^outchar
)(int c
) = ^(int c
) {
89 ptrdiff_t (^outstr
)(const char *str
) = ^(const char *str
) {
91 while (*s
&& 0 != outchar(*s
++))
96 ptrdiff_t (^outint
)(int value
)= ^(int value
) {
98 snprintf(id
, sizeof (id
), "%u", value
);
102 ptrdiff_t (^outtstamp
)(void) = ^(void) {
108 strftime(tstamp
, sizeof (tstamp
), "%Y%m%dT%H%M%SZ", &tm
);
109 return outstr(tstamp
);
114 for (int i
= 0; resid
> 0; i
++)
115 switch (c
= fmt
[i
]) {
121 switch (c
= fmt
[i
]) {
126 outint(pbi
->pbi_pid
);
129 outint(pbi
->pbi_uid
);
132 outstr(pbi
->pbi_name
[0] ?
133 pbi
->pbi_name
: pbi
->pbi_comm
);
136 outtstamp(); // ISO 8601 format
140 err(EX_DATAERR
, "unknown format char: %%%c", c
);
142 err(EX_DATAERR
, "bad format char %%\\%03o", c
);
144 err(EX_DATAERR
, "bad format specifier");
156 const struct options
*opt
;
159 main(int argc
, char *const *argv
)
161 if (NULL
== (pgm
= strrchr(*argv
, '/')))
167 #define ZOPT_CHSIZE (ZOPT_ALG + 1)
169 static char *const zoptkeys
[] = {
170 [ZOPT_ALG
] = "algorithm",
171 [ZOPT_CHSIZE
] = "chunksize",
175 err_set_exit_b(^(int eval
) {
176 if (EX_USAGE
== eval
) {
178 "usage:\n\t%s [-s] [-v] [[-o file] | [-c pathfmt ]] [-b size] "
180 "[-d] [-n] [-i] [-p] [-S] [-z] [-C] "
181 "[-Z compression-options] "
188 fprintf(stderr
, "where compression-options:\n");
189 const char zvalfmt
[] = "\t%s=%s\t\t%s\n";
190 fprintf(stderr
, zvalfmt
, zoptkeys
[ZOPT_ALG
], "alg",
191 "set compression algorithm");
192 fprintf(stderr
, zvalfmt
, zoptkeys
[ZOPT_CHSIZE
], "size",
193 "set compression chunksize, Mib");
198 char *corefmt
= NULL
;
199 char *corefname
= NULL
;
200 const size_t oneM
= 1024 * 1024;
202 #define LARGEST_CHUNKSIZE INT32_MAX
203 #define DEFAULT_COMPRESSION_CHUNKSIZE (16 * oneM)
205 struct options options
= {
219 .chunksize
= LARGEST_CHUNKSIZE
,
220 .calgorithm
= COMPRESSION_LZFSE
,
226 while ((c
= getopt(argc
, argv
, "inmvdszpCSRZ:o:c:b:")) != -1) {
232 case 's': /* FreeBSD compat: stop while gathering */
235 case 'o': /* Linux (& SunOS) compat: basic name */
236 corefname
= strdup(optarg
);
238 case 'c': /* FreeBSD compat: basic name */
239 /* (also allows pattern-based naming) */
240 corefmt
= strdup(optarg
);
243 case 'b': /* bound the size of the core file */
244 if (NULL
!= optarg
) {
245 off_t bsize
= atoi(optarg
) * oneM
;
247 options
.sizebound
= bsize
;
249 errx(EX_USAGE
, "invalid bound");
251 errx(EX_USAGE
, "no bound specified");
253 case 'v': /* verbose output */
258 * dev and debugging help
260 case 'n': /* write the core file to /dev/null */
263 case 'd': /* debugging */
268 case 'p': /* preserve partial core file (even if errors) */
273 * Remaining options are experimental and/or
274 * affect the content of the core file
276 case 'i': /* include LC_COREINFO data */
279 case 'C': /* corpsify rather than suspend */
283 case 'R': /* include the shared cache by reference */
288 case 'S': /* use dyld info to control the content */
292 case 'z': /* create compressed LC_SEGMENT* segments */
293 if (0 == options
.compress
) {
295 options
.chunksize
= DEFAULT_COMPRESSION_CHUNKSIZE
;
299 case 'Z': /* control compression options */
301 * Only LZFSE and LZ4 seem practical.
302 * (Default to LZ4 compression when the
303 * process is suspended, LZFSE when corpsed?)
305 if (0 == options
.compress
) {
307 options
.chunksize
= DEFAULT_COMPRESSION_CHUNKSIZE
;
313 switch (getsubopt(&sopts
, zoptkeys
, &value
)) {
314 case ZOPT_ALG
: /* change the algorithm */
316 errx(EX_USAGE
, "missing algorithm for "
319 if (strcmp(value
, "lz4") == 0)
322 else if (strcmp(value
, "zlib") == 0)
325 else if (strcmp(value
, "lzma") == 0)
328 else if (strcmp(value
, "lzfse") == 0)
332 errx(EX_USAGE
, "unknown algorithm '%s'"
337 case ZOPT_CHSIZE
: /* set the chunksize */
339 errx(EX_USAGE
, "no value specified for "
341 zoptkeys
[ZOPT_CHSIZE
]);
342 if ((chsize
= atoi(value
)) < 1)
343 errx(EX_USAGE
, "chunksize %lu too small", chsize
);
344 if (chsize
> (LARGEST_CHUNKSIZE
/ oneM
))
345 errx(EX_USAGE
, "chunksize %lu too large", chsize
);
346 options
.chunksize
= chsize
* oneM
;
350 errx(EX_USAGE
, "illegal suboption '%s'",
353 errx(EX_USAGE
, "missing suboption");
358 errx(EX_USAGE
, "unknown flag");
362 errx(EX_USAGE
, "no pid specified");
366 if ((opt
->dryrun
? 1 : 0) +
367 (NULL
!= corefname
? 1 : 0) +
368 (NULL
!= corefmt
? 1 : 0) > 1)
369 errx(EX_USAGE
, "specify only one of -n, -o and -c");
373 const pid_t pid
= atoi(argv
[optind
]);
374 if (pid
< 1 || getpid() == pid
)
375 errx(EX_DATAERR
, "invalid pid: %d", pid
);
376 if (-1 == kill(pid
, 0)) {
379 errc(EX_DATAERR
, errno
, "no process with pid %d", pid
);
381 errc(EX_DATAERR
, errno
, "pid %d", pid
);
385 const struct proc_bsdinfo
*pbi
= get_bsdinfo(pid
);
387 errx(EX_OSERR
, "cannot get bsdinfo about %d", pid
);
390 * make our data model match the data model of the target
392 if (-1 == reexec_to_match_lp64ness(pbi
->pbi_flags
& PROC_FLAG_LP64
))
393 errc(1, errno
, "cannot match data model of %d", pid
);
395 #if defined(__LP64__)
396 if ((pbi
->pbi_flags
& PROC_FLAG_LP64
) == 0)
398 if ((pbi
->pbi_flags
& PROC_FLAG_LP64
) != 0)
400 errx(EX_OSERR
, "cannot match data model of %d", pid
);
403 * These are experimental options for the moment.
404 * These will likely change.
405 * Some may become defaults, some may be removed altogether.
414 warnx("experimental option(s) used, "
415 "resulting corefile may be unusable.");
417 if (pbi
->pbi_ruid
!= pbi
->pbi_svuid
||
418 pbi
->pbi_rgid
!= pbi
->pbi_svgid
)
419 errx(EX_NOPERM
, "pid %d - not dumping a set-id process", pid
);
421 if (NULL
== corefname
) {
422 if (NULL
== corefmt
) {
423 const char defcore
[] = "%N-%P-%T";
424 if (NULL
== (corefmt
= kern_corefile()))
425 corefmt
= strdup(defcore
);
427 // use the same directory as kern.corefile
428 char *p
= strrchr(corefmt
, '/');
431 size_t len
= strlen(corefmt
) +
433 char *buf
= malloc(len
);
434 snprintf(buf
, len
, "%s/%s", corefmt
, defcore
);
439 printf("corefmt '%s'\n", corefmt
);
442 corefname
= format_gcore_name(corefmt
, pbi
);
447 kern_return_t ret
= task_for_pid(mach_task_self(), pid
, &task
);
448 if (KERN_SUCCESS
!= ret
) {
449 if (KERN_FAILURE
== ret
)
450 errx(EX_NOPERM
, "insufficient privilege");
452 errx(EX_NOPERM
, "task_for_pid: %s", mach_error_string(ret
));
456 * Now that we have the task port, we adopt the credentials of
457 * the target process, *before* opening the core file, and
458 * analyzing the address space.
460 * If we are unable to match the target credentials, bail out.
462 if (getgid() != pbi
->pbi_gid
&&
463 setgid(pbi
->pbi_gid
) == -1)
464 errc(EX_NOPERM
, errno
, "insufficient privilege");
466 if (getuid() != pbi
->pbi_uid
&&
467 setuid(pbi
->pbi_uid
) == -1)
468 errc(EX_NOPERM
, errno
, "insufficient privilege");
474 corefname
= strdup("/dev/null");
475 fd
= open(corefname
, O_RDWR
);
477 fd
= open(corefname
, O_RDWR
| O_CREAT
| O_EXCL
, 0400);
481 if (-1 == fd
|| -1 == fstat(fd
, &st
))
482 errc(EX_CANTCREAT
, errno
, "%s", corefname
);
483 if ((st
.st_mode
& S_IFMT
) != S_IFREG
|| 1 != st
.st_nlink
) {
485 errx(EX_CANTCREAT
, "%s: invalid file", corefname
);
490 printf("Dumping core ");
493 opt
->sparse
? "sparse" : "normal",
494 opt
->compress
? ", compressed" : "",
496 opt
->scfileref
? ", scfilerefs" :
499 if (0 != opt
->sizebound
) {
501 printf(", %s", str_hsize(hstr
, opt
->sizebound
));
505 printf("for pid %d to %s\n", pid
, corefname
);
510 * The traditional way to capture a consistent core dump is to
511 * suspend the process while processing it and writing it out.
512 * Yet suspending a large process for a long time can have
513 * unpleasant side-effects. Alternatively dumping from the live
514 * process can lead to an inconsistent state in the core file.
516 * Instead we can ask xnu to create a 'corpse' - the process is transiently
517 * suspended while a COW snapshot of the address space is constructed
518 * in the kernel and dump from that. This vastly reduces the suspend
519 * time, but it is more resource hungry and thus may fail.
521 * The -s flag (opt->suspend) causes a task_suspend/task_resume
522 * The -C flag (opt->corpse) causes a corpse be taken, exiting if that fails.
523 * Both flags can be specified, in which case corpse errors are ignored.
525 * With no flags, we imitate traditional behavior though more
526 * efficiently: we try to take a corpse-based dump, in the event that
527 * fails, dump the live process.
530 int trycorpse
= 1; /* default: use corpses */
531 int badcorpse_is_fatal
= 1; /* default: failure to create a corpse is an error */
533 if (!opt
->suspend
&& !opt
->corpse
) {
534 /* try a corpse dump, else dump the live process */
535 badcorpse_is_fatal
= 0;
536 } else if (opt
->suspend
) {
537 trycorpse
= opt
->corpse
;
538 /* if suspended anyway, ignore corpse-creation errors */
539 badcorpse_is_fatal
= 0;
547 * Create a corpse from the image before dumping it
549 mach_port_t corpse
= MACH_PORT_NULL
;
550 ret
= task_generate_corpse(task
, &corpse
);
554 printf("corpse generated on port %x, task %x\n",
556 ecode
= coredump(corpse
, fd
);
557 mach_port_deallocate(mach_task_self(), corpse
);
560 if (badcorpse_is_fatal
|| opt
->debug
) {
561 warnx("failed to snapshot pid %d: %s\n",
562 pid
, mach_error_string(ret
));
563 if (badcorpse_is_fatal
) {
564 ecode
= KERN_RESOURCE_SHORTAGE
== ret
? EX_TEMPFAIL
: EX_OSERR
;
568 ecode
= coredump(task
, fd
);
573 * Examine the task directly
575 ecode
= coredump(task
, fd
);
582 if (0 != ecode
&& !opt
->preserve
&& !opt
->dryrun
) {
584 * try not to leave a half-written mess occupying
585 * blocks on the filesystem
593 errx(ecode
, "failed to dump core for pid %d", pid
);