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 <pid>.core
		   or <program>.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/<YOURKERNELNAME>
		   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.<process-id>	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<pid>.core\fP.
+ .PP
+ The options are:
+ .TP
+ \fB-c\fP
+ Write the core file to the specified file instead of \fI<pid>.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.<pid>\fP to \fI<pid>.core\fP
+ to prevent matching names like \fIcore.h\fP and \fIcore.c\fP when using
+ programs such as \fIfind(1)\fP.
  .SH FILES
! <process-id>.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 <sys/file.h>
  #include <stdio.h>
  #include <nlist.h>
+ #include <varargs.h>
  
  #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);
+ 		}
+ 	}
