1*b2c335bbSray /* $OpenBSD: sendbug.c,v 1.55 2008/04/19 09:22:31 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/param.h> 105ae6585fSray #include <sys/stat.h> 115ae6585fSray #include <sys/sysctl.h> 125ae6585fSray #include <sys/wait.h> 135ae6585fSray 1449d07c37Sray #include <ctype.h> 155ae6585fSray #include <err.h> 165ae6585fSray #include <errno.h> 175ae6585fSray #include <fcntl.h> 185ae6585fSray #include <limits.h> 195ae6585fSray #include <paths.h> 205ae6585fSray #include <pwd.h> 214b9386a8Sderaadt #include <signal.h> 225ae6585fSray #include <stdio.h> 235ae6585fSray #include <stdlib.h> 245ae6585fSray #include <string.h> 255ae6585fSray #include <unistd.h> 265ae6585fSray 275ae6585fSray #include "atomicio.h" 285ae6585fSray 297d2774c0Sray #define _PATH_DMESG "/var/run/dmesg.boot" 3051cd2972Sray #define DMESG_START "OpenBSD " 317d2774c0Sray 32fad7ccdbSray int checkfile(const char *); 336c4efebfSray void dmesg(FILE *); 344c385748Sray int editit(const char *); 35b2e65daaStedu void init(void); 36e9d79a51Sray int matchline(const char *, const char *, size_t); 375ae6585fSray int prompt(void); 380690c094Sray int send_file(const char *, int); 395ae6585fSray int sendmail(const char *); 405ae6585fSray void template(FILE *); 415ae6585fSray 425ae6585fSray const char *categories = "system user library documentation ports kernel " 435ae6585fSray "alpha amd64 arm i386 m68k m88k mips ppc sgi sparc sparc64 vax"; 44823e89efSderaadt char *version = "4.2"; 45c952560cSray const char *comment[] = { 46c952560cSray "<synopsis of the problem (one line)>", 47c952560cSray "<[ non-critical | serious | critical ] (one line)>", 48c952560cSray "<[ low | medium | high ] (one line)>", 49c952560cSray "<PR category (one line)>", 50c952560cSray "<[ sw-bug | doc-bug | change-request | support ] (one line)>", 51c952560cSray "<release number or tag (one line)>", 52c952560cSray "<machine, os, target, libraries (multiple lines)>", 53c952560cSray "<precise description of the problem (multiple lines)>", 54c952560cSray "<code/input/activities to reproduce the problem (multiple lines)>", 55c952560cSray "<how to correct or work around the problem, if known (multiple lines)>" 56c952560cSray }; 57823e89efSderaadt 58823e89efSderaadt struct passwd *pw; 59e051e8faSderaadt char os[BUFSIZ], rel[BUFSIZ], mach[BUFSIZ], details[BUFSIZ]; 606b1a1e2aSderaadt char *fullname, *tmppath; 61aad8b29bSray int Dflag, wantcleanup; 62823e89efSderaadt 63b2e65daaStedu __dead void 64903e61cbSderaadt usage(void) 65903e61cbSderaadt { 66946adaceSray extern char *__progname; 67946adaceSray 68946adaceSray fprintf(stderr, "usage: %s [-DLPV]\n", __progname); 69b2e65daaStedu exit(1); 70903e61cbSderaadt } 71903e61cbSderaadt 72b2e65daaStedu void 73b2e65daaStedu cleanup() 74b2e65daaStedu { 75b2e65daaStedu if (wantcleanup && tmppath && unlink(tmppath) == -1) 76b2e65daaStedu warn("unlink"); 77b2e65daaStedu } 78b2e65daaStedu 79b2e65daaStedu 805ae6585fSray int 815ae6585fSray main(int argc, char *argv[]) 825ae6585fSray { 83aad8b29bSray int ch, c, fd, ret = 1; 84f4348cb9Sderaadt const char *tmpdir; 8570dfcc75Sderaadt struct stat sb; 86f4348cb9Sderaadt char *pr_form; 8770dfcc75Sderaadt time_t mtime; 8870dfcc75Sderaadt FILE *fp; 895ae6585fSray 907d2774c0Sray while ((ch = getopt(argc, argv, "DLPV")) != -1) 91903e61cbSderaadt switch (ch) { 927d2774c0Sray case 'D': 937d2774c0Sray Dflag = 1; 947d2774c0Sray break; 95903e61cbSderaadt case 'L': 96903e61cbSderaadt printf("Known categories:\n"); 97903e61cbSderaadt printf("%s\n\n", categories); 98903e61cbSderaadt exit(0); 99903e61cbSderaadt case 'P': 100b2e65daaStedu init(); 101903e61cbSderaadt template(stdout); 102903e61cbSderaadt exit(0); 1035711e205Sderaadt case 'V': 1045711e205Sderaadt printf("%s\n", version); 1055711e205Sderaadt exit(0); 106903e61cbSderaadt default: 107903e61cbSderaadt usage(); 108903e61cbSderaadt } 1097d2774c0Sray argc -= optind; 1107d2774c0Sray argv += optind; 111903e61cbSderaadt 112f76e53adSray if (argc > 0) 113903e61cbSderaadt usage(); 114903e61cbSderaadt 1155ae6585fSray if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') 1165ae6585fSray tmpdir = _PATH_TMP; 117795a8b97Sray if (asprintf(&tmppath, "%s%sp.XXXXXXXXXX", tmpdir, 118f4348cb9Sderaadt tmpdir[strlen(tmpdir) - 1] == '/' ? "" : "/") == -1) 119b2e65daaStedu err(1, "asprintf"); 1205ae6585fSray if ((fd = mkstemp(tmppath)) == -1) 1215ae6585fSray err(1, "mkstemp"); 122b2e65daaStedu wantcleanup = 1; 123b2e65daaStedu atexit(cleanup); 124f4348cb9Sderaadt if ((fp = fdopen(fd, "w+")) == NULL) 125b2e65daaStedu err(1, "fdopen"); 1265ae6585fSray 127b2e65daaStedu init(); 1285ae6585fSray 1294ff3398aSderaadt pr_form = getenv("PR_FORM"); 1304ff3398aSderaadt if (pr_form) { 1314ff3398aSderaadt char buf[BUFSIZ]; 1324ff3398aSderaadt size_t len; 1334ff3398aSderaadt FILE *frfp; 1344ff3398aSderaadt 1354ff3398aSderaadt frfp = fopen(pr_form, "r"); 1364ff3398aSderaadt if (frfp == NULL) { 137946adaceSray warn("can't seem to read your template file " 138946adaceSray "(`%s'), ignoring PR_FORM", pr_form); 1394ff3398aSderaadt template(fp); 1404ff3398aSderaadt } else { 1414ff3398aSderaadt while (!feof(frfp)) { 1424ff3398aSderaadt len = fread(buf, 1, sizeof buf, frfp); 1434ff3398aSderaadt if (len == 0) 1444ff3398aSderaadt break; 1454ff3398aSderaadt if (fwrite(buf, 1, len, fp) != len) 1464ff3398aSderaadt break; 1474ff3398aSderaadt } 1484ff3398aSderaadt fclose(frfp); 1494ff3398aSderaadt } 150aad8b29bSray } else 1515ae6585fSray template(fp); 1525ae6585fSray 153f4348cb9Sderaadt if (fflush(fp) == EOF || fstat(fd, &sb) == -1 || fclose(fp) == EOF) 154b2e65daaStedu err(1, "error creating template"); 1555ae6585fSray mtime = sb.st_mtime; 1565ae6585fSray 1575ae6585fSray edit: 1583bdb63c6Sray if (editit(tmppath) == -1) 159648f0ffcSray err(1, "error running editor"); 1605ae6585fSray 161f4348cb9Sderaadt if (stat(tmppath, &sb) == -1) 162b2e65daaStedu err(1, "stat"); 163f4348cb9Sderaadt if (mtime == sb.st_mtime) 164b2e65daaStedu errx(1, "report unchanged, nothing sent"); 1655ae6585fSray 1665ae6585fSray prompt: 167fad7ccdbSray if (!checkfile(tmppath)) 168fad7ccdbSray fprintf(stderr, "fields are blank, must be filled in\n"); 1695ae6585fSray c = prompt(); 1705ae6585fSray switch (c) { 1715711e205Sderaadt case 'a': 1725711e205Sderaadt case EOF: 173b2e65daaStedu wantcleanup = 0; 174b2e65daaStedu errx(1, "unsent report in %s", tmppath); 1755ae6585fSray case 'e': 1765ae6585fSray goto edit; 1775ae6585fSray case 's': 1785ae6585fSray if (sendmail(tmppath) == -1) 1795ae6585fSray goto quit; 1805ae6585fSray break; 1815ae6585fSray default: 1825ae6585fSray goto prompt; 1835ae6585fSray } 1845ae6585fSray 1855ae6585fSray ret = 0; 1865ae6585fSray quit: 1875ae6585fSray return (ret); 1885ae6585fSray } 1895ae6585fSray 1906c4efebfSray void 1916c4efebfSray dmesg(FILE *fp) 1926c4efebfSray { 1936c4efebfSray char buf[BUFSIZ]; 1946c4efebfSray FILE *dfp; 1956c4efebfSray off_t offset = -1; 1966c4efebfSray 1976c4efebfSray dfp = fopen(_PATH_DMESG, "r"); 1986c4efebfSray if (dfp == NULL) { 1996c4efebfSray warn("can't read dmesg"); 2006c4efebfSray return; 2016c4efebfSray } 2026c4efebfSray 2036c4efebfSray fputs("\n" 204c952560cSray "SENDBUG: dmesg is attached.\n" 205c952560cSray "SENDBUG: Feel free to delete or use the -D flag if it contains " 206c952560cSray "sensitive information.\n", fp); 20751cd2972Sray /* Find last dmesg. */ 2086c4efebfSray for (;;) { 2096c4efebfSray off_t o; 2106c4efebfSray 2116c4efebfSray o = ftello(dfp); 2126c4efebfSray if (fgets(buf, sizeof(buf), dfp) == NULL) 2136c4efebfSray break; 21451cd2972Sray if (!strncmp(DMESG_START, buf, sizeof(DMESG_START) - 1)) 2156c4efebfSray offset = o; 2166c4efebfSray } 2176c4efebfSray if (offset != -1) { 2186c4efebfSray size_t len; 2196c4efebfSray 2206c4efebfSray clearerr(dfp); 2216c4efebfSray fseeko(dfp, offset, SEEK_SET); 2226c4efebfSray while (offset != -1 && !feof(dfp)) { 2236c4efebfSray len = fread(buf, 1, sizeof buf, dfp); 2246c4efebfSray if (len == 0) 2256c4efebfSray break; 2266c4efebfSray if (fwrite(buf, 1, len, fp) != len) 2276c4efebfSray break; 2286c4efebfSray } 2296c4efebfSray } 2306c4efebfSray fclose(dfp); 2316c4efebfSray } 2326c4efebfSray 2333bdb63c6Sray /* 2343bdb63c6Sray * Execute an editor on the specified pathname, which is interpreted 2353bdb63c6Sray * from the shell. This means flags may be included. 2363bdb63c6Sray * 2373bdb63c6Sray * Returns -1 on error, or the exit value on success. 2383bdb63c6Sray */ 2394b9386a8Sderaadt int 2404c385748Sray editit(const char *pathname) 2414b9386a8Sderaadt { 242f4348cb9Sderaadt char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p; 24343316b18Sderaadt sig_t sighup, sigint, sigquit, sigchld; 2444c385748Sray pid_t pid; 24543316b18Sderaadt int saved_errno, st, ret = -1; 2464b9386a8Sderaadt 247c8a426adSray ed = getenv("VISUAL"); 248c8a426adSray if (ed == NULL || ed[0] == '\0') 249c8a426adSray ed = getenv("EDITOR"); 250c8a426adSray if (ed == NULL || ed[0] == '\0') 2514b9386a8Sderaadt ed = _PATH_VI; 252292bd7edSray if (asprintf(&p, "%s %s", ed, pathname) == -1) 2538af53ff7Sray return (-1); 2544b9386a8Sderaadt argp[2] = p; 2554b9386a8Sderaadt 2568ec2d82eSray sighup = signal(SIGHUP, SIG_IGN); 2578ec2d82eSray sigint = signal(SIGINT, SIG_IGN); 2588ec2d82eSray sigquit = signal(SIGQUIT, SIG_IGN); 25943316b18Sderaadt sigchld = signal(SIGCHLD, SIG_DFL); 260ae6b53b5Sray if ((pid = fork()) == -1) 2613cf7ad38Sray goto fail; 2624b9386a8Sderaadt if (pid == 0) { 2634b9386a8Sderaadt execv(_PATH_BSHELL, argp); 2644b9386a8Sderaadt _exit(127); 2654b9386a8Sderaadt } 2663cf7ad38Sray while (waitpid(pid, &st, 0) == -1) 2673cf7ad38Sray if (errno != EINTR) 2683cf7ad38Sray goto fail; 26943316b18Sderaadt if (!WIFEXITED(st)) 2703bdb63c6Sray errno = EINTR; 27143316b18Sderaadt else 27243316b18Sderaadt ret = WEXITSTATUS(st); 2733cf7ad38Sray 2743cf7ad38Sray fail: 2753cf7ad38Sray saved_errno = errno; 2763cf7ad38Sray (void)signal(SIGHUP, sighup); 2773cf7ad38Sray (void)signal(SIGINT, sigint); 2783cf7ad38Sray (void)signal(SIGQUIT, sigquit); 27943316b18Sderaadt (void)signal(SIGCHLD, sigchld); 2803cf7ad38Sray free(p); 2813cf7ad38Sray errno = saved_errno; 28243316b18Sderaadt return (ret); 2834b9386a8Sderaadt } 284b2e65daaStedu 2855ae6585fSray int 2865ae6585fSray prompt(void) 2875ae6585fSray { 2885ae6585fSray int c, ret; 2895ae6585fSray 2905ae6585fSray fpurge(stdin); 2915ae6585fSray fprintf(stderr, "a)bort, e)dit, or s)end: "); 2925ae6585fSray fflush(stderr); 2935ae6585fSray ret = getchar(); 2945ae6585fSray if (ret == EOF || ret == '\n') 2955ae6585fSray return (ret); 2965ae6585fSray do { 2975ae6585fSray c = getchar(); 2985ae6585fSray } while (c != EOF && c != '\n'); 2995ae6585fSray return (ret); 3005ae6585fSray } 3015ae6585fSray 3025ae6585fSray int 303292bd7edSray sendmail(const char *pathname) 3045ae6585fSray { 3055ae6585fSray int filedes[2]; 3065ae6585fSray 3075ae6585fSray if (pipe(filedes) == -1) { 308292bd7edSray warn("pipe: unsent report in %s", pathname); 3095ae6585fSray return (-1); 3105ae6585fSray } 3115ae6585fSray switch (fork()) { 3125ae6585fSray case -1: 3135ae6585fSray warn("fork error: unsent report in %s", 314292bd7edSray pathname); 3155ae6585fSray return (-1); 3165ae6585fSray case 0: 3175ae6585fSray close(filedes[1]); 3185ae6585fSray if (dup2(filedes[0], STDIN_FILENO) == -1) { 3195ae6585fSray warn("dup2 error: unsent report in %s", 320292bd7edSray pathname); 3215ae6585fSray return (-1); 3225ae6585fSray } 3235ae6585fSray close(filedes[0]); 3245ae6585fSray execl("/usr/sbin/sendmail", "sendmail", 32570dfcc75Sderaadt "-oi", "-t", (void *)NULL); 3265ae6585fSray warn("sendmail error: unsent report in %s", 327292bd7edSray pathname); 3285ae6585fSray return (-1); 3295ae6585fSray default: 3305ae6585fSray close(filedes[0]); 3315ae6585fSray /* Pipe into sendmail. */ 332292bd7edSray if (send_file(pathname, filedes[1]) == -1) { 3335ae6585fSray warn("send_file error: unsent report in %s", 334292bd7edSray pathname); 3355ae6585fSray return (-1); 3365ae6585fSray } 3375ae6585fSray close(filedes[1]); 3385ae6585fSray wait(NULL); 3395ae6585fSray break; 3405ae6585fSray } 3415ae6585fSray return (0); 3425ae6585fSray } 3435ae6585fSray 344b2e65daaStedu void 3455ae6585fSray init(void) 3465ae6585fSray { 347b088fbb0Sray size_t amp, len, gecoslen, namelen; 348f4348cb9Sderaadt int sysname[2]; 349b088fbb0Sray char ch, *cp; 3505ae6585fSray 351f4348cb9Sderaadt if ((pw = getpwuid(getuid())) == NULL) 352b2e65daaStedu err(1, "getpwuid"); 35349d07c37Sray namelen = strlen(pw->pw_name); 3545ae6585fSray 3559a66796cSmoritz /* Count number of '&'. */ 356b088fbb0Sray for (amp = 0, cp = pw->pw_gecos; *cp && *cp != ','; ++cp) 357b088fbb0Sray if (*cp == '&') 3589a66796cSmoritz ++amp; 359b088fbb0Sray 360b088fbb0Sray /* Truncate gecos to full name. */ 361b088fbb0Sray gecoslen = cp - pw->pw_gecos; 362b088fbb0Sray pw->pw_gecos[gecoslen] = '\0'; 363b088fbb0Sray 3649a66796cSmoritz /* Expanded str = orig str - '&' chars + concatenated logins. */ 365b088fbb0Sray len = gecoslen - amp + (amp * namelen) + 1; 366b088fbb0Sray if ((fullname = malloc(len)) == NULL) 367b2e65daaStedu err(1, "malloc"); 368f4348cb9Sderaadt 369b088fbb0Sray /* Upper case first char of login. */ 370b088fbb0Sray ch = pw->pw_name[0]; 371b088fbb0Sray pw->pw_name[0] = toupper((unsigned char)pw->pw_name[0]); 372b088fbb0Sray 373b088fbb0Sray cp = pw->pw_gecos; 374b088fbb0Sray fullname[0] = '\0'; 375b088fbb0Sray while (cp != NULL) { 376b088fbb0Sray char *token; 377b088fbb0Sray 378b088fbb0Sray token = strsep(&cp, "&"); 379b088fbb0Sray if (token != pw->pw_gecos && 380b088fbb0Sray strlcat(fullname, pw->pw_name, len) >= len) 381b088fbb0Sray errx(1, "truncated string"); 382b088fbb0Sray if (strlcat(fullname, token, len) >= len) 383b088fbb0Sray errx(1, "truncated string"); 38449d07c37Sray } 385b088fbb0Sray /* Restore case of first char of login. */ 386b088fbb0Sray pw->pw_name[0] = ch; 3875ae6585fSray 3885ae6585fSray sysname[0] = CTL_KERN; 3895ae6585fSray sysname[1] = KERN_OSTYPE; 3905ae6585fSray len = sizeof(os) - 1; 391f4348cb9Sderaadt if (sysctl(sysname, 2, &os, &len, NULL, 0) == -1) 392b2e65daaStedu err(1, "sysctl"); 3935ae6585fSray 3945ae6585fSray sysname[0] = CTL_KERN; 3955ae6585fSray sysname[1] = KERN_OSRELEASE; 3965ae6585fSray len = sizeof(rel) - 1; 397f4348cb9Sderaadt if (sysctl(sysname, 2, &rel, &len, NULL, 0) == -1) 398b2e65daaStedu err(1, "sysctl"); 3995ae6585fSray 400e051e8faSderaadt sysname[0] = CTL_KERN; 401e051e8faSderaadt sysname[1] = KERN_VERSION; 402e051e8faSderaadt len = sizeof(details) - 1; 403f4348cb9Sderaadt if (sysctl(sysname, 2, &details, &len, NULL, 0) == -1) 404e051e8faSderaadt err(1, "sysctl"); 405e051e8faSderaadt 406e051e8faSderaadt cp = strchr(details, '\n'); 407e051e8faSderaadt if (cp) { 408e051e8faSderaadt cp++; 409e051e8faSderaadt if (*cp) 410e051e8faSderaadt *cp++ = '\t'; 411e051e8faSderaadt if (*cp) 412e051e8faSderaadt *cp++ = '\t'; 413e051e8faSderaadt if (*cp) 414e051e8faSderaadt *cp++ = '\t'; 415e051e8faSderaadt } 416e051e8faSderaadt 4175ae6585fSray sysname[0] = CTL_HW; 4185ae6585fSray sysname[1] = HW_MACHINE; 4195ae6585fSray len = sizeof(mach) - 1; 420f4348cb9Sderaadt if (sysctl(sysname, 2, &mach, &len, NULL, 0) == -1) 421b2e65daaStedu err(1, "sysctl"); 4225ae6585fSray } 4235ae6585fSray 4245ae6585fSray int 4255ae6585fSray send_file(const char *file, int dst) 4265ae6585fSray { 42770dfcc75Sderaadt size_t len; 42854cc23aeSray char *buf, *lbuf; 42970dfcc75Sderaadt FILE *fp; 43054cc23aeSray int rval = -1, saved_errno; 4315ae6585fSray 4325ae6585fSray if ((fp = fopen(file, "r")) == NULL) 4335ae6585fSray return (-1); 43454cc23aeSray lbuf = NULL; 4355ae6585fSray while ((buf = fgetln(fp, &len))) { 436*b2c335bbSray if (buf[len - 1] == '\n') { 43754cc23aeSray buf[len - 1] = '\0'; 438*b2c335bbSray --len; 439*b2c335bbSray } else { 44054cc23aeSray /* EOF without EOL, copy and add the NUL */ 44154cc23aeSray if ((lbuf = malloc(len + 1)) == NULL) 44254cc23aeSray goto end; 44354cc23aeSray memcpy(lbuf, buf, len); 44454cc23aeSray lbuf[len] = '\0'; 44554cc23aeSray buf = lbuf; 44654cc23aeSray } 44754cc23aeSray 4485ae6585fSray /* Skip lines starting with "SENDBUG". */ 44954cc23aeSray if (strncmp(buf, "SENDBUG", sizeof("SENDBUG") - 1) == 0) 4505ae6585fSray continue; 4515ae6585fSray while (len) { 452e051e8faSderaadt char *sp = NULL, *ep = NULL; 4535ae6585fSray size_t copylen; 4545ae6585fSray 45554cc23aeSray if ((sp = strchr(buf, '<')) != NULL) { 45654cc23aeSray size_t i; 45754cc23aeSray 45854cc23aeSray for (i = 0; i < sizeof(comment) / sizeof(*comment); ++i) { 45954cc23aeSray size_t commentlen = strlen(comment[i]); 46054cc23aeSray 46154cc23aeSray if (strncmp(sp, comment[i], commentlen) == 0) { 46254cc23aeSray ep = sp + commentlen - 1; 46354cc23aeSray break; 46454cc23aeSray } 46554cc23aeSray } 46654cc23aeSray } 4675ae6585fSray /* Length of string before comment. */ 4688cf86b71Sray if (ep) 4698cf86b71Sray copylen = sp - buf; 4708cf86b71Sray else 4718cf86b71Sray copylen = len; 472*b2c335bbSray if (atomicio(vwrite, dst, buf, copylen) != copylen) 47354cc23aeSray goto end; 4745ae6585fSray if (!ep) 4755ae6585fSray break; 4765ae6585fSray /* Skip comment. */ 4775ae6585fSray len -= ep - buf + 1; 4785ae6585fSray buf = ep + 1; 4795ae6585fSray } 480*b2c335bbSray if (atomicio(vwrite, dst, "\n", 1) != 1) 481*b2c335bbSray goto end; 4825ae6585fSray } 48354cc23aeSray rval = 0; 48454cc23aeSray end: 48554cc23aeSray saved_errno = errno; 48654cc23aeSray free(lbuf); 4875ae6585fSray fclose(fp); 48854cc23aeSray errno = saved_errno; 48954cc23aeSray return (rval); 4905ae6585fSray } 4915ae6585fSray 492fad7ccdbSray /* 493fad7ccdbSray * Does line start with `s' and end with non-comment and non-whitespace? 494e9d79a51Sray * Note: Does not treat `line' as a C string. 495fad7ccdbSray */ 496fad7ccdbSray int 497e9d79a51Sray matchline(const char *s, const char *line, size_t linelen) 498fad7ccdbSray { 499fad7ccdbSray size_t slen; 500c952560cSray int iscomment; 501fad7ccdbSray 502fad7ccdbSray slen = strlen(s); 503fad7ccdbSray /* Is line shorter than string? */ 504fad7ccdbSray if (linelen <= slen) 505fad7ccdbSray return (0); 506fad7ccdbSray /* Does line start with string? */ 507fad7ccdbSray if (memcmp(line, s, slen) != 0) 508fad7ccdbSray return (0); 509fad7ccdbSray /* Does line contain anything but comments and whitespace? */ 510fad7ccdbSray line += slen; 511fad7ccdbSray linelen -= slen; 512c952560cSray iscomment = 0; 513fad7ccdbSray while (linelen) { 514c952560cSray if (iscomment) { 515fad7ccdbSray if (*line == '>') 516c952560cSray iscomment = 0; 517fad7ccdbSray } else if (*line == '<') 518c952560cSray iscomment = 1; 519e9d79a51Sray else if (!isspace((unsigned char)*line)) 520fad7ccdbSray return (1); 521fad7ccdbSray ++line; 522fad7ccdbSray --linelen; 523fad7ccdbSray } 524fad7ccdbSray return (0); 525fad7ccdbSray } 526fad7ccdbSray 527fad7ccdbSray /* 528fad7ccdbSray * Are all required fields filled out? 529fad7ccdbSray */ 530fad7ccdbSray int 531fad7ccdbSray checkfile(const char *pathname) 532fad7ccdbSray { 533fad7ccdbSray FILE *fp; 534fad7ccdbSray size_t len; 535fad7ccdbSray int category, class, priority, release, severity, synopsis; 536fad7ccdbSray char *buf; 537fad7ccdbSray 538fad7ccdbSray if ((fp = fopen(pathname, "r")) == NULL) { 539fad7ccdbSray warn("%s", pathname); 540fad7ccdbSray return (0); 541fad7ccdbSray } 542fad7ccdbSray category = class = priority = release = severity = synopsis = 0; 543fad7ccdbSray while ((buf = fgetln(fp, &len))) { 544fad7ccdbSray if (matchline(">Category:", buf, len)) 545fad7ccdbSray category = 1; 546fad7ccdbSray else if (matchline(">Class:", buf, len)) 547fad7ccdbSray class = 1; 548fad7ccdbSray else if (matchline(">Priority:", buf, len)) 549fad7ccdbSray priority = 1; 550fad7ccdbSray else if (matchline(">Release:", buf, len)) 551fad7ccdbSray release = 1; 552fad7ccdbSray else if (matchline(">Severity:", buf, len)) 553fad7ccdbSray severity = 1; 554fad7ccdbSray else if (matchline(">Synopsis:", buf, len)) 555fad7ccdbSray synopsis = 1; 556fad7ccdbSray } 557fad7ccdbSray fclose(fp); 558fad7ccdbSray return (category && class && priority && release && severity && 559fad7ccdbSray synopsis); 560fad7ccdbSray } 561fad7ccdbSray 5625ae6585fSray void 5635ae6585fSray template(FILE *fp) 5645ae6585fSray { 5655ae6585fSray fprintf(fp, "SENDBUG: -*- sendbug -*-\n"); 566f4348cb9Sderaadt fprintf(fp, "SENDBUG: Lines starting with `SENDBUG' will" 56754cc23aeSray " be removed automatically.\n"); 5685ae6585fSray fprintf(fp, "SENDBUG:\n"); 5695ae6585fSray fprintf(fp, "SENDBUG: Choose from the following categories:\n"); 5705ae6585fSray fprintf(fp, "SENDBUG:\n"); 5715ae6585fSray fprintf(fp, "SENDBUG: %s\n", categories); 5725ae6585fSray fprintf(fp, "SENDBUG:\n"); 5735ae6585fSray fprintf(fp, "SENDBUG:\n"); 5745ae6585fSray fprintf(fp, "To: %s\n", "gnats@openbsd.org"); 5755ae6585fSray fprintf(fp, "Subject: \n"); 5765ae6585fSray fprintf(fp, "From: %s\n", pw->pw_name); 577d23b3302Sray fprintf(fp, "Cc: %s\n", pw->pw_name); 5785ae6585fSray fprintf(fp, "Reply-To: %s\n", pw->pw_name); 5795711e205Sderaadt fprintf(fp, "X-sendbug-version: %s\n", version); 5805ae6585fSray fprintf(fp, "\n"); 5815ae6585fSray fprintf(fp, "\n"); 5825ae6585fSray fprintf(fp, ">Submitter-Id:\tnet\n"); 5835ae6585fSray fprintf(fp, ">Originator:\t%s\n", fullname); 5845ae6585fSray fprintf(fp, ">Organization:\n"); 5855ae6585fSray fprintf(fp, "net\n"); 586c952560cSray fprintf(fp, ">Synopsis:\t%s\n", comment[0]); 587c952560cSray fprintf(fp, ">Severity:\t%s\n", comment[1]); 588c952560cSray fprintf(fp, ">Priority:\t%s\n", comment[2]); 589c952560cSray fprintf(fp, ">Category:\t%s\n", comment[3]); 590c952560cSray fprintf(fp, ">Class:\t\t%s\n", comment[4]); 591c952560cSray fprintf(fp, ">Release:\t%s\n", comment[5]); 5925ae6585fSray fprintf(fp, ">Environment:\n"); 593c952560cSray fprintf(fp, "\t%s\n", comment[6]); 5945ae6585fSray fprintf(fp, "\tSystem : %s %s\n", os, rel); 595e051e8faSderaadt fprintf(fp, "\tDetails : %s\n", details); 5965ae6585fSray fprintf(fp, "\tArchitecture: %s.%s\n", os, mach); 5975ae6585fSray fprintf(fp, "\tMachine : %s\n", mach); 5985ae6585fSray fprintf(fp, ">Description:\n"); 599c952560cSray fprintf(fp, "\t%s\n", comment[7]); 6005ae6585fSray fprintf(fp, ">How-To-Repeat:\n"); 601c952560cSray fprintf(fp, "\t%s\n", comment[8]); 6025ae6585fSray fprintf(fp, ">Fix:\n"); 603c952560cSray fprintf(fp, "\t%s\n", comment[9]); 604aad8b29bSray 605aad8b29bSray if (!Dflag) 606aad8b29bSray dmesg(fp); 6075ae6585fSray } 608