diff options
author | Alfred Perlstein <alfred@FreeBSD.org> | 2010-03-02 06:58:58 +0000 |
---|---|---|
committer | Alfred Perlstein <alfred@FreeBSD.org> | 2010-03-02 06:58:58 +0000 |
commit | e7228204343ac61d2316e74891e3606361a23f6e (patch) | |
tree | f83c2f2fdc768503cc062e89b74034f42a21dc98 /sys/kern/kern_sig.c | |
parent | 24179b01409038c958cdeb06e602d9878d06db1b (diff) | |
download | src-e7228204343ac61d2316e74891e3606361a23f6e.tar.gz src-e7228204343ac61d2316e74891e3606361a23f6e.zip |
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
Notes
Notes:
svn path=/head/; revision=204552
Diffstat (limited to 'sys/kern/kern_sig.c')
-rw-r--r-- | sys/kern/kern_sig.c | 130 |
1 files changed, 121 insertions, 9 deletions
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index c0ec56f74f71..98a121b12da0 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) { |