1*70dfcc75Sderaadt /* $OpenBSD: sendbug.c,v 1.2 2007/03/23 02:11:00 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 405ae6585fSray int 415ae6585fSray main(int argc, char *argv[]) 425ae6585fSray { 435ae6585fSray const char *editor, *tmpdir; 445ae6585fSray char *tmppath = NULL; 455ae6585fSray int c, fd, ret = 1; 46*70dfcc75Sderaadt struct stat sb; 47*70dfcc75Sderaadt time_t mtime; 48*70dfcc75Sderaadt FILE *fp; 495ae6585fSray 505ae6585fSray if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') 515ae6585fSray tmpdir = _PATH_TMP; 525ae6585fSray if (asprintf(&tmppath, "%s/p.XXXXXXXXXX", tmpdir) == -1) { 535ae6585fSray warn("asprintf"); 545ae6585fSray goto quit; 555ae6585fSray } 565ae6585fSray if ((fd = mkstemp(tmppath)) == -1) 575ae6585fSray err(1, "mkstemp"); 585ae6585fSray if ((fp = fdopen(fd, "w+")) == NULL) { 595ae6585fSray warn("fdopen"); 605ae6585fSray goto cleanup; 615ae6585fSray } 625ae6585fSray 635ae6585fSray if (init() == -1) 645ae6585fSray goto cleanup; 655ae6585fSray 665ae6585fSray template(fp); 675ae6585fSray 685ae6585fSray if (fflush(fp) == EOF || fstat(fd, &sb) == -1 || fclose(fp) == EOF) { 695ae6585fSray warn("error creating template"); 705ae6585fSray goto cleanup; 715ae6585fSray } 725ae6585fSray mtime = sb.st_mtime; 735ae6585fSray 745ae6585fSray edit: 755ae6585fSray if ((editor = getenv("EDITOR")) == NULL) 765ae6585fSray editor = "vi"; 775ae6585fSray switch (fork()) { 785ae6585fSray case -1: 795ae6585fSray warn("fork"); 805ae6585fSray goto cleanup; 815ae6585fSray case 0: 82*70dfcc75Sderaadt execlp(editor, editor, tmppath, (void *)NULL); 835ae6585fSray err(1, "execlp"); 845ae6585fSray default: 855ae6585fSray wait(NULL); 865ae6585fSray break; 875ae6585fSray } 885ae6585fSray 895ae6585fSray if (stat(tmppath, &sb) == -1) { 905ae6585fSray warn("stat"); 915ae6585fSray goto cleanup; 925ae6585fSray } 935ae6585fSray if (mtime == sb.st_mtime) { 945ae6585fSray warnx("report unchanged, nothing sent"); 955ae6585fSray goto cleanup; 965ae6585fSray } 975ae6585fSray 985ae6585fSray prompt: 995ae6585fSray c = prompt(); 1005ae6585fSray switch (c) { 1015ae6585fSray case 'a': case EOF: 1025ae6585fSray warnx("unsent report in %s", tmppath); 1035ae6585fSray goto quit; 1045ae6585fSray case 'e': 1055ae6585fSray goto edit; 1065ae6585fSray case 's': 1075ae6585fSray if (sendmail(tmppath) == -1) 1085ae6585fSray goto quit; 1095ae6585fSray break; 1105ae6585fSray default: 1115ae6585fSray goto prompt; 1125ae6585fSray } 1135ae6585fSray 1145ae6585fSray ret = 0; 1155ae6585fSray 1165ae6585fSray cleanup: 1175ae6585fSray if (tmppath && unlink(tmppath) == -1) 1185ae6585fSray warn("unlink"); 1195ae6585fSray 1205ae6585fSray quit: 1215ae6585fSray return (ret); 1225ae6585fSray } 1235ae6585fSray 1245ae6585fSray int 1255ae6585fSray prompt(void) 1265ae6585fSray { 1275ae6585fSray int c, ret; 1285ae6585fSray 1295ae6585fSray fpurge(stdin); 1305ae6585fSray fprintf(stderr, "a)bort, e)dit, or s)end: "); 1315ae6585fSray fflush(stderr); 1325ae6585fSray ret = getchar(); 1335ae6585fSray if (ret == EOF || ret == '\n') 1345ae6585fSray return (ret); 1355ae6585fSray do { 1365ae6585fSray c = getchar(); 1375ae6585fSray } while (c != EOF && c != '\n'); 1385ae6585fSray return (ret); 1395ae6585fSray } 1405ae6585fSray 1415ae6585fSray int 1425ae6585fSray sendmail(const char *tmppath) 1435ae6585fSray { 1445ae6585fSray int filedes[2]; 1455ae6585fSray 1465ae6585fSray if (pipe(filedes) == -1) { 1475ae6585fSray warn("pipe: unsent report in %s", tmppath); 1485ae6585fSray return (-1); 1495ae6585fSray } 1505ae6585fSray switch (fork()) { 1515ae6585fSray case -1: 1525ae6585fSray warn("fork error: unsent report in %s", 1535ae6585fSray tmppath); 1545ae6585fSray return (-1); 1555ae6585fSray case 0: 1565ae6585fSray close(filedes[1]); 1575ae6585fSray if (dup2(filedes[0], STDIN_FILENO) == -1) { 1585ae6585fSray warn("dup2 error: unsent report in %s", 1595ae6585fSray tmppath); 1605ae6585fSray return (-1); 1615ae6585fSray } 1625ae6585fSray close(filedes[0]); 1635ae6585fSray execl("/usr/sbin/sendmail", "sendmail", 164*70dfcc75Sderaadt "-oi", "-t", (void *)NULL); 1655ae6585fSray warn("sendmail error: unsent report in %s", 1665ae6585fSray tmppath); 1675ae6585fSray return (-1); 1685ae6585fSray default: 1695ae6585fSray close(filedes[0]); 1705ae6585fSray /* Pipe into sendmail. */ 1715ae6585fSray if (send_file(tmppath, filedes[1]) == -1) { 1725ae6585fSray warn("send_file error: unsent report in %s", 1735ae6585fSray tmppath); 1745ae6585fSray return (-1); 1755ae6585fSray } 1765ae6585fSray close(filedes[1]); 1775ae6585fSray wait(NULL); 1785ae6585fSray break; 1795ae6585fSray } 1805ae6585fSray return (0); 1815ae6585fSray } 1825ae6585fSray 1835ae6585fSray int 1845ae6585fSray init(void) 1855ae6585fSray { 1865ae6585fSray size_t len; 1875ae6585fSray int sysname[2]; 1885ae6585fSray 1895ae6585fSray if ((pw = getpwuid(getuid())) == NULL) { 1905ae6585fSray warn("getpwuid"); 1915ae6585fSray return (-1); 1925ae6585fSray } 1935ae6585fSray 1945ae6585fSray /* Get full name. */ 1955ae6585fSray len = strcspn(pw->pw_gecos, ","); 1965ae6585fSray if ((fullname = malloc(len + 1)) == NULL) { 1975ae6585fSray warn("malloc"); 1985ae6585fSray return (-1); 1995ae6585fSray } 2005ae6585fSray memcpy(fullname, pw->pw_gecos, len); 2015ae6585fSray fullname[len] = '\0'; 2025ae6585fSray 2035ae6585fSray sysname[0] = CTL_KERN; 2045ae6585fSray sysname[1] = KERN_OSTYPE; 2055ae6585fSray len = sizeof(os) - 1; 2065ae6585fSray if (sysctl(sysname, 2, &os, &len, NULL, 0) == -1) { 2075ae6585fSray warn("sysctl"); 2085ae6585fSray return (-1); 2095ae6585fSray } 2105ae6585fSray 2115ae6585fSray sysname[0] = CTL_KERN; 2125ae6585fSray sysname[1] = KERN_OSRELEASE; 2135ae6585fSray len = sizeof(rel) - 1; 2145ae6585fSray if (sysctl(sysname, 2, &rel, &len, NULL, 0) == -1) { 2155ae6585fSray warn("sysctl"); 2165ae6585fSray return (-1); 2175ae6585fSray } 2185ae6585fSray 2195ae6585fSray sysname[0] = CTL_HW; 2205ae6585fSray sysname[1] = HW_MACHINE; 2215ae6585fSray len = sizeof(mach) - 1; 2225ae6585fSray if (sysctl(sysname, 2, &mach, &len, NULL, 0) == -1) { 2235ae6585fSray warn("sysctl"); 2245ae6585fSray return (-1); 2255ae6585fSray } 2265ae6585fSray 2275ae6585fSray return (0); 2285ae6585fSray } 2295ae6585fSray 2305ae6585fSray int 2315ae6585fSray send_file(const char *file, int dst) 2325ae6585fSray { 2335ae6585fSray int blank = 0; 234*70dfcc75Sderaadt size_t len; 235*70dfcc75Sderaadt char *buf; 236*70dfcc75Sderaadt FILE *fp; 2375ae6585fSray 2385ae6585fSray if ((fp = fopen(file, "r")) == NULL) 2395ae6585fSray return (-1); 2405ae6585fSray while ((buf = fgetln(fp, &len))) { 2415ae6585fSray /* Skip lines starting with "SENDBUG". */ 2425ae6585fSray if (len >= sizeof("SENDBUG") - 1 && 2435ae6585fSray memcmp(buf, "SENDBUG", sizeof("SENDBUG") - 1) == 0) 2445ae6585fSray continue; 2455ae6585fSray if (len == 1 && buf[0] == '\n') 2465ae6585fSray blank = 1; 2475ae6585fSray /* Skip comments, but only if we encountered a blank line. */ 2485ae6585fSray while (len) { 2495ae6585fSray char *sp, *ep = NULL; 2505ae6585fSray size_t copylen; 2515ae6585fSray 2525ae6585fSray if (blank && (sp = memchr(buf, '<', len)) != NULL) 2535ae6585fSray ep = memchr(sp, '>', len - (sp - buf + 1)); 2545ae6585fSray /* Length of string before comment. */ 2555ae6585fSray copylen = ep ? sp - buf : len; 2565ae6585fSray if (atomicio(vwrite, dst, buf, copylen) != copylen) { 2575ae6585fSray int saved_errno = errno; 2585ae6585fSray 2595ae6585fSray fclose(fp); 2605ae6585fSray errno = saved_errno; 2615ae6585fSray return (-1); 2625ae6585fSray } 2635ae6585fSray if (!ep) 2645ae6585fSray break; 2655ae6585fSray /* Skip comment. */ 2665ae6585fSray len -= ep - buf + 1; 2675ae6585fSray buf = ep + 1; 2685ae6585fSray } 2695ae6585fSray } 2705ae6585fSray fclose(fp); 2715ae6585fSray return (0); 2725ae6585fSray } 2735ae6585fSray 2745ae6585fSray void 2755ae6585fSray template(FILE *fp) 2765ae6585fSray { 2775ae6585fSray fprintf(fp, "SENDBUG: -*- sendbug -*-\n"); 2785ae6585fSray fprintf(fp, "SENDBUG: Lines starting with `SENDBUG' will be removed automatically, as\n"); 2795ae6585fSray fprintf(fp, "SENDBUG: will all comments (text enclosed in `<' and `>'). \n"); 2805ae6585fSray fprintf(fp, "SENDBUG:\n"); 2815ae6585fSray fprintf(fp, "SENDBUG: Choose from the following categories:\n"); 2825ae6585fSray fprintf(fp, "SENDBUG:\n"); 2835ae6585fSray fprintf(fp, "SENDBUG: %s\n", categories); 2845ae6585fSray fprintf(fp, "SENDBUG:\n"); 2855ae6585fSray fprintf(fp, "SENDBUG:\n"); 2865ae6585fSray fprintf(fp, "To: %s\n", "gnats@openbsd.org"); 2875ae6585fSray fprintf(fp, "Subject: \n"); 2885ae6585fSray fprintf(fp, "From: %s\n", pw->pw_name); 2895ae6585fSray fprintf(fp, "Cc: \n"); 2905ae6585fSray fprintf(fp, "Reply-To: %s\n", pw->pw_name); 2915ae6585fSray fprintf(fp, "X-sendbug-version: 4.2\n"); 2925ae6585fSray fprintf(fp, "\n"); 2935ae6585fSray fprintf(fp, "\n"); 2945ae6585fSray fprintf(fp, ">Submitter-Id:\tnet\n"); 2955ae6585fSray fprintf(fp, ">Originator:\t%s\n", fullname); 2965ae6585fSray fprintf(fp, ">Organization:\n"); 2975ae6585fSray fprintf(fp, "net\n"); 2985ae6585fSray fprintf(fp, ">Synopsis:\t<synopsis of the problem (one line)>\n"); 2995ae6585fSray fprintf(fp, ">Severity:\t<[ non-critical | serious | critical ] (one line)>\n"); 3005ae6585fSray fprintf(fp, ">Priority:\t<[ low | medium | high ] (one line)>\n"); 3015ae6585fSray fprintf(fp, ">Category:\t<PR category (one line)>\n"); 3025ae6585fSray fprintf(fp, ">Class:\t\t<[ sw-bug | doc-bug | change-request | support ] (one line)>\n"); 3035ae6585fSray fprintf(fp, ">Release:\t<release number or tag (one line)>\n"); 3045ae6585fSray fprintf(fp, ">Environment:\n"); 3055ae6585fSray fprintf(fp, "\t<machine, os, target, libraries (multiple lines)>\n"); 3065ae6585fSray fprintf(fp, "\tSystem : %s %s\n", os, rel); 3075ae6585fSray fprintf(fp, "\tArchitecture: %s.%s\n", os, mach); 3085ae6585fSray fprintf(fp, "\tMachine : %s\n", mach); 3095ae6585fSray fprintf(fp, ">Description:\n"); 3105ae6585fSray fprintf(fp, "\t<precise description of the problem (multiple lines)>\n"); 3115ae6585fSray fprintf(fp, ">How-To-Repeat:\n"); 3125ae6585fSray fprintf(fp, "\t<code/input/activities to reproduce the problem (multiple lines)>\n"); 3135ae6585fSray fprintf(fp, ">Fix:\n"); 3145ae6585fSray fprintf(fp, "\t<how to correct or work around the problem, if known (multiple lines)>\n"); 3155ae6585fSray } 316