xref: /openbsd-src/usr.bin/sendbug/sendbug.c (revision 903e61cb73fdf298e9f24e65eac6e93de1e68ff7)
1*903e61cbSderaadt /*	$OpenBSD: sendbug.c,v 1.3 2007/03/23 02:28:14 deraadt 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 
155ae6585fSray #include <err.h>
165ae6585fSray #include <errno.h>
175ae6585fSray #include <fcntl.h>
185ae6585fSray #include <limits.h>
195ae6585fSray #include <paths.h>
205ae6585fSray #include <pwd.h>
215ae6585fSray #include <stdio.h>
225ae6585fSray #include <stdlib.h>
235ae6585fSray #include <string.h>
245ae6585fSray #include <unistd.h>
255ae6585fSray 
265ae6585fSray #include "atomicio.h"
275ae6585fSray 
285ae6585fSray int init(void);
295ae6585fSray int prompt(void);
305ae6585fSray int send_file(const char *, int dst);
315ae6585fSray int sendmail(const char *);
325ae6585fSray void template(FILE *);
335ae6585fSray 
345ae6585fSray struct passwd *pw;
355ae6585fSray const char *categories = "system user library documentation ports kernel "
365ae6585fSray     "alpha amd64 arm i386 m68k m88k mips ppc sgi sparc sparc64 vax";
375ae6585fSray char os[BUFSIZ], rel[BUFSIZ], mach[BUFSIZ];
385ae6585fSray char *fullname;
395ae6585fSray 
40*903e61cbSderaadt void
41*903e61cbSderaadt usage(void)
42*903e61cbSderaadt {
43*903e61cbSderaadt 	fprintf(stderr, "usage: sendbug [-LP]\n");
44*903e61cbSderaadt }
45*903e61cbSderaadt 
465ae6585fSray int
475ae6585fSray main(int argc, char *argv[])
485ae6585fSray {
495ae6585fSray 	const char *editor, *tmpdir;
505ae6585fSray 	char *tmppath = NULL;
51*903e61cbSderaadt 	int ch, c, fd, ret = 1;
5270dfcc75Sderaadt 	struct stat sb;
5370dfcc75Sderaadt 	time_t mtime;
5470dfcc75Sderaadt 	FILE *fp;
555ae6585fSray 
56*903e61cbSderaadt 	while ((ch = getopt(argc, argv, "LP")) != -1)
57*903e61cbSderaadt 		switch (ch) {
58*903e61cbSderaadt 		case 'L':
59*903e61cbSderaadt 			printf("Known categories:\n");
60*903e61cbSderaadt 			printf("%s\n\n", categories);
61*903e61cbSderaadt 			exit(0);
62*903e61cbSderaadt 		case 'P':
63*903e61cbSderaadt 			if (init() == -1)
64*903e61cbSderaadt 				exit(1);
65*903e61cbSderaadt 			template(stdout);
66*903e61cbSderaadt 			exit(0);
67*903e61cbSderaadt 		default:
68*903e61cbSderaadt 			usage();
69*903e61cbSderaadt 			exit(1);
70*903e61cbSderaadt 		}
71*903e61cbSderaadt 
72*903e61cbSderaadt 	if (argc > 1) {
73*903e61cbSderaadt 		usage();
74*903e61cbSderaadt 		exit(1);
75*903e61cbSderaadt 	}
76*903e61cbSderaadt 
775ae6585fSray 	if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
785ae6585fSray 		tmpdir = _PATH_TMP;
795ae6585fSray 	if (asprintf(&tmppath, "%s/p.XXXXXXXXXX", tmpdir) == -1) {
805ae6585fSray 		warn("asprintf");
815ae6585fSray 		goto quit;
825ae6585fSray 	}
835ae6585fSray 	if ((fd = mkstemp(tmppath)) == -1)
845ae6585fSray 		err(1, "mkstemp");
855ae6585fSray 	if ((fp = fdopen(fd, "w+")) == NULL) {
865ae6585fSray 		warn("fdopen");
875ae6585fSray 		goto cleanup;
885ae6585fSray 	}
895ae6585fSray 
905ae6585fSray 	if (init() == -1)
915ae6585fSray 		goto cleanup;
925ae6585fSray 
935ae6585fSray 	template(fp);
945ae6585fSray 
955ae6585fSray 	if (fflush(fp) == EOF || fstat(fd, &sb) == -1 || fclose(fp) == EOF) {
965ae6585fSray 		warn("error creating template");
975ae6585fSray 		goto cleanup;
985ae6585fSray 	}
995ae6585fSray 	mtime = sb.st_mtime;
1005ae6585fSray 
1015ae6585fSray  edit:
1025ae6585fSray 	if ((editor = getenv("EDITOR")) == NULL)
1035ae6585fSray 		editor = "vi";
1045ae6585fSray 	switch (fork()) {
1055ae6585fSray 	case -1:
1065ae6585fSray 		warn("fork");
1075ae6585fSray 		goto cleanup;
1085ae6585fSray 	case 0:
10970dfcc75Sderaadt 		execlp(editor, editor, tmppath, (void *)NULL);
1105ae6585fSray 		err(1, "execlp");
1115ae6585fSray 	default:
1125ae6585fSray 		wait(NULL);
1135ae6585fSray 		break;
1145ae6585fSray 	}
1155ae6585fSray 
1165ae6585fSray 	if (stat(tmppath, &sb) == -1) {
1175ae6585fSray 		warn("stat");
1185ae6585fSray 		goto cleanup;
1195ae6585fSray 	}
1205ae6585fSray 	if (mtime == sb.st_mtime) {
1215ae6585fSray 		warnx("report unchanged, nothing sent");
1225ae6585fSray 		goto cleanup;
1235ae6585fSray 	}
1245ae6585fSray 
1255ae6585fSray  prompt:
1265ae6585fSray 	c = prompt();
1275ae6585fSray 	switch (c) {
1285ae6585fSray 	case 'a': case EOF:
1295ae6585fSray 		warnx("unsent report in %s", tmppath);
1305ae6585fSray 		goto quit;
1315ae6585fSray 	case 'e':
1325ae6585fSray 		goto edit;
1335ae6585fSray 	case 's':
1345ae6585fSray 		if (sendmail(tmppath) == -1)
1355ae6585fSray 			goto quit;
1365ae6585fSray 		break;
1375ae6585fSray 	default:
1385ae6585fSray 		goto prompt;
1395ae6585fSray 	}
1405ae6585fSray 
1415ae6585fSray 	ret = 0;
1425ae6585fSray 
1435ae6585fSray  cleanup:
1445ae6585fSray 	if (tmppath && unlink(tmppath) == -1)
1455ae6585fSray 		warn("unlink");
1465ae6585fSray 
1475ae6585fSray  quit:
1485ae6585fSray 	return (ret);
1495ae6585fSray }
1505ae6585fSray 
1515ae6585fSray int
1525ae6585fSray prompt(void)
1535ae6585fSray {
1545ae6585fSray 	int c, ret;
1555ae6585fSray 
1565ae6585fSray 	fpurge(stdin);
1575ae6585fSray 	fprintf(stderr, "a)bort, e)dit, or s)end: ");
1585ae6585fSray 	fflush(stderr);
1595ae6585fSray 	ret = getchar();
1605ae6585fSray 	if (ret == EOF || ret == '\n')
1615ae6585fSray 		return (ret);
1625ae6585fSray 	do {
1635ae6585fSray 		c = getchar();
1645ae6585fSray 	} while (c != EOF && c != '\n');
1655ae6585fSray 	return (ret);
1665ae6585fSray }
1675ae6585fSray 
1685ae6585fSray int
1695ae6585fSray sendmail(const char *tmppath)
1705ae6585fSray {
1715ae6585fSray 	int filedes[2];
1725ae6585fSray 
1735ae6585fSray 	if (pipe(filedes) == -1) {
1745ae6585fSray 		warn("pipe: unsent report in %s", tmppath);
1755ae6585fSray 		return (-1);
1765ae6585fSray 	}
1775ae6585fSray 	switch (fork()) {
1785ae6585fSray 	case -1:
1795ae6585fSray 		warn("fork error: unsent report in %s",
1805ae6585fSray 		    tmppath);
1815ae6585fSray 		return (-1);
1825ae6585fSray 	case 0:
1835ae6585fSray 		close(filedes[1]);
1845ae6585fSray 		if (dup2(filedes[0], STDIN_FILENO) == -1) {
1855ae6585fSray 			warn("dup2 error: unsent report in %s",
1865ae6585fSray 			    tmppath);
1875ae6585fSray 			return (-1);
1885ae6585fSray 		}
1895ae6585fSray 		close(filedes[0]);
1905ae6585fSray 		execl("/usr/sbin/sendmail", "sendmail",
19170dfcc75Sderaadt 		    "-oi", "-t", (void *)NULL);
1925ae6585fSray 		warn("sendmail error: unsent report in %s",
1935ae6585fSray 		    tmppath);
1945ae6585fSray 		return (-1);
1955ae6585fSray 	default:
1965ae6585fSray 		close(filedes[0]);
1975ae6585fSray 		/* Pipe into sendmail. */
1985ae6585fSray 		if (send_file(tmppath, filedes[1]) == -1) {
1995ae6585fSray 			warn("send_file error: unsent report in %s",
2005ae6585fSray 			    tmppath);
2015ae6585fSray 			return (-1);
2025ae6585fSray 		}
2035ae6585fSray 		close(filedes[1]);
2045ae6585fSray 		wait(NULL);
2055ae6585fSray 		break;
2065ae6585fSray 	}
2075ae6585fSray 	return (0);
2085ae6585fSray }
2095ae6585fSray 
2105ae6585fSray int
2115ae6585fSray init(void)
2125ae6585fSray {
2135ae6585fSray 	size_t len;
2145ae6585fSray 	int sysname[2];
2155ae6585fSray 
2165ae6585fSray 	if ((pw = getpwuid(getuid())) == NULL) {
2175ae6585fSray 		warn("getpwuid");
2185ae6585fSray 		return (-1);
2195ae6585fSray 	}
2205ae6585fSray 
2215ae6585fSray 	/* Get full name. */
2225ae6585fSray 	len = strcspn(pw->pw_gecos, ",");
2235ae6585fSray 	if ((fullname = malloc(len + 1)) == NULL) {
2245ae6585fSray 		warn("malloc");
2255ae6585fSray 		return (-1);
2265ae6585fSray 	}
2275ae6585fSray 	memcpy(fullname, pw->pw_gecos, len);
2285ae6585fSray 	fullname[len] = '\0';
2295ae6585fSray 
2305ae6585fSray 	sysname[0] = CTL_KERN;
2315ae6585fSray 	sysname[1] = KERN_OSTYPE;
2325ae6585fSray 	len = sizeof(os) - 1;
2335ae6585fSray 	if (sysctl(sysname, 2, &os, &len, NULL, 0) == -1) {
2345ae6585fSray 		warn("sysctl");
2355ae6585fSray 		return (-1);
2365ae6585fSray 	}
2375ae6585fSray 
2385ae6585fSray 	sysname[0] = CTL_KERN;
2395ae6585fSray 	sysname[1] = KERN_OSRELEASE;
2405ae6585fSray 	len = sizeof(rel) - 1;
2415ae6585fSray 	if (sysctl(sysname, 2, &rel, &len, NULL, 0) == -1) {
2425ae6585fSray 		warn("sysctl");
2435ae6585fSray 		return (-1);
2445ae6585fSray 	}
2455ae6585fSray 
2465ae6585fSray 	sysname[0] = CTL_HW;
2475ae6585fSray 	sysname[1] = HW_MACHINE;
2485ae6585fSray 	len = sizeof(mach) - 1;
2495ae6585fSray 	if (sysctl(sysname, 2, &mach, &len, NULL, 0) == -1) {
2505ae6585fSray 		warn("sysctl");
2515ae6585fSray 		return (-1);
2525ae6585fSray 	}
2535ae6585fSray 
2545ae6585fSray 	return (0);
2555ae6585fSray }
2565ae6585fSray 
2575ae6585fSray int
2585ae6585fSray send_file(const char *file, int dst)
2595ae6585fSray {
2605ae6585fSray 	int blank = 0;
26170dfcc75Sderaadt 	size_t len;
26270dfcc75Sderaadt 	char *buf;
26370dfcc75Sderaadt 	FILE *fp;
2645ae6585fSray 
2655ae6585fSray 	if ((fp = fopen(file, "r")) == NULL)
2665ae6585fSray 		return (-1);
2675ae6585fSray 	while ((buf = fgetln(fp, &len))) {
2685ae6585fSray 		/* Skip lines starting with "SENDBUG". */
2695ae6585fSray 		if (len >= sizeof("SENDBUG") - 1 &&
2705ae6585fSray 		    memcmp(buf, "SENDBUG", sizeof("SENDBUG") - 1) == 0)
2715ae6585fSray 			continue;
2725ae6585fSray 		if (len == 1 && buf[0] == '\n')
2735ae6585fSray 			blank = 1;
2745ae6585fSray 		/* Skip comments, but only if we encountered a blank line. */
2755ae6585fSray 		while (len) {
2765ae6585fSray 			char *sp, *ep = NULL;
2775ae6585fSray 			size_t copylen;
2785ae6585fSray 
2795ae6585fSray 			if (blank && (sp = memchr(buf, '<', len)) != NULL)
2805ae6585fSray 				ep = memchr(sp, '>', len - (sp - buf + 1));
2815ae6585fSray 			/* Length of string before comment. */
2825ae6585fSray 			copylen = ep ? sp - buf : len;
2835ae6585fSray 			if (atomicio(vwrite, dst, buf, copylen) != copylen) {
2845ae6585fSray 				int saved_errno = errno;
2855ae6585fSray 
2865ae6585fSray 				fclose(fp);
2875ae6585fSray 				errno = saved_errno;
2885ae6585fSray 				return (-1);
2895ae6585fSray 			}
2905ae6585fSray 			if (!ep)
2915ae6585fSray 				break;
2925ae6585fSray 			/* Skip comment. */
2935ae6585fSray 			len -= ep - buf + 1;
2945ae6585fSray 			buf = ep + 1;
2955ae6585fSray 		}
2965ae6585fSray 	}
2975ae6585fSray 	fclose(fp);
2985ae6585fSray 	return (0);
2995ae6585fSray }
3005ae6585fSray 
3015ae6585fSray void
3025ae6585fSray template(FILE *fp)
3035ae6585fSray {
3045ae6585fSray 	fprintf(fp, "SENDBUG: -*- sendbug -*-\n");
3055ae6585fSray 	fprintf(fp, "SENDBUG: Lines starting with `SENDBUG' will be removed automatically, as\n");
3065ae6585fSray 	fprintf(fp, "SENDBUG: will all comments (text enclosed in `<' and `>').              \n");
3075ae6585fSray 	fprintf(fp, "SENDBUG:\n");
3085ae6585fSray 	fprintf(fp, "SENDBUG: Choose from the following categories:\n");
3095ae6585fSray 	fprintf(fp, "SENDBUG:\n");
3105ae6585fSray 	fprintf(fp, "SENDBUG: %s\n", categories);
3115ae6585fSray 	fprintf(fp, "SENDBUG:\n");
3125ae6585fSray 	fprintf(fp, "SENDBUG:\n");
3135ae6585fSray 	fprintf(fp, "To: %s\n", "gnats@openbsd.org");
3145ae6585fSray 	fprintf(fp, "Subject: \n");
3155ae6585fSray 	fprintf(fp, "From: %s\n", pw->pw_name);
3165ae6585fSray 	fprintf(fp, "Cc: \n");
3175ae6585fSray 	fprintf(fp, "Reply-To: %s\n", pw->pw_name);
3185ae6585fSray 	fprintf(fp, "X-sendbug-version: 4.2\n");
3195ae6585fSray 	fprintf(fp, "\n");
3205ae6585fSray 	fprintf(fp, "\n");
3215ae6585fSray 	fprintf(fp, ">Submitter-Id:\tnet\n");
3225ae6585fSray 	fprintf(fp, ">Originator:\t%s\n", fullname);
3235ae6585fSray 	fprintf(fp, ">Organization:\n");
3245ae6585fSray 	fprintf(fp, "net\n");
3255ae6585fSray 	fprintf(fp, ">Synopsis:\t<synopsis of the problem (one line)>\n");
3265ae6585fSray 	fprintf(fp, ">Severity:\t<[ non-critical | serious | critical ] (one line)>\n");
3275ae6585fSray 	fprintf(fp, ">Priority:\t<[ low | medium | high ] (one line)>\n");
3285ae6585fSray 	fprintf(fp, ">Category:\t<PR category (one line)>\n");
3295ae6585fSray 	fprintf(fp, ">Class:\t\t<[ sw-bug | doc-bug | change-request | support ] (one line)>\n");
3305ae6585fSray 	fprintf(fp, ">Release:\t<release number or tag (one line)>\n");
3315ae6585fSray 	fprintf(fp, ">Environment:\n");
3325ae6585fSray 	fprintf(fp, "\t<machine, os, target, libraries (multiple lines)>\n");
3335ae6585fSray 	fprintf(fp, "\tSystem      : %s %s\n", os, rel);
3345ae6585fSray 	fprintf(fp, "\tArchitecture: %s.%s\n", os, mach);
3355ae6585fSray 	fprintf(fp, "\tMachine     : %s\n", mach);
3365ae6585fSray 	fprintf(fp, ">Description:\n");
3375ae6585fSray 	fprintf(fp, "\t<precise description of the problem (multiple lines)>\n");
3385ae6585fSray 	fprintf(fp, ">How-To-Repeat:\n");
3395ae6585fSray 	fprintf(fp, "\t<code/input/activities to reproduce the problem (multiple lines)>\n");
3405ae6585fSray 	fprintf(fp, ">Fix:\n");
3415ae6585fSray 	fprintf(fp, "\t<how to correct or work around the problem, if known (multiple lines)>\n");
3425ae6585fSray }
343