1 /* $OpenBSD: sendbug.c,v 1.2 2007/03/23 02:11:00 deraadt Exp $ */ 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; 45 int c, fd, ret = 1; 46 struct stat sb; 47 time_t mtime; 48 FILE *fp; 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: 82 execlp(editor, editor, tmppath, (void *)NULL); 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", 164 "-oi", "-t", (void *)NULL); 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 { 233 int blank = 0; 234 size_t len; 235 char *buf; 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 } 316