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