/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <limits.h>
#include <libproc.h>
#include <sys/corectl.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <zone.h>
static char *pname;
static void
convert_path(const char *path, char *fname, size_t size,
struct ps_prochandle *P)
{
char *p, *s;
ssize_t len;
const psinfo_t *pip = Ppsinfo(P);
int got_uts = 0;
struct utsname uts;
char exec[PATH_MAX];
fname[size - 1] = '\0';
size--;
while ((p = strchr(path, '%')) != NULL && size != 0) {
len = MIN(size, p - path);
bcopy(path, fname, len);
fname += len;
if ((size -= len) == 0)
break;
p++;
switch (*p) {
case 'p':
len = snprintf(fname, size, "%d", (int)pip->pr_pid);
break;
case 'u':
len = snprintf(fname, size, "%d", (int)pip->pr_uid);
break;
case 'g':
len = snprintf(fname, size, "%d", (int)pip->pr_gid);
break;
case 'f':
len = snprintf(fname, size, "%s", pip->pr_fname);
break;
case 'd':
len = 0;
if (Pexecname(P, exec, sizeof (exec)) == NULL ||
exec[0] != '/' || (s = strrchr(exec, '/')) == NULL)
break;
*s = '\0';
len = snprintf(fname, size, "%s", &exec[1]);
break;
case 'n':
if (got_uts++ == 0)
(void) uname(&uts);
len = snprintf(fname, size, "%s", uts.nodename);
break;
case 'm':
if (got_uts++ == 0)
(void) uname(&uts);
len = snprintf(fname, size, "%s", uts.machine);
break;
case 't':
len = snprintf(fname, size, "%ld", (long)time(NULL));
break;
case 'z':
/*
* getzonenamebyid() returns the size including the
* terminating null byte so we need to adjust len.
*/
if ((len = getzonenamebyid(pip->pr_zoneid, fname,
size)) < 0)
len = snprintf(fname, size, "%d",
(int)pip->pr_zoneid);
else
len--;
break;
case '%':
*fname = '%';
len = 1;
break;
default:
len = snprintf(fname, size, "%%%c", *p);
}
if (len >= size)
return;
fname += len;
size -= len;
path = p + 1;
}
(void) strncpy(fname, path, size);
}
static void
gcore(struct ps_prochandle *P, const char *fname, core_content_t content,
int *errp)
{
if (Pgcore(P, fname, content) == 0) {
(void) printf("%s: %s dumped\n", pname, fname);
} else {
(void) fprintf(stderr, "%s: %s dump failed: %s\n", pname,
fname, errno == EBADE ? "unexpected short write" :
strerror(errno));
(*errp)++;
}
}
int
main(int argc, char **argv)
{
struct ps_prochandle *P;
int gerr;
char *prefix = NULL;
int opt;
int opt_p = 0, opt_g = 0, opt_c = 0;
int oflags = 0;
int i;
char fname[MAXPATHLEN];
char path[MAXPATHLEN];
int err = 0;
core_content_t content = CC_CONTENT_DEFAULT;
struct rlimit rlim;
if ((pname = strrchr(argv[0], '/')) == NULL)
pname = argv[0];
else
argv[0] = ++pname; /* for getopt() */
while ((opt = getopt(argc, argv, "o:Fgpc:")) != EOF) {
switch (opt) {
case 'o':
prefix = optarg;
break;
case 'c':
if (proc_str2content(optarg, &content) != 0) {
(void) fprintf(stderr, "%s: invalid "
"content string '%s'\n", pname, optarg);
goto usage;
}
opt_c = 1;
break;
case 'F':
oflags |= PGRAB_FORCE;
break;
case 'p':
opt_p = 1;
break;
case 'g':
opt_g = 1;
break;
default:
goto usage;
}
}
if ((opt_p | opt_g) == 0) {
if (prefix == NULL)
prefix = "core";
} else {
int options;
if ((options = core_get_options()) == -1) {
perror("core_get_options()");
return (1);
}
if (opt_p && !(options & CC_PROCESS_PATH)) {
(void) fprintf(stderr, "%s: per-process core dumps "
"are disabled (ignoring -p)\n", pname);
opt_p = 0;
}
if (opt_g && !(options & CC_GLOBAL_PATH)) {
(void) fprintf(stderr, "%s: global core dumps "
"are disabled (ignoring -g)\n", pname);
opt_g = 0;
}
if ((opt_p | opt_g) == 0 && prefix == NULL)
return (1);
}
argc -= optind;
argv += optind;
if (argc == 0)
goto usage;
/*
* Make sure we'll have enough file descriptors to handle a target
* that has many many mappings.
*/
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
rlim.rlim_cur = rlim.rlim_max;
(void) setrlimit(RLIMIT_NOFILE, &rlim);
(void) enable_extended_FILE_stdio(-1, -1);
}
for (i = 0; i < argc; i++) {
P = proc_arg_grab(argv[i], PR_ARG_PIDS, oflags, &gerr);
if (P == NULL) {
(void) fprintf(stderr, "%s: cannot grab %s: %s\n",
pname, argv[i], Pgrab_error(gerr));
err++;
continue;
}
if (prefix != NULL) {
(void) snprintf(path, sizeof (path), "%s.%%p", prefix);
convert_path(path, fname, sizeof (fname), P);
gcore(P, fname, content, &err);
}
if (opt_p) {
pid_t pid = Pstatus(P)->pr_pid;
(void) core_get_process_path(path, sizeof (path), pid);
convert_path(path, fname, sizeof (fname), P);
if (!opt_c)
(void) core_get_process_content(&content, pid);
gcore(P, fname, content, &err);
}
if (opt_g) {
/*
* Global core files are always just readable and
* writable by their owner so we temporarily change
* the umask.
*/
mode_t oldmode = umask(S_IXUSR | S_IRWXG | S_IRWXO);
(void) core_get_global_path(path, sizeof (path));
convert_path(path, fname, sizeof (fname), P);
if (!opt_c)
(void) core_get_global_content(&content);
gcore(P, fname, content, &err);
(void) umask(oldmode);
}
Prelease(P, 0);
}
return (err != 0);
usage:
(void) fprintf(stderr, "usage: %s "
"[ -pgF ] [ -o filename ] [ -c content ] pid ...\n", pname);
return (2);
}