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