summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_sig.c
diff options
context:
space:
mode:
authoralfred <alfred@FreeBSD.org>2010-03-02 06:58:58 +0000
committeralfred <alfred@FreeBSD.org>2010-03-02 06:58:58 +0000
commitf34ce3dd384c7e26c5474a869851a24b36a10df2 (patch)
treef83c2f2fdc768503cc062e89b74034f42a21dc98 /sys/kern/kern_sig.c
parent8f72bf4ef06c0868171b0ca88af8f0671c7aedee (diff)
downloadFreeBSD-src-f34ce3dd384c7e26c5474a869851a24b36a10df2.zip
FreeBSD-src-f34ce3dd384c7e26c5474a869851a24b36a10df2.tar.gz
Merge projects/enhanced_coredumps (r204346) into HEAD:
Enhanced process coredump routines. This brings in the following features: 1) Limit number of cores per process via the %I coredump formatter. Example: if corefilename is set to %N.%I.core AND num_cores = 3, then if a process "rpd" cores, then the corefile will be named "rpd.0.core", however if it cores again, then the kernel will generate "rpd.1.core" until we hit the limit of "num_cores". this is useful to get several corefiles, but also prevent filling the machine with corefiles. 2) Encode machine hostname in core dump name via %H. 3) Compress coredumps, useful for embedded platforms with limited space. A sysctl kern.compress_user_cores is made available if turned on. To enable compressed coredumps, the following config options need to be set: options COMPRESS_USER_CORES device zlib # brings in the zlib requirements. device gzio # brings in the kernel vnode gzip output module. 4) Eventhandlers are fired to indicate coredumps in progress. 5) The imgact sv_coredump routine has grown a flag to pass in more state, currently this is used only for passing a flag down to compress the coredump or not. Note that the gzio facility can be used for generic output of gzip'd streams via vnodes. Obtained from: Juniper Networks Reviewed by: kan
Diffstat (limited to 'sys/kern/kern_sig.c')
-rw-r--r--sys/kern/kern_sig.c130
1 files changed, 121 insertions, 9 deletions
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index c0ec56f..98a121b 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include "opt_kdtrace.h"
#include "opt_ktrace.h"
+#include "opt_core.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -49,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/condvar.h>
#include <sys/event.h>
#include <sys/fcntl.h>
+#include <sys/imgact.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/ktrace.h>
@@ -78,6 +80,8 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_extern.h>
#include <vm/uma.h>
+#include <sys/jail.h>
+
#include <machine/cpu.h>
#include <security/audit/audit.h>
@@ -98,7 +102,7 @@ SDT_PROBE_ARGTYPE(proc, kernel, , signal_discard, 1, "struct proc *");
SDT_PROBE_ARGTYPE(proc, kernel, , signal_discard, 2, "int");
static int coredump(struct thread *);
-static char *expand_name(const char *, uid_t, pid_t);
+static char *expand_name(const char *, uid_t, pid_t, struct thread *, int);
static int killpg1(struct thread *td, int sig, int pgid, int all,
ksiginfo_t *ksi);
static int issignal(struct thread *td, int stop_allowed);
@@ -2936,12 +2940,51 @@ childproc_exited(struct proc *p)
sigparent(p, reason, status);
}
+/*
+ * We only have 1 character for the core count in the format
+ * string, so the range will be 0-9
+ */
+#define MAX_NUM_CORES 10
+static int num_cores = 5;
+
+static int
+sysctl_debug_num_cores_check (SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ int new_val;
+
+ error = sysctl_handle_int(oidp, &new_val, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (new_val > MAX_NUM_CORES)
+ new_val = MAX_NUM_CORES;
+ if (new_val < 0)
+ new_val = 0;
+ num_cores = new_val;
+ return (0);
+}
+SYSCTL_PROC(_debug, OID_AUTO, ncores, CTLTYPE_INT|CTLFLAG_RW,
+ 0, sizeof(int), sysctl_debug_num_cores_check, "I", "");
+
+#if defined(COMPRESS_USER_CORES)
+int compress_user_cores = 1;
+SYSCTL_INT(_kern, OID_AUTO, compress_user_cores, CTLFLAG_RW,
+ &compress_user_cores, 0, "");
+
+int compress_user_cores_gzlevel = -1; /* default level */
+SYSCTL_INT(_kern, OID_AUTO, compress_user_cores_gzlevel, CTLFLAG_RW,
+ &compress_user_cores_gzlevel, -1, "user core gz compression level");
+
+#define GZ_SUFFIX ".gz"
+#define GZ_SUFFIX_LEN 3
+#endif
+
static char corefilename[MAXPATHLEN] = {"%N.core"};
SYSCTL_STRING(_kern, OID_AUTO, corefile, CTLFLAG_RW, corefilename,
sizeof(corefilename), "process corefile name format string");
/*
- * expand_name(name, uid, pid)
+ * expand_name(name, uid, pid, td, compress)
* Expand the name described in corefilename, using name, uid, and pid.
* corefilename is a printf-like string, with three format specifiers:
* %N name of process ("name")
@@ -2952,20 +2995,21 @@ SYSCTL_STRING(_kern, OID_AUTO, corefile, CTLFLAG_RW, corefilename,
* This is controlled by the sysctl variable kern.corefile (see above).
*/
static char *
-expand_name(name, uid, pid)
- const char *name;
- uid_t uid;
- pid_t pid;
+expand_name(const char *name, uid_t uid, pid_t pid, struct thread *td,
+ int compress)
{
struct sbuf sb;
const char *format;
char *temp;
size_t i;
-
+ int indexpos;
+ char hostname[MAXHOSTNAMELEN];
+
format = corefilename;
temp = malloc(MAXPATHLEN, M_TEMP, M_NOWAIT | M_ZERO);
if (temp == NULL)
return (NULL);
+ indexpos = -1;
(void)sbuf_new(&sb, temp, MAXPATHLEN, SBUF_FIXEDLEN);
for (i = 0; format[i]; i++) {
switch (format[i]) {
@@ -2975,6 +3019,15 @@ expand_name(name, uid, pid)
case '%':
sbuf_putc(&sb, '%');
break;
+ case 'H': /* hostname */
+ getcredhostname(td->td_ucred, hostname,
+ sizeof(hostname));
+ sbuf_printf(&sb, "%s", hostname);
+ break;
+ case 'I': /* autoincrementing index */
+ sbuf_printf(&sb, "0");
+ indexpos = sbuf_len(&sb) - 1;
+ break;
case 'N': /* process name */
sbuf_printf(&sb, "%s", name);
break;
@@ -2994,6 +3047,11 @@ expand_name(name, uid, pid)
sbuf_putc(&sb, format[i]);
}
}
+#ifdef COMPRESS_USER_CORES
+ if (compress) {
+ sbuf_printf(&sb, GZ_SUFFIX);
+ }
+#endif
if (sbuf_overflowed(&sb)) {
sbuf_delete(&sb);
log(LOG_ERR, "pid %ld (%s), uid (%lu): corename is too "
@@ -3003,6 +3061,53 @@ expand_name(name, uid, pid)
}
sbuf_finish(&sb);
sbuf_delete(&sb);
+
+ /*
+ * If the core format has a %I in it, then we need to check
+ * for existing corefiles before returning a name.
+ * To do this we iterate over 0..num_cores to find a
+ * non-existing core file name to use.
+ */
+ if (indexpos != -1) {
+ struct nameidata nd;
+ int error, n;
+ int flags = O_CREAT | O_EXCL | FWRITE | O_NOFOLLOW;
+ int cmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+ int vfslocked;
+
+ for (n = 0; n < num_cores; n++) {
+ temp[indexpos] = '0' + n;
+ NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE,
+ temp, td);
+ error = vn_open(&nd, &flags, cmode, NULL);
+ if (error) {
+ if (error == EEXIST) {
+ continue;
+ }
+ log(LOG_ERR,
+ "pid %d (%s), uid (%u): Path `%s' failed "
+ "on initial open test, error = %d\n",
+ pid, name, uid, temp, error);
+ free(temp, M_TEMP);
+ return (NULL);
+ }
+ vfslocked = NDHASGIANT(&nd);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ VOP_UNLOCK(nd.ni_vp, 0);
+ error = vn_close(nd.ni_vp, FWRITE, td->td_ucred, td);
+ VFS_UNLOCK_GIANT(vfslocked);
+ if (error) {
+ log(LOG_ERR,
+ "pid %d (%s), uid (%u): Path `%s' failed "
+ "on close after initial open test, "
+ "error = %d\n",
+ pid, name, uid, temp, error);
+ free(temp, M_TEMP);
+ return (NULL);
+ }
+ break;
+ }
+ }
return (temp);
}
@@ -3028,12 +3133,19 @@ coredump(struct thread *td)
char *name; /* name of corefile */
off_t limit;
int vfslocked;
+ int compress;
+#ifdef COMPRESS_USER_CORES
+ compress = compress_user_cores;
+#else
+ compress = 0;
+#endif
PROC_LOCK_ASSERT(p, MA_OWNED);
MPASS((p->p_flag & P_HADTHREADS) == 0 || p->p_singlethread == td);
_STOPEVENT(p, S_CORE, 0);
- name = expand_name(p->p_comm, td->td_ucred->cr_uid, p->p_pid);
+ name = expand_name(p->p_comm, td->td_ucred->cr_uid, p->p_pid, td,
+ compress);
if (name == NULL) {
PROC_UNLOCK(p);
#ifdef AUDIT
@@ -3124,7 +3236,7 @@ restart:
PROC_UNLOCK(p);
error = p->p_sysent->sv_coredump ?
- p->p_sysent->sv_coredump(td, vp, limit) :
+ p->p_sysent->sv_coredump(td, vp, limit, compress ? IMGACT_CORE_COMPRESS : 0) :
ENOSYS;
if (locked) {
OpenPOWER on IntegriCloud