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