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