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