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