--- /dev/null
+.\" Copyright (c) 2017 Ian Sutton <ian@ce.gl>$
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: June 10 2017 $
+.Dt PUFFCRASH 1
+.Os
+.Sh NAME
+.Nm puffcrash
+.Nd OpenBSD kernel debugging assitant
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar config
+.Op Ar serial\ console\ log
+.Sh DESCRIPTION
+.Nm
+reads serial terminal output from OpenBSD machines, listening for
+kernel panics.
+When detected (and following 'trace' command is issued to
+.Xr ddb 4 ,
+puffcrash tries to ascertain the source lines implicated in the crash via
+.Xr objdump 1
+and source tags (usually generated by `make tags`), displaying them if succesful.
+.Pp
+.Nm
+reads from standard input if logfile argument is omitted.
+.Sh SEE ALSO
+.Xr ddb 4
+.Sh HISTORY
+.Nm
+does not appear in
+.Ox 6.1
+currently.
+.Sh AUTHORS
+.An -nosplit
+.Nm
+was written by
+.An Ian Sutton Aq Mt ian@ce.gl .
*/
#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <signal.h>
+
+#define LINE_SIZE 4096
+
+struct config {
+ char *obj_path;
+ char *tool_path;
+ char *src_path;
+ FILE *conf;
+ FILE *term;
+} *gcfg;
+
+struct sigaction sigact;
+static void sigh(int);
+static void sigc(void);
+
+static void __dead
+usage(void)
+{
+ fprintf(stderr, "usage: puffcrash [-c config] [serial console log]\n");
+ exit(1);
+}
+
+void
+sigh(int sig)
+{
+ if (sig == SIGINT) sigc();
+}
+
+void
+sigc(void)
+{
+ if (gcfg->obj_path) free(gcfg->obj_path);
+ if (gcfg->tool_path) free(gcfg->tool_path);
+ if (gcfg->src_path) free(gcfg->src_path);
+
+ if (gcfg->conf) fclose(gcfg->conf);
+ if (gcfg->term && gcfg->term != stdin) fclose(gcfg->term);
+
+ exit(1);
+}
+
+int
+parse_config(char *config_path, struct config *cfg)
+{
+ char *line, *i;
+ int check = 0;
+
+ cfg->conf = fopen(config_path, "r");
+
+ if(!(line = calloc(1, 256)) || !cfg->conf)
+ return 1;
+
+ while ((fgets(line, 256, cfg->conf))) {
+ if (strstr(line, "obj_path")) {
+ i = strstr(line, "=") + 2;
+ strlcpy(cfg->obj_path, i, PATH_MAX);
+ check |= (1 << 0);
+ } else if (strstr(line, "tool_path")) {
+ i = strstr(line, "=") + 2;
+ strlcpy(cfg->tool_path, i, PATH_MAX);
+ check |= (1 << 1);
+ } else if (strstr(line, "src_path")) {
+ i = strstr(line, "=") + 2;
+ strlcpy(cfg->src_path, i, PATH_MAX);
+ check |= (1 << 2);
+ }
+ }
+
+ free(line);
+
+ if (check == 0x7)
+ return 0;
+ else
+ return 1;
+}
+
+void
+watch_serial(void)
+{
+ char *line;
+
+ if (!(line = calloc(1, LINE_SIZE))) {
+ printf("could not allocate memory\n");
+ sigc();
+ }
+
+ while ((fgets(line, LINE_SIZE, gcfg->term))) {
+ printf("%s", line);
+ }
+}
int
main(int argc, char *argv[])
{
+ struct config cfg;
+ int ch;
+ char term_path[PATH_MAX], config_path[PATH_MAX];
+
+ bzero(&cfg, sizeof(struct config));
+ strlcpy(config_path, "/etc/puffcrash.conf", PATH_MAX);
+
+ atexit(sigc);
+ sigact.sa_handler = sigh;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction(SIGINT, &sigact, (struct sigaction *) NULL);
+
+ while ((ch = getopt(argc, argv, "c:")) != -1) {
+ switch (ch) {
+ case 'c':
+ strlcpy(config_path, optarg, PATH_MAX);
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ cfg.obj_path = calloc(1, PATH_MAX);
+ cfg.tool_path = calloc(1, PATH_MAX);
+ cfg.src_path = calloc(1, PATH_MAX);
+
+ gcfg = &cfg;
+
+ if (parse_config(config_path, &cfg)) {
+ printf("failed to parse config file %s.\n", config_path);
+ sigc();
+ }
+
+ if (argc) {
+ strlcpy(term_path, argv[0], PATH_MAX);
+ cfg.term = fopen(term_path, "r");
+ } else
+ cfg.term = stdin;
+
+ if (!cfg.term) {
+ printf("failed to open serial console output logfile \"%s\"\n", argv[0]);
+ sigc();
+ }
+
+ watch_serial();
+
return 0;
}