xref: /openbsd-src/usr.bin/sendbug/sendbug.c (revision d23b33027f43b018be6f236512bdd1f04ac3e5ce)
1*d23b3302Sray /*	$OpenBSD: sendbug.c,v 1.34 2007/03/28 04:05:52 ray Exp $	*/
25ae6585fSray 
35ae6585fSray /*
45ae6585fSray  * Written by Ray Lai <ray@cyth.net>.
55ae6585fSray  * Public domain.
65ae6585fSray  */
75ae6585fSray 
85ae6585fSray #include <sys/types.h>
95ae6585fSray #include <sys/mman.h>
105ae6585fSray #include <sys/param.h>
115ae6585fSray #include <sys/stat.h>
125ae6585fSray #include <sys/sysctl.h>
135ae6585fSray #include <sys/wait.h>
145ae6585fSray 
1549d07c37Sray #include <ctype.h>
165ae6585fSray #include <err.h>
175ae6585fSray #include <errno.h>
185ae6585fSray #include <fcntl.h>
195ae6585fSray #include <limits.h>
205ae6585fSray #include <paths.h>
215ae6585fSray #include <pwd.h>
224b9386a8Sderaadt #include <signal.h>
235ae6585fSray #include <stdio.h>
245ae6585fSray #include <stdlib.h>
255ae6585fSray #include <string.h>
265ae6585fSray #include <unistd.h>
275ae6585fSray 
285ae6585fSray #include "atomicio.h"
295ae6585fSray 
304b9386a8Sderaadt int	editit(char *);
31b2e65daaStedu void	init(void);
325ae6585fSray int	prompt(void);
330690c094Sray int	send_file(const char *, int);
345ae6585fSray int	sendmail(const char *);
355ae6585fSray void	template(FILE *);
365ae6585fSray 
375ae6585fSray const char *categories = "system user library documentation ports kernel "
385ae6585fSray     "alpha amd64 arm i386 m68k m88k mips ppc sgi sparc sparc64 vax";
39823e89efSderaadt char *version = "4.2";
40823e89efSderaadt 
41823e89efSderaadt struct passwd *pw;
42e051e8faSderaadt char os[BUFSIZ], rel[BUFSIZ], mach[BUFSIZ], details[BUFSIZ];
436b1a1e2aSderaadt char *fullname, *tmppath;
44b2e65daaStedu int wantcleanup;
45823e89efSderaadt 
46b2e65daaStedu __dead void
47903e61cbSderaadt usage(void)
48903e61cbSderaadt {
49994a1f98Sderaadt 	fprintf(stderr, "usage: sendbug [-LPV]\n");
50b2e65daaStedu 	exit(1);
51903e61cbSderaadt }
52903e61cbSderaadt 
53b2e65daaStedu void
54b2e65daaStedu cleanup()
55b2e65daaStedu {
56b2e65daaStedu 	if (wantcleanup && tmppath && unlink(tmppath) == -1)
57b2e65daaStedu 		warn("unlink");
58b2e65daaStedu }
59b2e65daaStedu 
60b2e65daaStedu 
615ae6585fSray int
625ae6585fSray main(int argc, char *argv[])
635ae6585fSray {
64903e61cbSderaadt 	int ch, c, fd, ret = 1;
65f4348cb9Sderaadt 	const char *tmpdir;
6670dfcc75Sderaadt 	struct stat sb;
67f4348cb9Sderaadt 	char *pr_form;
6870dfcc75Sderaadt 	time_t mtime;
6970dfcc75Sderaadt 	FILE *fp;
705ae6585fSray 
715711e205Sderaadt 	while ((ch = getopt(argc, argv, "LPV")) != -1)
72903e61cbSderaadt 		switch (ch) {
73903e61cbSderaadt 		case 'L':
74903e61cbSderaadt 			printf("Known categories:\n");
75903e61cbSderaadt 			printf("%s\n\n", categories);
76903e61cbSderaadt 			exit(0);
77903e61cbSderaadt 		case 'P':
78b2e65daaStedu 			init();
79903e61cbSderaadt 			template(stdout);
80903e61cbSderaadt 			exit(0);
815711e205Sderaadt 		case 'V':
825711e205Sderaadt 			printf("%s\n", version);
835711e205Sderaadt 			exit(0);
84903e61cbSderaadt 		default:
85903e61cbSderaadt 			usage();
86903e61cbSderaadt 		}
87903e61cbSderaadt 
88903e61cbSderaadt 	if (argc > 1) {
89903e61cbSderaadt 		usage();
90903e61cbSderaadt 	}
91903e61cbSderaadt 
925ae6585fSray 	if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
935ae6585fSray 		tmpdir = _PATH_TMP;
94795a8b97Sray 	if (asprintf(&tmppath, "%s%sp.XXXXXXXXXX", tmpdir,
95f4348cb9Sderaadt 	    tmpdir[strlen(tmpdir) - 1] == '/' ? "" : "/") == -1)
96b2e65daaStedu 		err(1, "asprintf");
975ae6585fSray 	if ((fd = mkstemp(tmppath)) == -1)
985ae6585fSray 		err(1, "mkstemp");
99b2e65daaStedu 	wantcleanup = 1;
100b2e65daaStedu 	atexit(cleanup);
101f4348cb9Sderaadt 	if ((fp = fdopen(fd, "w+")) == NULL)
102b2e65daaStedu 		err(1, "fdopen");
1035ae6585fSray 
104b2e65daaStedu 	init();
1055ae6585fSray 
1064ff3398aSderaadt 	pr_form = getenv("PR_FORM");
1074ff3398aSderaadt 	if (pr_form) {
1084ff3398aSderaadt 		char buf[BUFSIZ];
1094ff3398aSderaadt 		size_t len;
1104ff3398aSderaadt 		FILE *frfp;
1114ff3398aSderaadt 
1124ff3398aSderaadt 		frfp = fopen(pr_form, "r");
1134ff3398aSderaadt 		if (frfp == NULL) {
1144ff3398aSderaadt 			fprintf(stderr, "sendbug: can't seem to read your"
1154ff3398aSderaadt 			    " template file (`%s'), ignoring PR_FORM\n",
1164ff3398aSderaadt 			    pr_form);
1174ff3398aSderaadt 			template(fp);
1184ff3398aSderaadt 		} else {
1194ff3398aSderaadt 			while (!feof(frfp)) {
1204ff3398aSderaadt 				len = fread(buf, 1, sizeof buf, frfp);
1214ff3398aSderaadt 				if (len == 0)
1224ff3398aSderaadt 					break;
1234ff3398aSderaadt 				if (fwrite(buf, 1, len, fp) != len)
1244ff3398aSderaadt 					break;
1254ff3398aSderaadt 			}
1264ff3398aSderaadt 			fclose(frfp);
1274ff3398aSderaadt 		}
1284ff3398aSderaadt 	} else
1295ae6585fSray 		template(fp);
1305ae6585fSray 
131f4348cb9Sderaadt 	if (fflush(fp) == EOF || fstat(fd, &sb) == -1 || fclose(fp) == EOF)
132b2e65daaStedu 		err(1, "error creating template");
1335ae6585fSray 	mtime = sb.st_mtime;
1345ae6585fSray 
1355ae6585fSray  edit:
136648f0ffcSray 	if (editit(tmppath) == -1 && errno != ECHILD)
137648f0ffcSray 		err(1, "error running editor");
1385ae6585fSray 
139f4348cb9Sderaadt 	if (stat(tmppath, &sb) == -1)
140b2e65daaStedu 		err(1, "stat");
141f4348cb9Sderaadt 	if (mtime == sb.st_mtime)
142b2e65daaStedu 		errx(1, "report unchanged, nothing sent");
1435ae6585fSray 
1445ae6585fSray  prompt:
1455ae6585fSray 	c = prompt();
1465ae6585fSray 	switch (c) {
1475711e205Sderaadt 	case 'a':
1485711e205Sderaadt 	case EOF:
149b2e65daaStedu 		wantcleanup = 0;
150b2e65daaStedu 		errx(1, "unsent report in %s", tmppath);
1515ae6585fSray 	case 'e':
1525ae6585fSray 		goto edit;
1535ae6585fSray 	case 's':
1545ae6585fSray 		if (sendmail(tmppath) == -1)
1555ae6585fSray 			goto quit;
1565ae6585fSray 		break;
1575ae6585fSray 	default:
1585ae6585fSray 		goto prompt;
1595ae6585fSray 	}
1605ae6585fSray 
1615ae6585fSray 	ret = 0;
1625ae6585fSray quit:
1635ae6585fSray 	return (ret);
1645ae6585fSray }
1655ae6585fSray 
1664b9386a8Sderaadt int
1674b9386a8Sderaadt editit(char *tmpfile)
1684b9386a8Sderaadt {
169f4348cb9Sderaadt 	char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p;
1708ec2d82eSray 	sig_t sighup, sigint, sigquit;
1714b9386a8Sderaadt 	pid_t pid, xpid;
1726b1a1e2aSderaadt 	int st;
1734b9386a8Sderaadt 
174c8a426adSray 	ed = getenv("VISUAL");
175c8a426adSray 	if (ed == NULL || ed[0] == '\0')
176c8a426adSray 		ed = getenv("EDITOR");
177c8a426adSray 	if (ed == NULL || ed[0] == '\0')
1784b9386a8Sderaadt 		ed = _PATH_VI;
1794b9386a8Sderaadt 	if (asprintf(&p, "%s %s", ed, tmpfile) == -1)
1808af53ff7Sray 		return (-1);
1814b9386a8Sderaadt 	argp[2] = p;
1824b9386a8Sderaadt 
1834b9386a8Sderaadt  top:
1848ec2d82eSray 	sighup = signal(SIGHUP, SIG_IGN);
1858ec2d82eSray 	sigint = signal(SIGINT, SIG_IGN);
1868ec2d82eSray 	sigquit = signal(SIGQUIT, SIG_IGN);
1877132776cSray 	if ((pid = fork()) == -1) {
1883c361d1bSray 		int saved_errno = errno;
1893c361d1bSray 
1908ec2d82eSray 		(void)signal(SIGHUP, sighup);
1918ec2d82eSray 		(void)signal(SIGINT, sigint);
1928ec2d82eSray 		(void)signal(SIGQUIT, sigquit);
1933c361d1bSray 		if (saved_errno == EAGAIN) {
1944b9386a8Sderaadt 			sleep(1);
1954b9386a8Sderaadt 			goto top;
1964b9386a8Sderaadt 		}
1974b9386a8Sderaadt 		free(p);
1982bba59c0Sray 		errno = saved_errno;
1998af53ff7Sray 		return (-1);
2004b9386a8Sderaadt 	}
2014b9386a8Sderaadt 	if (pid == 0) {
2024b9386a8Sderaadt 		execv(_PATH_BSHELL, argp);
2034b9386a8Sderaadt 		_exit(127);
2044b9386a8Sderaadt 	}
2054b9386a8Sderaadt 	free(p);
2064b9386a8Sderaadt 	for (;;) {
207ecdc3c8bSray 		xpid = waitpid(pid, &st, WUNTRACED);
2086b1a1e2aSderaadt 		if (xpid == -1) {
2092bba59c0Sray 			if (errno != EINTR)
2106b1a1e2aSderaadt 				return (-1);
2116b1a1e2aSderaadt 		} else if (WIFSTOPPED(st))
2126b1a1e2aSderaadt 			raise(WSTOPSIG(st));
213f27d80daSray 		else
2144b9386a8Sderaadt 			break;
2154b9386a8Sderaadt 	}
2168ec2d82eSray 	(void)signal(SIGHUP, sighup);
2178ec2d82eSray 	(void)signal(SIGINT, sigint);
2188ec2d82eSray 	(void)signal(SIGQUIT, sigquit);
2192bba59c0Sray 	if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) {
2202bba59c0Sray 		errno = ECHILD;
2218af53ff7Sray 		return (-1);
2222bba59c0Sray 	}
2234b9386a8Sderaadt 	return (0);
2244b9386a8Sderaadt }
225b2e65daaStedu 
2265ae6585fSray int
2275ae6585fSray prompt(void)
2285ae6585fSray {
2295ae6585fSray 	int c, ret;
2305ae6585fSray 
2315ae6585fSray 	fpurge(stdin);
2325ae6585fSray 	fprintf(stderr, "a)bort, e)dit, or s)end: ");
2335ae6585fSray 	fflush(stderr);
2345ae6585fSray 	ret = getchar();
2355ae6585fSray 	if (ret == EOF || ret == '\n')
2365ae6585fSray 		return (ret);
2375ae6585fSray 	do {
2385ae6585fSray 		c = getchar();
2395ae6585fSray 	} while (c != EOF && c != '\n');
2405ae6585fSray 	return (ret);
2415ae6585fSray }
2425ae6585fSray 
2435ae6585fSray int
2445ae6585fSray sendmail(const char *tmppath)
2455ae6585fSray {
2465ae6585fSray 	int filedes[2];
2475ae6585fSray 
2485ae6585fSray 	if (pipe(filedes) == -1) {
2495ae6585fSray 		warn("pipe: unsent report in %s", tmppath);
2505ae6585fSray 		return (-1);
2515ae6585fSray 	}
2525ae6585fSray 	switch (fork()) {
2535ae6585fSray 	case -1:
2545ae6585fSray 		warn("fork error: unsent report in %s",
2555ae6585fSray 		    tmppath);
2565ae6585fSray 		return (-1);
2575ae6585fSray 	case 0:
2585ae6585fSray 		close(filedes[1]);
2595ae6585fSray 		if (dup2(filedes[0], STDIN_FILENO) == -1) {
2605ae6585fSray 			warn("dup2 error: unsent report in %s",
2615ae6585fSray 			    tmppath);
2625ae6585fSray 			return (-1);
2635ae6585fSray 		}
2645ae6585fSray 		close(filedes[0]);
2655ae6585fSray 		execl("/usr/sbin/sendmail", "sendmail",
26670dfcc75Sderaadt 		    "-oi", "-t", (void *)NULL);
2675ae6585fSray 		warn("sendmail error: unsent report in %s",
2685ae6585fSray 		    tmppath);
2695ae6585fSray 		return (-1);
2705ae6585fSray 	default:
2715ae6585fSray 		close(filedes[0]);
2725ae6585fSray 		/* Pipe into sendmail. */
2735ae6585fSray 		if (send_file(tmppath, filedes[1]) == -1) {
2745ae6585fSray 			warn("send_file error: unsent report in %s",
2755ae6585fSray 			    tmppath);
2765ae6585fSray 			return (-1);
2775ae6585fSray 		}
2785ae6585fSray 		close(filedes[1]);
2795ae6585fSray 		wait(NULL);
2805ae6585fSray 		break;
2815ae6585fSray 	}
2825ae6585fSray 	return (0);
2835ae6585fSray }
2845ae6585fSray 
285b2e65daaStedu void
2865ae6585fSray init(void)
2875ae6585fSray {
288b088fbb0Sray 	size_t amp, len, gecoslen, namelen;
289f4348cb9Sderaadt 	int sysname[2];
290b088fbb0Sray 	char ch, *cp;
2915ae6585fSray 
292f4348cb9Sderaadt 	if ((pw = getpwuid(getuid())) == NULL)
293b2e65daaStedu 		err(1, "getpwuid");
29449d07c37Sray 	namelen = strlen(pw->pw_name);
2955ae6585fSray 
2969a66796cSmoritz 	/* Count number of '&'. */
297b088fbb0Sray 	for (amp = 0, cp = pw->pw_gecos; *cp && *cp != ','; ++cp)
298b088fbb0Sray 		if (*cp == '&')
2999a66796cSmoritz 			++amp;
300b088fbb0Sray 
301b088fbb0Sray 	/* Truncate gecos to full name. */
302b088fbb0Sray 	gecoslen = cp - pw->pw_gecos;
303b088fbb0Sray 	pw->pw_gecos[gecoslen] = '\0';
304b088fbb0Sray 
3059a66796cSmoritz 	/* Expanded str = orig str - '&' chars + concatenated logins. */
306b088fbb0Sray 	len = gecoslen - amp + (amp * namelen) + 1;
307b088fbb0Sray 	if ((fullname = malloc(len)) == NULL)
308b2e65daaStedu 		err(1, "malloc");
309f4348cb9Sderaadt 
310b088fbb0Sray 	/* Upper case first char of login. */
311b088fbb0Sray 	ch = pw->pw_name[0];
312b088fbb0Sray 	pw->pw_name[0] = toupper((unsigned char)pw->pw_name[0]);
313b088fbb0Sray 
314b088fbb0Sray 	cp = pw->pw_gecos;
315b088fbb0Sray 	fullname[0] = '\0';
316b088fbb0Sray 	while (cp != NULL) {
317b088fbb0Sray 		char *token;
318b088fbb0Sray 
319b088fbb0Sray 		token = strsep(&cp, "&");
320b088fbb0Sray 		if (token != pw->pw_gecos &&
321b088fbb0Sray 		    strlcat(fullname, pw->pw_name, len) >= len)
322b088fbb0Sray 			errx(1, "truncated string");
323b088fbb0Sray 		if (strlcat(fullname, token, len) >= len)
324b088fbb0Sray 			errx(1, "truncated string");
32549d07c37Sray 	}
326b088fbb0Sray 	/* Restore case of first char of login. */
327b088fbb0Sray 	pw->pw_name[0] = ch;
3285ae6585fSray 
3295ae6585fSray 	sysname[0] = CTL_KERN;
3305ae6585fSray 	sysname[1] = KERN_OSTYPE;
3315ae6585fSray 	len = sizeof(os) - 1;
332f4348cb9Sderaadt 	if (sysctl(sysname, 2, &os, &len, NULL, 0) == -1)
333b2e65daaStedu 		err(1, "sysctl");
3345ae6585fSray 
3355ae6585fSray 	sysname[0] = CTL_KERN;
3365ae6585fSray 	sysname[1] = KERN_OSRELEASE;
3375ae6585fSray 	len = sizeof(rel) - 1;
338f4348cb9Sderaadt 	if (sysctl(sysname, 2, &rel, &len, NULL, 0) == -1)
339b2e65daaStedu 		err(1, "sysctl");
3405ae6585fSray 
341e051e8faSderaadt 	sysname[0] = CTL_KERN;
342e051e8faSderaadt 	sysname[1] = KERN_VERSION;
343e051e8faSderaadt 	len = sizeof(details) - 1;
344f4348cb9Sderaadt 	if (sysctl(sysname, 2, &details, &len, NULL, 0) == -1)
345e051e8faSderaadt 		err(1, "sysctl");
346e051e8faSderaadt 
347e051e8faSderaadt 	cp = strchr(details, '\n');
348e051e8faSderaadt 	if (cp) {
349e051e8faSderaadt 		cp++;
350e051e8faSderaadt 		if (*cp)
351e051e8faSderaadt 			*cp++ = '\t';
352e051e8faSderaadt 		if (*cp)
353e051e8faSderaadt 			*cp++ = '\t';
354e051e8faSderaadt 		if (*cp)
355e051e8faSderaadt 			*cp++ = '\t';
356e051e8faSderaadt 	}
357e051e8faSderaadt 
3585ae6585fSray 	sysname[0] = CTL_HW;
3595ae6585fSray 	sysname[1] = HW_MACHINE;
3605ae6585fSray 	len = sizeof(mach) - 1;
361f4348cb9Sderaadt 	if (sysctl(sysname, 2, &mach, &len, NULL, 0) == -1)
362b2e65daaStedu 		err(1, "sysctl");
3635ae6585fSray }
3645ae6585fSray 
3655ae6585fSray int
3665ae6585fSray send_file(const char *file, int dst)
3675ae6585fSray {
3685ae6585fSray 	int blank = 0;
36970dfcc75Sderaadt 	size_t len;
37070dfcc75Sderaadt 	char *buf;
37170dfcc75Sderaadt 	FILE *fp;
3725ae6585fSray 
3735ae6585fSray 	if ((fp = fopen(file, "r")) == NULL)
3745ae6585fSray 		return (-1);
3755ae6585fSray 	while ((buf = fgetln(fp, &len))) {
3765ae6585fSray 		/* Skip lines starting with "SENDBUG". */
3775ae6585fSray 		if (len >= sizeof("SENDBUG") - 1 &&
3785ae6585fSray 		    memcmp(buf, "SENDBUG", sizeof("SENDBUG") - 1) == 0)
3795ae6585fSray 			continue;
3805ae6585fSray 		if (len == 1 && buf[0] == '\n')
3815ae6585fSray 			blank = 1;
3825ae6585fSray 		/* Skip comments, but only if we encountered a blank line. */
3835ae6585fSray 		while (len) {
384e051e8faSderaadt 			char *sp = NULL, *ep = NULL;
3855ae6585fSray 			size_t copylen;
3865ae6585fSray 
3875ae6585fSray 			if (blank && (sp = memchr(buf, '<', len)) != NULL)
3885ae6585fSray 				ep = memchr(sp, '>', len - (sp - buf + 1));
3895ae6585fSray 			/* Length of string before comment. */
3908cf86b71Sray 			if (ep)
3918cf86b71Sray 				copylen = sp - buf;
3928cf86b71Sray 			else
3938cf86b71Sray 				copylen = len;
3945ae6585fSray 			if (atomicio(vwrite, dst, buf, copylen) != copylen) {
3955ae6585fSray 				int saved_errno = errno;
3965ae6585fSray 
3975ae6585fSray 				fclose(fp);
3985ae6585fSray 				errno = saved_errno;
3995ae6585fSray 				return (-1);
4005ae6585fSray 			}
4015ae6585fSray 			if (!ep)
4025ae6585fSray 				break;
4035ae6585fSray 			/* Skip comment. */
4045ae6585fSray 			len -= ep - buf + 1;
4055ae6585fSray 			buf = ep + 1;
4065ae6585fSray 		}
4075ae6585fSray 	}
4085ae6585fSray 	fclose(fp);
4095ae6585fSray 	return (0);
4105ae6585fSray }
4115ae6585fSray 
4125ae6585fSray void
4135ae6585fSray template(FILE *fp)
4145ae6585fSray {
4155ae6585fSray 	fprintf(fp, "SENDBUG: -*- sendbug -*-\n");
416f4348cb9Sderaadt 	fprintf(fp, "SENDBUG: Lines starting with `SENDBUG' will"
417f4348cb9Sderaadt 	    " be removed automatically, as\n");
4185ae6585fSray 	fprintf(fp, "SENDBUG: will all comments (text enclosed in `<' and `>').\n");
4195ae6585fSray 	fprintf(fp, "SENDBUG:\n");
4205ae6585fSray 	fprintf(fp, "SENDBUG: Choose from the following categories:\n");
4215ae6585fSray 	fprintf(fp, "SENDBUG:\n");
4225ae6585fSray 	fprintf(fp, "SENDBUG: %s\n", categories);
4235ae6585fSray 	fprintf(fp, "SENDBUG:\n");
4245ae6585fSray 	fprintf(fp, "SENDBUG:\n");
4255ae6585fSray 	fprintf(fp, "To: %s\n", "gnats@openbsd.org");
4265ae6585fSray 	fprintf(fp, "Subject: \n");
4275ae6585fSray 	fprintf(fp, "From: %s\n", pw->pw_name);
428*d23b3302Sray 	fprintf(fp, "Cc: %s\n", pw->pw_name);
4295ae6585fSray 	fprintf(fp, "Reply-To: %s\n", pw->pw_name);
4305711e205Sderaadt 	fprintf(fp, "X-sendbug-version: %s\n", version);
4315ae6585fSray 	fprintf(fp, "\n");
4325ae6585fSray 	fprintf(fp, "\n");
4335ae6585fSray 	fprintf(fp, ">Submitter-Id:\tnet\n");
4345ae6585fSray 	fprintf(fp, ">Originator:\t%s\n", fullname);
4355ae6585fSray 	fprintf(fp, ">Organization:\n");
4365ae6585fSray 	fprintf(fp, "net\n");
4375ae6585fSray 	fprintf(fp, ">Synopsis:\t<synopsis of the problem (one line)>\n");
438f4348cb9Sderaadt 	fprintf(fp, ">Severity:\t"
439f4348cb9Sderaadt 	    "<[ non-critical | serious | critical ] (one line)>\n");
4405ae6585fSray 	fprintf(fp, ">Priority:\t<[ low | medium | high ] (one line)>\n");
4415ae6585fSray 	fprintf(fp, ">Category:\t<PR category (one line)>\n");
442f4348cb9Sderaadt 	fprintf(fp, ">Class:\t\t"
443f4348cb9Sderaadt 	    "<[ sw-bug | doc-bug | change-request | support ] (one line)>\n");
4445ae6585fSray 	fprintf(fp, ">Release:\t<release number or tag (one line)>\n");
4455ae6585fSray 	fprintf(fp, ">Environment:\n");
4465ae6585fSray 	fprintf(fp, "\t<machine, os, target, libraries (multiple lines)>\n");
4475ae6585fSray 	fprintf(fp, "\tSystem      : %s %s\n", os, rel);
448e051e8faSderaadt 	fprintf(fp, "\tDetails     : %s\n", details);
4495ae6585fSray 	fprintf(fp, "\tArchitecture: %s.%s\n", os, mach);
4505ae6585fSray 	fprintf(fp, "\tMachine     : %s\n", mach);
4515ae6585fSray 	fprintf(fp, ">Description:\n");
4525ae6585fSray 	fprintf(fp, "\t<precise description of the problem (multiple lines)>\n");
4535ae6585fSray 	fprintf(fp, ">How-To-Repeat:\n");
454f4348cb9Sderaadt 	fprintf(fp, "\t<code/input/activities to reproduce the problem"
455f4348cb9Sderaadt 	    " (multiple lines)>\n");
4565ae6585fSray 	fprintf(fp, ">Fix:\n");
457f4348cb9Sderaadt 	fprintf(fp, "\t<how to correct or work around the problem,"
458f4348cb9Sderaadt 	    " if known (multiple lines)>\n");
4595ae6585fSray }
460