Date: Tue, 2 Mar 2010 06:58:58 +0000 (UTC) From: Alfred Perlstein <alfred@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r204552 - in head/sys: conf kern net sys Message-ID: <201003020658.o226wwB2051156@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: alfred Date: Tue Mar 2 06:58:58 2010 New Revision: 204552 URL: http://svn.freebsd.org/changeset/base/204552 Log: 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 Added: head/sys/kern/kern_gzio.c (contents, props changed) head/sys/net/zutil.h (contents, props changed) Modified: head/sys/conf/files head/sys/conf/options head/sys/kern/imgact_elf.c head/sys/kern/kern_sig.c head/sys/net/zlib.h head/sys/sys/eventhandler.h head/sys/sys/imgact.h head/sys/sys/imgact_aout.h head/sys/sys/imgact_elf.h head/sys/sys/sysent.h Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Tue Mar 2 06:54:15 2010 (r204551) +++ head/sys/conf/files Tue Mar 2 06:58:58 2010 (r204552) @@ -2053,6 +2053,7 @@ kern/kern_exec.c standard kern/kern_exit.c standard kern/kern_fail.c standard kern/kern_fork.c standard +kern/kern_gzio.c optional gzio kern/kern_idle.c standard kern/kern_intr.c standard kern/kern_jail.c standard @@ -2344,7 +2345,7 @@ net/slcompress.c optional netgraph_vjc net/vnet.c optional vimage net/zlib.c optional crypto | geom_uzip | ipsec | \ mxge | netgraph_deflate | \ - ddb_ctf + ddb_ctf | zlib net80211/ieee80211.c optional wlan net80211/ieee80211_acl.c optional wlan wlan_acl net80211/ieee80211_action.c optional wlan Modified: head/sys/conf/options ============================================================================== --- head/sys/conf/options Tue Mar 2 06:54:15 2010 (r204551) +++ head/sys/conf/options Tue Mar 2 06:58:58 2010 (r204552) @@ -71,6 +71,7 @@ COMPAT_FREEBSD5 opt_compat.h COMPAT_FREEBSD6 opt_compat.h COMPAT_FREEBSD7 opt_compat.h COMPILING_LINT opt_global.h +COMPRESS_USER_CORES opt_core.h CY_PCI_FASTINTR DEADLKRES opt_watchdog.h DIRECTIO Modified: head/sys/kern/imgact_elf.c ============================================================================== --- head/sys/kern/imgact_elf.c Tue Mar 2 06:54:15 2010 (r204551) +++ head/sys/kern/imgact_elf.c Tue Mar 2 06:58:58 2010 (r204552) @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" +#include "opt_core.h" #include <sys/param.h> #include <sys/exec.h> @@ -58,6 +59,10 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/sysent.h> #include <sys/vnode.h> +#include <sys/syslog.h> +#include <sys/eventhandler.h> + +#include <net/zlib.h> #include <vm/vm.h> #include <vm/vm_kern.h> @@ -95,6 +100,12 @@ static boolean_t __elfN(check_note)(stru SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW, 0, ""); +#ifdef COMPRESS_USER_CORES +static int compress_core(gzFile, char *, char *, unsigned int, + struct thread * td); +#define CORE_BUF_SIZE (16 * 1024) +#endif + int __elfN(fallback_brand) = -1; SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, fallback_brand, CTLFLAG_RW, &__elfN(fallback_brand), 0, @@ -1003,16 +1014,38 @@ static void cb_put_phdr(vm_map_entry_t, static void cb_size_segment(vm_map_entry_t, void *); static void each_writable_segment(struct thread *, segment_callback, void *); static int __elfN(corehdr)(struct thread *, struct vnode *, struct ucred *, - int, void *, size_t); + int, void *, size_t, gzFile); static void __elfN(puthdr)(struct thread *, void *, size_t *, int); static void __elfN(putnote)(void *, size_t *, const char *, int, const void *, size_t); +#ifdef COMPRESS_USER_CORES +extern int compress_user_cores; +extern int compress_user_cores_gzlevel; +#endif + +static int +core_output(struct vnode *vp, void *base, size_t len, off_t offset, + struct ucred *active_cred, struct ucred *file_cred, + struct thread *td, char *core_buf, gzFile gzfile) { + + int error; + if (gzfile) { +#ifdef COMPRESS_USER_CORES + error = compress_core(gzfile, base, core_buf, len, td); +#else + panic("shouldn't be here"); +#endif + } else { + error = vn_rdwr_inchunks(UIO_WRITE, vp, base, len, offset, + UIO_USERSPACE, IO_UNIT | IO_DIRECT, active_cred, file_cred, + NULL, td); + } + return (error); +} + int -__elfN(coredump)(td, vp, limit) - struct thread *td; - struct vnode *vp; - off_t limit; +__elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) { struct ucred *cred = td->td_ucred; int error = 0; @@ -1020,6 +1053,37 @@ __elfN(coredump)(td, vp, limit) void *hdr; size_t hdrsize; + gzFile gzfile = Z_NULL; + char *core_buf = NULL; +#ifdef COMPRESS_USER_CORES + char gzopen_flags[8]; + char *p; + int doing_compress = flags & IMGACT_CORE_COMPRESS; +#endif + + hdr = NULL; + +#ifdef COMPRESS_USER_CORES + if (doing_compress) { + p = gzopen_flags; + *p++ = 'w'; + if (compress_user_cores_gzlevel >= 0 && + compress_user_cores_gzlevel <= 9) + *p++ = '0' + compress_user_cores_gzlevel; + *p = 0; + gzfile = gz_open("", gzopen_flags, vp); + if (gzfile == Z_NULL) { + error = EFAULT; + goto done; + } + core_buf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); + if (!core_buf) { + error = ENOMEM; + goto done; + } + } +#endif + /* Size the program segments. */ seginfo.count = 0; seginfo.size = 0; @@ -1044,7 +1108,8 @@ __elfN(coredump)(td, vp, limit) if (hdr == NULL) { return (EINVAL); } - error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize); + error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize, + gzfile); /* Write the contents of all of the writable segments. */ if (error == 0) { @@ -1055,17 +1120,28 @@ __elfN(coredump)(td, vp, limit) php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; offset = hdrsize; for (i = 0; i < seginfo.count; i++) { - error = vn_rdwr_inchunks(UIO_WRITE, vp, - (caddr_t)(uintptr_t)php->p_vaddr, - php->p_filesz, offset, UIO_USERSPACE, - IO_UNIT | IO_DIRECT, cred, NOCRED, NULL, - curthread); + error = core_output(vp, (caddr_t)(uintptr_t)php->p_vaddr, + php->p_filesz, offset, cred, NOCRED, curthread, core_buf, gzfile); if (error != 0) break; offset += php->p_filesz; php++; } } + if (error) { + log(LOG_WARNING, + "Failed to write core file for process %s (error %d)\n", + curproc->p_comm, error); + } + +#ifdef COMPRESS_USER_CORES +done: +#endif + if (core_buf) + free(core_buf, M_TEMP); + if (gzfile) + gzclose(gzfile); + free(hdr, M_TEMP); return (error); @@ -1189,13 +1265,14 @@ each_writable_segment(td, func, closure) * the page boundary. */ static int -__elfN(corehdr)(td, vp, cred, numsegs, hdr, hdrsize) +__elfN(corehdr)(td, vp, cred, numsegs, hdr, hdrsize, gzfile) struct thread *td; struct vnode *vp; struct ucred *cred; int numsegs; size_t hdrsize; void *hdr; + gzFile gzfile; { size_t off; @@ -1204,10 +1281,26 @@ __elfN(corehdr)(td, vp, cred, numsegs, h off = 0; __elfN(puthdr)(td, hdr, &off, numsegs); - /* Write it to the core file. */ - return (vn_rdwr_inchunks(UIO_WRITE, vp, hdr, hdrsize, (off_t)0, - UIO_SYSSPACE, IO_UNIT | IO_DIRECT, cred, NOCRED, NULL, - td)); + if (!gzfile) { + /* Write it to the core file. */ + return (vn_rdwr_inchunks(UIO_WRITE, vp, hdr, hdrsize, (off_t)0, + UIO_SYSSPACE, IO_UNIT | IO_DIRECT, cred, NOCRED, NULL, + td)); + } else { +#ifdef COMPRESS_USER_CORES + if (gzwrite(gzfile, hdr, hdrsize) != hdrsize) { + log(LOG_WARNING, + "Failed to compress core file header for process" + " %s.\n", curproc->p_comm); + return (EFAULT); + } + else { + return (0); + } +#else + panic("shouldn't be here"); +#endif + } } #if defined(COMPAT_IA32) && __ELF_WORD_SIZE == 32 @@ -1476,3 +1569,50 @@ static struct execsw __elfN(execsw) = { __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) }; EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw)); + +#ifdef COMPRESS_USER_CORES +/* + * Compress and write out a core segment for a user process. + * + * 'inbuf' is the starting address of a VM segment in the process' address + * space that is to be compressed and written out to the core file. 'dest_buf' + * is a buffer in the kernel's address space. The segment is copied from + * 'inbuf' to 'dest_buf' first before being processed by the compression + * routine gzwrite(). This copying is necessary because the content of the VM + * segment may change between the compression pass and the crc-computation pass + * in gzwrite(). This is because realtime threads may preempt the UNIX kernel. + */ +static int +compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, + struct thread *td) +{ + int len_compressed; + int error = 0; + unsigned int chunk_len; + + while (len) { + chunk_len = (len > CORE_BUF_SIZE) ? CORE_BUF_SIZE : len; + copyin(inbuf, dest_buf, chunk_len); + len_compressed = gzwrite(file, dest_buf, chunk_len); + + EVENTHANDLER_INVOKE(app_coredump_progress, td, len_compressed); + + if ((unsigned int)len_compressed != chunk_len) { + log(LOG_WARNING, + "compress_core: length mismatch (0x%x returned, " + "0x%x expected)\n", len_compressed, chunk_len); + EVENTHANDLER_INVOKE(app_coredump_error, td, + "compress_core: length mismatch %x -> %x", + chunk_len, len_compressed); + error = EFAULT; + break; + } + inbuf += chunk_len; + len -= chunk_len; + if (ticks - PCPU_GET(switchticks) >= hogticks) + uio_yield(); + } + + return (error); +} +#endif /* COMPRESS_USER_CORES */ Added: head/sys/kern/kern_gzio.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/kern/kern_gzio.c Tue Mar 2 06:58:58 2010 (r204552) @@ -0,0 +1,406 @@ +/* + * $Id: kern_gzio.c,v 1.6 2008-10-18 22:54:45 lbazinet Exp $ + * + * core_gzip.c -- gzip routines used in compressing user process cores + * + * This file is derived from src/lib/libz/gzio.c in FreeBSD. + */ + +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-1998 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + */ + +/* @(#) $FreeBSD$ */ + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/vnode.h> +#include <sys/syslog.h> +#include <sys/endian.h> +#include <net/zutil.h> +#include <sys/libkern.h> + +#include <sys/vnode.h> +#include <sys/mount.h> + +#define GZ_HEADER_LEN 10 + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#define ALLOC(size) malloc(size, M_TEMP, M_WAITOK | M_ZERO) +#define TRYFREE(p) {if (p) free(p, M_TEMP);} + +static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + struct vnode *file; /* vnode pointer of .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + long startpos; /* start of compressed data in file (header skipped) */ + off_t outoff; /* current offset in output file */ + int flags; +} gz_stream; + + +local int do_flush OF((gzFile file, int flush)); +local int destroy OF((gz_stream *s)); +local void putU32 OF((gz_stream *file, uint32_t x)); +local void *gz_alloc OF((void *notused, u_int items, u_int size)); +local void gz_free OF((void *notused, void *ptr)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open return NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +gzFile gz_open (path, mode, vp) + const char *path; + const char *mode; + struct vnode *vp; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + const char *p = mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + int resid; + int error; + char buf[GZ_HEADER_LEN + 1]; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)gz_alloc; + s->stream.zfree = (free_func)gz_free; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->crc = 0; + s->msg = NULL; + s->transparent = 0; + s->outoff = 0; + s->flags = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + + if (s->mode != 'w') { + log(LOG_ERR, "gz_open: mode is not w (%c)\n", s->mode); + return destroy(s), (gzFile)Z_NULL; + } + + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + + s->stream.avail_out = Z_BUFSIZE; + s->file = vp; + + /* Write a very simple .gz header: + */ + snprintf(buf, sizeof(buf), "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], + gz_magic[1], Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, + 0 /*xflags*/, OS_CODE); + + if ((error = vn_rdwr(UIO_WRITE, s->file, buf, GZ_HEADER_LEN, s->outoff, + UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, curproc->p_ucred, + NOCRED, &resid, curthread))) { + s->outoff += GZ_HEADER_LEN - resid; + return destroy(s), (gzFile)Z_NULL; + } + s->outoff += GZ_HEADER_LEN; + s->startpos = 10L; + + return (gzFile)s; +} + + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { + err = deflateEnd(&(s->stream)); + } + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + + +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + const voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + off_t curoff; + size_t resid; + int error; + int vfslocked; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + curoff = s->outoff; + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + vfslocked = VFS_LOCK_GIANT(s->file->v_mount); + error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, Z_BUFSIZE, + curoff, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, + curproc->p_ucred, NOCRED, &resid, curthread); + VFS_UNLOCK_GIANT(vfslocked); + if (error) { + log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error); + curoff += Z_BUFSIZE - resid; + s->z_err = Z_ERRNO; + break; + } + curoff += Z_BUFSIZE; + s->stream.avail_out = Z_BUFSIZE; + } + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + if (s->z_err != Z_OK) { + log(LOG_ERR, + "gzwrite: deflate returned error %d\n", s->z_err); + break; + } + } + + s->crc = ~crc32_raw(buf, len, ~s->crc); + s->outoff = curoff; + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + off_t curoff = s->outoff; + size_t resid; + int vfslocked = 0; + int error; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + if (s->stream.avail_in) { + log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n"); + } + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + vfslocked = VFS_LOCK_GIANT(s->file->v_mount); + error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, len, curoff, + UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, curproc->p_ucred, + NOCRED, &resid, curthread); + VFS_UNLOCK_GIANT(vfslocked); + if (error) { + s->z_err = Z_ERRNO; + s->outoff = curoff + len - resid; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + curoff += len; + } + if (done) break; + s->z_err = deflate(&(s->stream), flush); + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + s->outoff = curoff; + + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putU32 (s, x) + gz_stream *s; + uint32_t x; +{ + uint32_t xx; + off_t curoff = s->outoff; + int resid; + +#if BYTE_ORDER == BIG_ENDIAN + xx = bswap32(x); +#else + xx = x; +#endif + vn_rdwr(UIO_WRITE, s->file, (caddr_t)&xx, sizeof(xx), curoff, + UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, curproc->p_ucred, + NOCRED, &resid, curthread); + s->outoff += sizeof(xx) - resid; +} + + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + int err; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { + err = do_flush (file, Z_FINISH); + if (err != Z_OK) { + log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err); + return destroy((gz_stream*)file); + } +#if 0 + printf("gzclose: putting crc: %lld total: %lld\n", + (long long)s->crc, (long long)s->stream.total_in); + printf("sizeof uLong = %d\n", (int)sizeof(uLong)); +#endif + putU32 (s, s->crc); + putU32 (s, (uint32_t) s->stream.total_in); + } + return destroy((gz_stream*)file); +} + +/* + * Space allocation and freeing routines for use by zlib routines when called + * from gzip modules. + */ +static void * +gz_alloc(void *notused __unused, u_int items, u_int size) +{ + void *ptr; + + MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO); + return ptr; +} + +static void +gz_free(void *opaque __unused, void *ptr) +{ + FREE(ptr, M_TEMP); +} + Modified: head/sys/kern/kern_sig.c ============================================================================== --- head/sys/kern/kern_sig.c Tue Mar 2 06:54:15 2010 (r204551) +++ head/sys/kern/kern_sig.c Tue Mar 2 06:58:58 2010 (r204552) @@ -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 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, * 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) { Modified: head/sys/net/zlib.h ============================================================================== --- head/sys/net/zlib.h Tue Mar 2 06:54:15 2010 (r204551) +++ head/sys/net/zlib.h Tue Mar 2 06:58:58 2010 (r204552) @@ -1010,6 +1010,13 @@ extern int EXPORT inflateInit2_ OF((z_st uLongf *get_crc_table OF((void)); /* can be used by asm versions of crc32() */ +#ifdef _KERNEL +struct vnode; +extern gzFile gz_open OF((const char *path, const char *mode, + struct vnode *vp)); +#endif + + #ifdef __cplusplus } #endif Added: head/sys/net/zutil.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/net/zutil.h Tue Mar 2 06:58:58 2010 (r204552) @@ -0,0 +1,231 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: zutil.h,v 1.16 1996/07/24 13:41:13 me Exp $ */ +/* $FreeBSD$ */ + +#ifndef _Z_UTIL_H +#define _Z_UTIL_H + +#define ZEXPORT + +#ifdef _KERNEL +#include <net/zlib.h> +#else +#include "zlib.h" +#endif + +#ifdef _KERNEL +/* Assume this is a *BSD or SVR4 kernel */ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/errno.h> +#include <sys/param.h> +#include <sys/kernel.h> +# define HAVE_MEMCPY +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memset(d, v, n) bzero((d), (n)) +# define memcmp bcmp + +#else +#if defined(__KERNEL__) +/* Assume this is a Linux kernel */ +#include <linux/string.h> +#define HAVE_MEMCPY + +#else /* not kernel */ + +#if defined(MSDOS)||defined(VMS)||defined(CRAY)||defined(WIN32)||defined(RISCOS) +# include <stddef.h> +# include <errno.h> +#else + extern int errno; +#endif +#ifdef STDC +# include <string.h> +# include <stdlib.h> +#endif +#endif /* __KERNEL__ */ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201003020658.o226wwB2051156>