Subject: Multiple core files overwrite each other (#186) Index: sys/sys/kern_sig.c, ucb/gcore.c 2.11BSD Description: If more than one program dumps core in a directory all but the last core file is overwritten. Repeat-By: Have more than one program abort (or be killed by sending it a signal) in the same directory. System daemons (and getty) which chdir to / or use / as their home directory are good candidates. Fix: The change to the kernel is small and involves replacing the fixed string "core" with a computed filename of "program.core" where 'program' is the first 14 characters of the executable's filename. It is now more obvious which program produced the core image. The program name was always present in the 'u' area at the front of the core image but required a 'strings core | head' to determine the name of the program which died. The format 'program.core' was chosen so that core images would not be confused with files such as 'core.c' or 'core.h', etc. If the format 'core.program' was used and the 'program' was 'c' or 'h' it would be possible to overwrite an existing source file. Also, doing a 'find / -name 'core.* -print' would print out non coredump files. The 'gcore' program was updated with a couple new capabilities: 1) -s stops the process by sending a SIGSTOP, the coredump is created and the process is continued with a SIGCONT. 2) -c allows the specification of a different/explicit filename. This can be used to override the .core or .core name. Only one process may be gcore'd at a time now however. The man page for 'gcore' was also updated to reflect the changes to the 'gcore' program. To apply this patch, save the text below to /tmp/foo, then: 1) patch -p0 < /tmp/foo 2) cd /usr/src/ucb 3) make gcore 4) install -s -m 2755 -g kmem gcore /usr/ucb/gcore 5) cd /usr/src/man/man1 6) /usr/man/manroff gcore.1 > /usr/man/cat1/gcore.0 7) Recompile the kernel - cd /sys/ make make install reboot =======cut here *** /usr/src/sys/sys/kern_sig.c.old Sat Feb 5 19:45:52 1994 --- /usr/src/sys/sys/kern_sig.c Fri Apr 15 22:17:13 1994 *************** *** 3,9 **** * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * ! * @(#)kern_sig.c 1.3 (2.11BSD GTE) 12/31/93 */ #include "param.h" --- 3,9 ---- * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * ! * @(#)kern_sig.c 1.4 (2.11BSD GTE) 4/15/94 */ #include "param.h" *************** *** 802,807 **** --- 802,809 ---- { register struct inode *ip; register struct nameidata *ndp = &u.u_nd; + register char *np; + char *cp, name[MAXCOMLEN + 6]; if (u.u_uid != u.u_ruid || u.u_gid != u.u_rgid) return (0); *************** *** 810,819 **** return (0); if (u.u_procp->p_textp && access(u.u_procp->p_textp->x_iptr, IREAD)) return (0); u.u_error = 0; ndp->ni_nameiop = CREATE | FOLLOW; ndp->ni_segflg = UIO_SYSSPACE; ! ndp->ni_dirp = "core"; ip = namei(ndp); if (ip == NULL) { if (u.u_error) --- 812,829 ---- return (0); if (u.u_procp->p_textp && access(u.u_procp->p_textp->x_iptr, IREAD)) return (0); + cp = u.u_comm; + np = name; + while (*np++ = *cp++) + ; + cp = ".core"; + np--; + while (*np++ = *cp++) + ; u.u_error = 0; ndp->ni_nameiop = CREATE | FOLLOW; ndp->ni_segflg = UIO_SYSSPACE; ! ndp->ni_dirp = name; ip = namei(ndp); if (ip == NULL) { if (u.u_error) *** /usr/src/man/man1/gcore.1.old Thu Dec 10 19:28:37 1987 --- /usr/src/man/man1/gcore.1 Fri Apr 15 23:14:03 1994 *************** *** 2,24 **** .\" All rights reserved. The Berkeley software License Agreement .\" specifies the terms and conditions for redistribution. .\" ! .\" @(#)gcore.1 6.1 (Berkeley) 4/29/85 .\" ! .TH GCORE 1 "April 29, 1985" .UC 5 .SH NAME ! gcore \- get core images of running processes .SH SYNOPSIS .B gcore ! process-id ... .SH DESCRIPTION ! .I Gcore creates a core image of each specified process, suitable for use with .IR adb (1). .SH FILES ! core. core images .SH BUGS ! For best results, the desired processes should be stopped. .PP ! Swapped out processes may not be gcore'd. --- 2,41 ---- .\" All rights reserved. The Berkeley software License Agreement .\" specifies the terms and conditions for redistribution. .\" ! .\" @(#)gcore.1 1.1 (2.11BSD GTE) 4/15/94 .\" ! .TH GCORE 1 "April 15, 1994" .UC 5 .SH NAME ! gcore \- get core image of running process .SH SYNOPSIS .B gcore ! [\fB-s\fP][\fB-c core\fP] \fIpid\fP .SH DESCRIPTION ! .I gcore creates a core image of each specified process, suitable for use with .IR adb (1). + By default the core image is written to the file \fI.core\fP. + .PP + The options are: + .TP + \fB-c\fP + Write the core file to the specified file instead of \fI.core\fP. + .TP + \fB-s\fP + Stop the process while creating the core image and resume it when done. + This makes sure that the core dump will be in a consistent state. The + process is resumed even if it was already stopped. Of course, you can + obtain the same result by manually stopping the process with kill(1). + .PP + The core image name was changed from \fIcore.\fP to \fI.core\fP + to prevent matching names like \fIcore.h\fP and \fIcore.c\fP when using + programs such as \fIfind(1)\fP. .SH FILES ! .core The core image. .SH BUGS ! If \fBgcore\fP encounters an error while creating the core image and ! the \fB-s\fP option was used the process will remain stopped. .PP ! Swapped out processes and system processes (the swapper) may not be gcore'd. *** /usr/src/ucb/gcore.c.old Thu Jan 6 22:43:49 1994 --- /usr/src/ucb/gcore.c Fri Apr 15 21:28:50 1994 *************** *** 1,4 **** --- 1,10 ---- /* + * Rewritten to look like the more modern versions of gcore on other systems. + * The -s option for stopping the process was added, the ability to name + * the core file was added, and to follow changes to the kernel the default + * corefile name was changed to be 'pid.core' rather than simply 'core'. + * Only one process may be gcore'd at a time now however. 4/15/94 - sms. + * * Originally written for V7 Unix, later changed to handle 2.9BSD, later * still brought up on 2.10BSD. Pretty close the 4.XBSD except for * handling swapped out processes (it doesn't). - sms *************** *** 10,15 **** --- 16,22 ---- #include #include #include + #include #define NLIST "/vmunix" #define MEM "/dev/mem" *************** *** 21,107 **** #define X_NPROC 1 { 0 }, }; - int mem, cor, nproc; - struct proc *pbuf; main(argc, argv) int argc; char **argv; { ! register int pid; ! off_t lseek(); ! char *malloc(); ! if (argc < 2) { ! printf("Usage: %s pid ...\n", argv[0]); ! exit(1); ! } openfiles(); getkvars(); (void)lseek(mem, (long)nl[X_NPROC].n_value, L_SET); (void)read(mem, &nproc, sizeof nproc); pbuf = (struct proc *)calloc(nproc, sizeof (struct proc)); ! if (pbuf == NULL) { ! puts("Not enough core"); ! exit(1); ! } (void)lseek(mem, (long)nl[X_PROC].n_value, L_SET); (void)read(mem, pbuf, nproc * sizeof (struct proc)); ! while (--argc) { ! if ((pid = atoi(*++argv)) <= 0) ! continue; ! printf("%d: ", pid); ! prcdmp(pid); ! } exit(0); } ! prcdmp(pid) register int pid; { register int i; register struct proc *pp; uid_t uid, getuid(); ! char coref[MAXNAMLEN + 1], ubuf[USIZE*64]; ! off_t lseek(); for (i = 0, pp = pbuf;; pp++) { if (pp->p_pid == pid) break; ! if (i++ == nproc) { ! printf("Process not found.\n"); ! return; ! } } ! if (pp->p_uid != (uid = getuid()) && uid != 0) { ! printf("Not owner.\n"); ! return; ! } ! if ((pp->p_flag & SLOAD) == 0) { ! printf("Swapped out.\n"); ! return; ! } ! if (pp->p_stat == SZOMB) { ! printf("Zombie.\n"); ! return; ! } ! if (pp->p_flag & SSYS) { ! printf("System process.\n"); ! /* i.e. swapper or pagedaemon */ ! return; ! } ! if (lseek(mem, (long)pp->p_addr << 6, L_SET) < 0) { ! puts("Bad mem seek"); ! return; ! } ! if (read(mem, &ubuf, sizeof ubuf) != sizeof ubuf) { ! puts("Bad mem read"); ! return; ! } ! (void)sprintf(coref, "core.%d", pid); ! if ((cor = creat(coref, 0666)) < 0) { ! perror(coref); exit(1); } (void)write(cor, &ubuf, sizeof ubuf); --- 28,126 ---- #define X_NPROC 1 { 0 }, }; + int mem, cor, nproc, sflag; + struct proc *pbuf; + char *corefile, *program_name; + + extern int optind, opterr; + extern char *optarg, *rindex(); + extern off_t lseek(); + main(argc, argv) int argc; char **argv; { ! register int pid, c; ! char fname[32]; ! if (program_name = rindex(argv[0], '/')) ! program_name++; ! else ! program_name = argv[0]; ! ! opterr = 0; ! while ((c = getopt(argc, argv, "c:s")) != EOF) ! { ! switch (c) ! { ! case 'c': ! corefile = optarg; ! break; ! case 's': ! sflag++; ! break; ! default: ! usage(); ! break; ! } ! } ! argv += optind; ! argc -= optind; ! if (argc != 1) ! usage(); ! pid = atoi(argv[0]); ! if (corefile == 0) ! { ! sprintf(fname, "%d.core", pid); ! corefile = fname; ! } ! openfiles(); getkvars(); (void)lseek(mem, (long)nl[X_NPROC].n_value, L_SET); (void)read(mem, &nproc, sizeof nproc); pbuf = (struct proc *)calloc(nproc, sizeof (struct proc)); ! if (pbuf == NULL) ! error("not enough core"); (void)lseek(mem, (long)nl[X_PROC].n_value, L_SET); (void)read(mem, pbuf, nproc * sizeof (struct proc)); ! core(pid); exit(0); } ! void ! core(pid) register int pid; { register int i; register struct proc *pp; uid_t uid, getuid(); ! char ubuf[USIZE*64]; for (i = 0, pp = pbuf;; pp++) { if (pp->p_pid == pid) break; ! if (i++ == nproc) ! error("%d: not found", pid); } ! if (pp->p_uid != (uid = getuid()) && uid != 0) ! error("%d: not owner", pid); ! if ((pp->p_flag & SLOAD) == 0) ! error("%d: swapped out", pid); ! if (pp->p_stat == SZOMB) ! error("%d: zombie", pid); ! if (pp->p_flag & SSYS) ! error("%d: system process", pid); ! if (sflag && kill(pid, SIGSTOP) < 0) ! warning("%d: could not send stop signal", pid); ! if (lseek(mem, (long)pp->p_addr << 6, L_SET) < 0) ! error("bad mem seek"); ! if (read(mem, &ubuf, sizeof ubuf) != sizeof ubuf) ! error("bad mem read"); ! if ((cor = open(corefile, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) { ! perror(corefile); exit(1); } (void)write(cor, &ubuf, sizeof ubuf); *************** *** 109,132 **** dump(pp->p_dsize); (void)lseek(mem, (long)pp->p_saddr << 6, L_SET); dump(pp->p_ssize); (void)close(cor); - printf("%s dumped\n", coref); } dump(size) register u_int size; { register int blocks, i; int bytes; ! char buffer[BUFSIZ]; size <<= 6; ! blocks = size / BUFSIZ; ! bytes = size % BUFSIZ; for (i = 0; i < blocks; i++) { ! (void)read(mem, buffer, BUFSIZ); ! (void)write(cor, buffer, BUFSIZ); } if (bytes) { (void)read(mem, buffer, bytes); --- 128,154 ---- dump(pp->p_dsize); (void)lseek(mem, (long)pp->p_saddr << 6, L_SET); dump(pp->p_ssize); + + if (sflag && kill(pid, SIGCONT) < 0) + warning("%d: could not send continue signal", pid); (void)close(cor); } + void dump(size) register u_int size; { register int blocks, i; int bytes; ! char buffer[BUFSIZ * 2]; size <<= 6; ! blocks = size / sizeof (buffer); ! bytes = size % sizeof (buffer); for (i = 0; i < blocks; i++) { ! (void)read(mem, buffer, sizeof (buffer)); ! (void)write(cor, buffer, sizeof (buffer)); } if (bytes) { (void)read(mem, buffer, bytes); *************** *** 134,139 **** --- 156,162 ---- } } + void openfiles() { mem = open(MEM, 0); *************** *** 143,153 **** } } getkvars() { nlist(NLIST, nl); ! if (nl[0].n_type == 0) { ! printf("%s: No namelist\n", NLIST); ! exit(1); ! } } --- 166,228 ---- } } + void getkvars() { nlist(NLIST, nl); ! if (nl[0].n_type == 0) ! error("%s: no namelist\n", NLIST); } + + void + usage() + { + fprintf(stderr, "usage: %s [ -s ] [ -c core ] pid\n", program_name); + exit(1); + } + + /* VARARGS */ + void + error(va_alist) + va_dcl + { + va_list ap; + register char *cp; + + (void)fprintf(stderr, "%s: ", program_name); + + va_start(ap); + cp = va_arg(ap, char *); + (void)vfprintf(stderr, cp, ap); + va_end(ap); + if (*cp) + { + cp += strlen(cp); + if (cp[-1] != '\n') + (void)fputc('\n', stderr); + } + exit(1); + /* NOTREACHED */ + } + + /* VARARGS */ + void + warning(va_alist) + va_dcl + { + va_list ap; + register char *cp; + + (void)fprintf(stderr, "%s: warning: ", program_name); + + va_start(ap); + cp = va_arg(ap, char *); + (void)vfprintf(stderr, cp, ap); + va_end(ap); + if (*cp) + { + cp += strlen(cp); + if (cp[-1] != '\n') + (void)fputc('\n', stderr); + } + }