Annotation of src/usr.bin/sendbug/sendbug.c, Revision 1.12
1.12 ! tedu 1: /* $OpenBSD: sendbug.c,v 1.11 2007/03/23 03:43:46 deraadt Exp $ */
1.1 ray 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:
1.12 ! tedu 28: void init(void);
1.1 ray 29: int prompt(void);
30: int send_file(const char *, int dst);
31: int sendmail(const char *);
32: void template(FILE *);
33:
34: const char *categories = "system user library documentation ports kernel "
35: "alpha amd64 arm i386 m68k m88k mips ppc sgi sparc sparc64 vax";
1.11 deraadt 36: char *version = "4.2";
37:
38: struct passwd *pw;
1.1 ray 39: char os[BUFSIZ], rel[BUFSIZ], mach[BUFSIZ];
40: char *fullname;
1.12 ! tedu 41: char *tmppath;
! 42: int wantcleanup;
1.11 deraadt 43:
1.12 ! tedu 44: __dead void
1.3 deraadt 45: usage(void)
46: {
1.6 deraadt 47: fprintf(stderr, "usage: sendbug [-LPV]\n");
1.12 ! tedu 48: exit(1);
! 49: }
! 50:
! 51: void
! 52: cleanup()
! 53: {
! 54: if (wantcleanup && tmppath && unlink(tmppath) == -1)
! 55: warn("unlink");
1.3 deraadt 56: }
57:
1.12 ! tedu 58:
1.1 ray 59: int
60: main(int argc, char *argv[])
61: {
62: const char *editor, *tmpdir;
1.12 ! tedu 63: char *argp[] = {"sh", "-c", NULL, NULL}, *pr_form;
1.3 deraadt 64: int ch, c, fd, ret = 1;
1.2 deraadt 65: struct stat sb;
1.1 ray 66: time_t mtime;
1.2 deraadt 67: FILE *fp;
1.3 deraadt 68:
1.5 deraadt 69: while ((ch = getopt(argc, argv, "LPV")) != -1)
1.3 deraadt 70: switch (ch) {
71: case 'L':
72: printf("Known categories:\n");
73: printf("%s\n\n", categories);
74: exit(0);
75: case 'P':
1.12 ! tedu 76: init();
1.3 deraadt 77: template(stdout);
78: exit(0);
1.5 deraadt 79: case 'V':
80: printf("%s\n", version);
81: exit(0);
1.3 deraadt 82: default:
83: usage();
1.7 deraadt 84: }
85:
1.3 deraadt 86: if (argc > 1) {
87: usage();
88: }
1.1 ray 89:
90: if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
91: tmpdir = _PATH_TMP;
1.9 ray 92: if (asprintf(&tmppath, "%s%sp.XXXXXXXXXX", tmpdir,
93: tmpdir[strlen(tmpdir) - 1] == '/' ? "" : "/") == -1) {
1.12 ! tedu 94: err(1, "asprintf");
1.1 ray 95: }
96: if ((fd = mkstemp(tmppath)) == -1)
97: err(1, "mkstemp");
1.12 ! tedu 98: wantcleanup = 1;
! 99: atexit(cleanup);
1.1 ray 100: if ((fp = fdopen(fd, "w+")) == NULL) {
1.12 ! tedu 101: err(1, "fdopen");
1.1 ray 102: }
103:
1.12 ! tedu 104: init();
1.1 ray 105:
1.10 deraadt 106: pr_form = getenv("PR_FORM");
107: if (pr_form) {
108: char buf[BUFSIZ];
109: size_t len;
110: FILE *frfp;
111:
112: frfp = fopen(pr_form, "r");
113: if (frfp == NULL) {
114: fprintf(stderr, "sendbug: can't seem to read your"
115: " template file (`%s'), ignoring PR_FORM\n",
116: pr_form);
117: template(fp);
118: } else {
119: while (!feof(frfp)) {
120: len = fread(buf, 1, sizeof buf, frfp);
121: if (len == 0)
122: break;
123: if (fwrite(buf, 1, len, fp) != len)
124: break;
125: }
126: fclose(frfp);
127: }
128: } else
129: template(fp);
1.1 ray 130:
131: if (fflush(fp) == EOF || fstat(fd, &sb) == -1 || fclose(fp) == EOF) {
1.12 ! tedu 132: err(1, "error creating template");
1.1 ray 133: }
134: mtime = sb.st_mtime;
135:
136: edit:
137: if ((editor = getenv("EDITOR")) == NULL)
138: editor = "vi";
139: switch (fork()) {
140: case -1:
1.12 ! tedu 141: err(1, "fork");
1.1 ray 142: case 0:
1.12 ! tedu 143: wantcleanup = 0;
1.8 ray 144: if (asprintf(&argp[2], "%s %s", editor, tmppath) == -1)
145: err(1, "asprintf");
146: execv(_PATH_BSHELL, argp);
147: err(1, "execv");
1.1 ray 148: default:
149: wait(NULL);
150: break;
151: }
152:
153: if (stat(tmppath, &sb) == -1) {
1.12 ! tedu 154: err(1, "stat");
1.1 ray 155: }
156: if (mtime == sb.st_mtime) {
1.12 ! tedu 157: errx(1, "report unchanged, nothing sent");
1.1 ray 158: }
159:
160: prompt:
161: c = prompt();
162: switch (c) {
1.5 deraadt 163: case 'a':
164: case EOF:
1.12 ! tedu 165: wantcleanup = 0;
! 166: errx(1, "unsent report in %s", tmppath);
1.1 ray 167: case 'e':
168: goto edit;
169: case 's':
170: if (sendmail(tmppath) == -1)
171: goto quit;
172: break;
173: default:
174: goto prompt;
175: }
176:
177: ret = 0;
1.12 ! tedu 178: quit:
1.1 ray 179: return (ret);
180: }
181:
1.12 ! tedu 182:
1.1 ray 183: int
184: prompt(void)
185: {
186: int c, ret;
187:
188: fpurge(stdin);
189: fprintf(stderr, "a)bort, e)dit, or s)end: ");
190: fflush(stderr);
191: ret = getchar();
192: if (ret == EOF || ret == '\n')
193: return (ret);
194: do {
195: c = getchar();
196: } while (c != EOF && c != '\n');
197: return (ret);
198: }
199:
200: int
201: sendmail(const char *tmppath)
202: {
203: int filedes[2];
204:
205: if (pipe(filedes) == -1) {
206: warn("pipe: unsent report in %s", tmppath);
207: return (-1);
208: }
209: switch (fork()) {
210: case -1:
211: warn("fork error: unsent report in %s",
212: tmppath);
213: return (-1);
214: case 0:
215: close(filedes[1]);
216: if (dup2(filedes[0], STDIN_FILENO) == -1) {
217: warn("dup2 error: unsent report in %s",
218: tmppath);
219: return (-1);
220: }
221: close(filedes[0]);
222: execl("/usr/sbin/sendmail", "sendmail",
1.2 deraadt 223: "-oi", "-t", (void *)NULL);
1.1 ray 224: warn("sendmail error: unsent report in %s",
225: tmppath);
226: return (-1);
227: default:
228: close(filedes[0]);
229: /* Pipe into sendmail. */
230: if (send_file(tmppath, filedes[1]) == -1) {
231: warn("send_file error: unsent report in %s",
232: tmppath);
233: return (-1);
234: }
235: close(filedes[1]);
236: wait(NULL);
237: break;
238: }
239: return (0);
240: }
241:
1.12 ! tedu 242: void
1.1 ray 243: init(void)
244: {
245: size_t len;
246: int sysname[2];
247:
248: if ((pw = getpwuid(getuid())) == NULL) {
1.12 ! tedu 249: err(1, "getpwuid");
1.1 ray 250: }
251:
252: /* Get full name. */
253: len = strcspn(pw->pw_gecos, ",");
254: if ((fullname = malloc(len + 1)) == NULL) {
1.12 ! tedu 255: err(1, "malloc");
1.1 ray 256: }
257: memcpy(fullname, pw->pw_gecos, len);
258: fullname[len] = '\0';
259:
260: sysname[0] = CTL_KERN;
261: sysname[1] = KERN_OSTYPE;
262: len = sizeof(os) - 1;
263: if (sysctl(sysname, 2, &os, &len, NULL, 0) == -1) {
1.12 ! tedu 264: err(1, "sysctl");
1.1 ray 265: }
266:
267: sysname[0] = CTL_KERN;
268: sysname[1] = KERN_OSRELEASE;
269: len = sizeof(rel) - 1;
270: if (sysctl(sysname, 2, &rel, &len, NULL, 0) == -1) {
1.12 ! tedu 271: err(1, "sysctl");
1.1 ray 272: }
273:
274: sysname[0] = CTL_HW;
275: sysname[1] = HW_MACHINE;
276: len = sizeof(mach) - 1;
277: if (sysctl(sysname, 2, &mach, &len, NULL, 0) == -1) {
1.12 ! tedu 278: err(1, "sysctl");
1.1 ray 279: }
280:
281: }
282:
283: int
284: send_file(const char *file, int dst)
285: {
1.2 deraadt 286: int blank = 0;
287: size_t len;
288: char *buf;
1.1 ray 289: FILE *fp;
290:
291: if ((fp = fopen(file, "r")) == NULL)
292: return (-1);
293: while ((buf = fgetln(fp, &len))) {
294: /* Skip lines starting with "SENDBUG". */
295: if (len >= sizeof("SENDBUG") - 1 &&
296: memcmp(buf, "SENDBUG", sizeof("SENDBUG") - 1) == 0)
297: continue;
298: if (len == 1 && buf[0] == '\n')
299: blank = 1;
300: /* Skip comments, but only if we encountered a blank line. */
301: while (len) {
302: char *sp, *ep = NULL;
303: size_t copylen;
304:
305: if (blank && (sp = memchr(buf, '<', len)) != NULL)
306: ep = memchr(sp, '>', len - (sp - buf + 1));
307: /* Length of string before comment. */
1.4 ray 308: if (ep)
309: copylen = sp - buf;
310: else
311: copylen = len;
1.1 ray 312: if (atomicio(vwrite, dst, buf, copylen) != copylen) {
313: int saved_errno = errno;
314:
315: fclose(fp);
316: errno = saved_errno;
317: return (-1);
318: }
319: if (!ep)
320: break;
321: /* Skip comment. */
322: len -= ep - buf + 1;
323: buf = ep + 1;
324: }
325: }
326: fclose(fp);
327: return (0);
328: }
329:
330: void
331: template(FILE *fp)
332: {
333: fprintf(fp, "SENDBUG: -*- sendbug -*-\n");
334: fprintf(fp, "SENDBUG: Lines starting with `SENDBUG' will be removed automatically, as\n");
335: fprintf(fp, "SENDBUG: will all comments (text enclosed in `<' and `>'). \n");
336: fprintf(fp, "SENDBUG:\n");
337: fprintf(fp, "SENDBUG: Choose from the following categories:\n");
338: fprintf(fp, "SENDBUG:\n");
339: fprintf(fp, "SENDBUG: %s\n", categories);
340: fprintf(fp, "SENDBUG:\n");
341: fprintf(fp, "SENDBUG:\n");
342: fprintf(fp, "To: %s\n", "gnats@openbsd.org");
343: fprintf(fp, "Subject: \n");
344: fprintf(fp, "From: %s\n", pw->pw_name);
345: fprintf(fp, "Cc: \n");
346: fprintf(fp, "Reply-To: %s\n", pw->pw_name);
1.5 deraadt 347: fprintf(fp, "X-sendbug-version: %s\n", version);
1.1 ray 348: fprintf(fp, "\n");
349: fprintf(fp, "\n");
350: fprintf(fp, ">Submitter-Id:\tnet\n");
351: fprintf(fp, ">Originator:\t%s\n", fullname);
352: fprintf(fp, ">Organization:\n");
353: fprintf(fp, "net\n");
354: fprintf(fp, ">Synopsis:\t<synopsis of the problem (one line)>\n");
355: fprintf(fp, ">Severity:\t<[ non-critical | serious | critical ] (one line)>\n");
356: fprintf(fp, ">Priority:\t<[ low | medium | high ] (one line)>\n");
357: fprintf(fp, ">Category:\t<PR category (one line)>\n");
358: fprintf(fp, ">Class:\t\t<[ sw-bug | doc-bug | change-request | support ] (one line)>\n");
359: fprintf(fp, ">Release:\t<release number or tag (one line)>\n");
360: fprintf(fp, ">Environment:\n");
361: fprintf(fp, "\t<machine, os, target, libraries (multiple lines)>\n");
362: fprintf(fp, "\tSystem : %s %s\n", os, rel);
363: fprintf(fp, "\tArchitecture: %s.%s\n", os, mach);
364: fprintf(fp, "\tMachine : %s\n", mach);
365: fprintf(fp, ">Description:\n");
366: fprintf(fp, "\t<precise description of the problem (multiple lines)>\n");
367: fprintf(fp, ">How-To-Repeat:\n");
368: fprintf(fp, "\t<code/input/activities to reproduce the problem (multiple lines)>\n");
369: fprintf(fp, ">Fix:\n");
370: fprintf(fp, "\t<how to correct or work around the problem, if known (multiple lines)>\n");
371: }