Annotation of src/usr.bin/sendbug/sendbug.c, Revision 1.1
1.1 ! ray 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Written by Ray Lai <ray@cyth.net>.
! 5: * Public domain.
! 6: */
! 7:
! 8: #include <sys/types.h>
! 9: #include <sys/mman.h>
! 10: #include <sys/param.h>
! 11: #include <sys/stat.h>
! 12: #include <sys/sysctl.h>
! 13: #include <sys/wait.h>
! 14:
! 15: #include <err.h>
! 16: #include <errno.h>
! 17: #include <fcntl.h>
! 18: #include <limits.h>
! 19: #include <paths.h>
! 20: #include <pwd.h>
! 21: #include <stdio.h>
! 22: #include <stdlib.h>
! 23: #include <string.h>
! 24: #include <unistd.h>
! 25:
! 26: #include "atomicio.h"
! 27:
! 28: int init(void);
! 29: int prompt(void);
! 30: int send_file(const char *, int dst);
! 31: int sendmail(const char *);
! 32: void template(FILE *);
! 33:
! 34: struct passwd *pw;
! 35: const char *categories = "system user library documentation ports kernel "
! 36: "alpha amd64 arm i386 m68k m88k mips ppc sgi sparc sparc64 vax";
! 37: char os[BUFSIZ], rel[BUFSIZ], mach[BUFSIZ];
! 38: char *fullname;
! 39:
! 40: int
! 41: main(int argc, char *argv[])
! 42: {
! 43: struct stat sb;
! 44: FILE *fp;
! 45: const char *editor, *tmpdir;
! 46: char *tmppath = NULL;
! 47: time_t mtime;
! 48: int c, fd, ret = 1;
! 49:
! 50: if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
! 51: tmpdir = _PATH_TMP;
! 52: if (asprintf(&tmppath, "%s/p.XXXXXXXXXX", tmpdir) == -1) {
! 53: warn("asprintf");
! 54: goto quit;
! 55: }
! 56: if ((fd = mkstemp(tmppath)) == -1)
! 57: err(1, "mkstemp");
! 58: if ((fp = fdopen(fd, "w+")) == NULL) {
! 59: warn("fdopen");
! 60: goto cleanup;
! 61: }
! 62:
! 63: if (init() == -1)
! 64: goto cleanup;
! 65:
! 66: template(fp);
! 67:
! 68: if (fflush(fp) == EOF || fstat(fd, &sb) == -1 || fclose(fp) == EOF) {
! 69: warn("error creating template");
! 70: goto cleanup;
! 71: }
! 72: mtime = sb.st_mtime;
! 73:
! 74: edit:
! 75: if ((editor = getenv("EDITOR")) == NULL)
! 76: editor = "vi";
! 77: switch (fork()) {
! 78: case -1:
! 79: warn("fork");
! 80: goto cleanup;
! 81: case 0:
! 82: execlp(editor, editor, tmppath, NULL);
! 83: err(1, "execlp");
! 84: default:
! 85: wait(NULL);
! 86: break;
! 87: }
! 88:
! 89: if (stat(tmppath, &sb) == -1) {
! 90: warn("stat");
! 91: goto cleanup;
! 92: }
! 93: if (mtime == sb.st_mtime) {
! 94: warnx("report unchanged, nothing sent");
! 95: goto cleanup;
! 96: }
! 97:
! 98: prompt:
! 99: c = prompt();
! 100: switch (c) {
! 101: case 'a': case EOF:
! 102: warnx("unsent report in %s", tmppath);
! 103: goto quit;
! 104: case 'e':
! 105: goto edit;
! 106: case 's':
! 107: if (sendmail(tmppath) == -1)
! 108: goto quit;
! 109: break;
! 110: default:
! 111: goto prompt;
! 112: }
! 113:
! 114: ret = 0;
! 115:
! 116: cleanup:
! 117: if (tmppath && unlink(tmppath) == -1)
! 118: warn("unlink");
! 119:
! 120: quit:
! 121: return (ret);
! 122: }
! 123:
! 124: int
! 125: prompt(void)
! 126: {
! 127: int c, ret;
! 128:
! 129: fpurge(stdin);
! 130: fprintf(stderr, "a)bort, e)dit, or s)end: ");
! 131: fflush(stderr);
! 132: ret = getchar();
! 133: if (ret == EOF || ret == '\n')
! 134: return (ret);
! 135: do {
! 136: c = getchar();
! 137: } while (c != EOF && c != '\n');
! 138: return (ret);
! 139: }
! 140:
! 141: int
! 142: sendmail(const char *tmppath)
! 143: {
! 144: int filedes[2];
! 145:
! 146: if (pipe(filedes) == -1) {
! 147: warn("pipe: unsent report in %s", tmppath);
! 148: return (-1);
! 149: }
! 150: switch (fork()) {
! 151: case -1:
! 152: warn("fork error: unsent report in %s",
! 153: tmppath);
! 154: return (-1);
! 155: case 0:
! 156: close(filedes[1]);
! 157: if (dup2(filedes[0], STDIN_FILENO) == -1) {
! 158: warn("dup2 error: unsent report in %s",
! 159: tmppath);
! 160: return (-1);
! 161: }
! 162: close(filedes[0]);
! 163: execl("/usr/sbin/sendmail", "sendmail",
! 164: "-oi", "-t", NULL);
! 165: warn("sendmail error: unsent report in %s",
! 166: tmppath);
! 167: return (-1);
! 168: default:
! 169: close(filedes[0]);
! 170: /* Pipe into sendmail. */
! 171: if (send_file(tmppath, filedes[1]) == -1) {
! 172: warn("send_file error: unsent report in %s",
! 173: tmppath);
! 174: return (-1);
! 175: }
! 176: close(filedes[1]);
! 177: wait(NULL);
! 178: break;
! 179: }
! 180: return (0);
! 181: }
! 182:
! 183: int
! 184: init(void)
! 185: {
! 186: size_t len;
! 187: int sysname[2];
! 188:
! 189: if ((pw = getpwuid(getuid())) == NULL) {
! 190: warn("getpwuid");
! 191: return (-1);
! 192: }
! 193:
! 194: /* Get full name. */
! 195: len = strcspn(pw->pw_gecos, ",");
! 196: if ((fullname = malloc(len + 1)) == NULL) {
! 197: warn("malloc");
! 198: return (-1);
! 199: }
! 200: memcpy(fullname, pw->pw_gecos, len);
! 201: fullname[len] = '\0';
! 202:
! 203: sysname[0] = CTL_KERN;
! 204: sysname[1] = KERN_OSTYPE;
! 205: len = sizeof(os) - 1;
! 206: if (sysctl(sysname, 2, &os, &len, NULL, 0) == -1) {
! 207: warn("sysctl");
! 208: return (-1);
! 209: }
! 210:
! 211: sysname[0] = CTL_KERN;
! 212: sysname[1] = KERN_OSRELEASE;
! 213: len = sizeof(rel) - 1;
! 214: if (sysctl(sysname, 2, &rel, &len, NULL, 0) == -1) {
! 215: warn("sysctl");
! 216: return (-1);
! 217: }
! 218:
! 219: sysname[0] = CTL_HW;
! 220: sysname[1] = HW_MACHINE;
! 221: len = sizeof(mach) - 1;
! 222: if (sysctl(sysname, 2, &mach, &len, NULL, 0) == -1) {
! 223: warn("sysctl");
! 224: return (-1);
! 225: }
! 226:
! 227: return (0);
! 228: }
! 229:
! 230: int
! 231: send_file(const char *file, int dst)
! 232: {
! 233: FILE *fp;
! 234: char *buf;
! 235: size_t len;
! 236: int blank = 0;
! 237:
! 238: if ((fp = fopen(file, "r")) == NULL)
! 239: return (-1);
! 240: while ((buf = fgetln(fp, &len))) {
! 241: /* Skip lines starting with "SENDBUG". */
! 242: if (len >= sizeof("SENDBUG") - 1 &&
! 243: memcmp(buf, "SENDBUG", sizeof("SENDBUG") - 1) == 0)
! 244: continue;
! 245: if (len == 1 && buf[0] == '\n')
! 246: blank = 1;
! 247: /* Skip comments, but only if we encountered a blank line. */
! 248: while (len) {
! 249: char *sp, *ep = NULL;
! 250: size_t copylen;
! 251:
! 252: if (blank && (sp = memchr(buf, '<', len)) != NULL)
! 253: ep = memchr(sp, '>', len - (sp - buf + 1));
! 254: /* Length of string before comment. */
! 255: copylen = ep ? sp - buf : len;
! 256: if (atomicio(vwrite, dst, buf, copylen) != copylen) {
! 257: int saved_errno = errno;
! 258:
! 259: fclose(fp);
! 260: errno = saved_errno;
! 261: return (-1);
! 262: }
! 263: if (!ep)
! 264: break;
! 265: /* Skip comment. */
! 266: len -= ep - buf + 1;
! 267: buf = ep + 1;
! 268: }
! 269: }
! 270: fclose(fp);
! 271: return (0);
! 272: }
! 273:
! 274: void
! 275: template(FILE *fp)
! 276: {
! 277: fprintf(fp, "SENDBUG: -*- sendbug -*-\n");
! 278: fprintf(fp, "SENDBUG: Lines starting with `SENDBUG' will be removed automatically, as\n");
! 279: fprintf(fp, "SENDBUG: will all comments (text enclosed in `<' and `>'). \n");
! 280: fprintf(fp, "SENDBUG:\n");
! 281: fprintf(fp, "SENDBUG: Choose from the following categories:\n");
! 282: fprintf(fp, "SENDBUG:\n");
! 283: fprintf(fp, "SENDBUG: %s\n", categories);
! 284: fprintf(fp, "SENDBUG:\n");
! 285: fprintf(fp, "SENDBUG:\n");
! 286: fprintf(fp, "To: %s\n", "gnats@openbsd.org");
! 287: fprintf(fp, "Subject: \n");
! 288: fprintf(fp, "From: %s\n", pw->pw_name);
! 289: fprintf(fp, "Cc: \n");
! 290: fprintf(fp, "Reply-To: %s\n", pw->pw_name);
! 291: fprintf(fp, "X-sendbug-version: 4.2\n");
! 292: fprintf(fp, "\n");
! 293: fprintf(fp, "\n");
! 294: fprintf(fp, ">Submitter-Id:\tnet\n");
! 295: fprintf(fp, ">Originator:\t%s\n", fullname);
! 296: fprintf(fp, ">Organization:\n");
! 297: fprintf(fp, "net\n");
! 298: fprintf(fp, ">Synopsis:\t<synopsis of the problem (one line)>\n");
! 299: fprintf(fp, ">Severity:\t<[ non-critical | serious | critical ] (one line)>\n");
! 300: fprintf(fp, ">Priority:\t<[ low | medium | high ] (one line)>\n");
! 301: fprintf(fp, ">Category:\t<PR category (one line)>\n");
! 302: fprintf(fp, ">Class:\t\t<[ sw-bug | doc-bug | change-request | support ] (one line)>\n");
! 303: fprintf(fp, ">Release:\t<release number or tag (one line)>\n");
! 304: fprintf(fp, ">Environment:\n");
! 305: fprintf(fp, "\t<machine, os, target, libraries (multiple lines)>\n");
! 306: fprintf(fp, "\tSystem : %s %s\n", os, rel);
! 307: fprintf(fp, "\tArchitecture: %s.%s\n", os, mach);
! 308: fprintf(fp, "\tMachine : %s\n", mach);
! 309: fprintf(fp, ">Description:\n");
! 310: fprintf(fp, "\t<precise description of the problem (multiple lines)>\n");
! 311: fprintf(fp, ">How-To-Repeat:\n");
! 312: fprintf(fp, "\t<code/input/activities to reproduce the problem (multiple lines)>\n");
! 313: fprintf(fp, ">Fix:\n");
! 314: fprintf(fp, "\t<how to correct or work around the problem, if known (multiple lines)>\n");
! 315: }