Add CRASHREPORT, show contact form on panic/impossible

When calling panic() or impossible(), create the option
of opening a browser window with most of the fields
already populated.  Code for MacOS and linux is included;
other ports are affected by argument change to early_init
which are done but not tested.

To enable, define CRASHREPORT in config.h and set
CRASHREPORTURL in sysconf to (for the moment at least)
http[s]://www.nethack.org/common/contactcr.html

Adds --grep-defined option to makedefs for Makefiles.

Adds "bid" (binary identifier), an MD4 of the main nethack
binary.  This is ONLY for helping (in the future) contact.html
to set the "NetHack from" field automatically for our own
binaries.  This can be faked, but the user can lie so nothing
lost.  There's nothing magic about MD4; other ports can use
anything that prodcues a long apparently random string we can
match against.

- new option --bidshow for us to get the MD4 of a
  released binary so I can add it to the website.
  Only available in wizard mode and not in nethack.6.
- typo macos -> macosx in hints file

No support for packaging builds as I'm not sure what that
would look like.

Adds a javascript helper for MacOS.
Adds a lua helper for linux (and builds and installs
 nhlua).
This commit is contained in:
nhkeni
2023-08-07 13:26:27 -04:00
parent 9f16eaa0a5
commit 8c095b009a
30 changed files with 633 additions and 204 deletions

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 allmain.c $NHDT-Date: 1688415115 2023/07/03 20:11:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.219 $ */
/* NetHack 3.7 allmain.c $NHDT-Date: 1693359544 2023/08/30 01:39:04 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.220 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -26,9 +26,14 @@ static void debug_fields(const char *);
static void dump_enums(void);
#endif
/*ARGSUSED*/
void
early_init(void)
early_init(int argc UNUSED, char *argv[] UNUSED)
{
#ifdef CRASHREPORT
// Do this as early as possible, but let ports do other things first.
crashreport_init(argc, argv);
#endif
decl_globals_init();
objects_globals_init();
monst_globals_init();
@@ -906,6 +911,9 @@ static const struct early_opt earlyopts[] = {
#ifdef WIN32
{ ARG_WINDOWS, "windows", 4, TRUE },
#endif
#ifdef CRASHREPORT
{ ARG_BIDSHOW, "bidshow", 7, FALSE },
#endif
};
#ifdef WIN32
@@ -990,6 +998,11 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg)
dump_glyphids();
return 2;
#endif
#ifdef CRASHREPORT
case ARG_BIDSHOW:
crashreport_bidshow();
return 2;
#endif
#ifdef WIN32
case ARG_WINDOWS:
if (extended_opt) {

263
src/end.c
View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 end.c $NHDT-Date: 1685863329 2023/06/04 07:22:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.274 $ */
/* NetHack 3.7 end.c $NHDT-Date: 1693519356 2023/08/31 22:02:36 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.277 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -47,7 +47,7 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN;
#define done_stopprint gp.program_state.stopprint
#ifndef PANICTRACE
#define NH_abort NH_abort_
#define NH_abort(x) NH_abort_
#endif
#ifdef AMIGA
@@ -77,6 +77,10 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN;
* gdb: +gives more detailed information
* +works on more OS versions
* -requires -g, which may preclude -O on some compilers
*
* And the UI: if sysopt.crashreporturl, and defined(CRASHREPORT)
* we gather the stacktrace (etc) and launch a helper to submit a bug report
* otherwise we just use stdout. Requires libc for now.
*/
#ifdef SYSCF
#define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
@@ -94,11 +98,11 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN;
#endif
#endif
static void NH_abort(void);
static void NH_abort(char *);
#ifndef NO_SIGNAL
static void panictrace_handler(int);
#endif
static boolean NH_panictrace_libc(void);
static boolean NH_panictrace_libc(char *);
static boolean NH_panictrace_gdb(void);
#ifndef NO_SIGNAL
@@ -126,7 +130,7 @@ panictrace_handler(int sig_unused UNUSED)
f2 = (int) write(2, SIG_MSG, sizeof SIG_MSG - 1);
nhUse(f2); /* what could we do if write to fd#2 (stderr) fails */
NH_abort(); /* ... and we're already in the process of quitting? */
NH_abort(NULL); /* ... and we're already in the process of quitting? */
}
void
@@ -166,7 +170,7 @@ panictrace_setsignals(boolean set)
#endif /* NO_SIGNAL */
static void
NH_abort(void)
NH_abort(char *why)
{
int gdb_prio = SYSOPT_PANICTRACE_GDB;
int libc_prio = SYSOPT_PANICTRACE_LIBC;
@@ -183,9 +187,9 @@ NH_abort(void)
gdb_prio++;
if (gdb_prio > libc_prio) {
(void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc()));
(void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc(why)));
} else {
(void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb()));
(void) (NH_panictrace_libc(why) || (gdb_prio && NH_panictrace_gdb()));
}
#else /* VMS */
@@ -203,9 +207,230 @@ NH_abort(void)
NH_abort_();
}
/* Build a URL with a query string and try to launch a new browser window
* to report from panic() or impossible(). Requires libc support for
* the stacktrace. Uses memory on the stack to avoid memory allocation
* (but libc can still do anything it wants). */
/* size of argument list for execve(2) */
#define SWR_LINES 20
/* max stack frames and header lines (details field) */
#define SWR_FRAMES 20
#define SWR_ADD(line) {if(xargc<(SWR_LINES-1)) xargv[xargc++] = line;}
#ifdef CRASHREPORT
# include <fcntl.h>
# ifdef MACOS
# include <CommonCrypto/CommonDigest.h>
# define HASH_PRAGMA_START
# define HASH_PRAGMA_END
# define HASH_CONTEXT CC_MD4_CTX
# define HASH_INIT(ctx) CC_MD4_Init(ctx)
# define HASH_UPDATE(ctx, ptr, len) CC_MD4_Update(ctx, ptr, len)
# define HASH_FINISH(ctx, out) CC_MD4_Final(out, ctx)
# define HASH_RESULT_SIZE CC_MD4_DIGEST_LENGTH
# endif
# ifdef __linux__
# include <openssl/md4.h>
# define HASH_PRAGMA_START \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
# define HASH_PRAGMA_END _Pragma("GCC diagnostic pop");
# define HASH_CONTEXT MD4_CTX
# define HASH_INIT(ctx) MD4_Init(ctx)
# define HASH_UPDATE(ctx, ptr, len) MD4_Update(ctx, ptr, len)
# define HASH_FINISH(ctx, out) MD4_Final(out, ctx)
# define HASH_RESULT_SIZE MD4_DIGEST_LENGTH
# endif
// Binary ID - Use only as a hint to contact.html for recognizing our own
// binaries. This is easily spoofed!
static char bid[(2*HASH_RESULT_SIZE)+1];
/* ARGSUSED */
void
crashreport_init(int argc UNUSED, char *argv[] UNUSED){
unsigned char tmp[HASH_RESULT_SIZE];
HASH_PRAGMA_START
HASH_CONTEXT ctx;
HASH_INIT(&ctx);
#ifdef MACOS
char *binfile = argv[0];
if(!binfile || !*binfile){
# ifdef BETA
// If this triggers, investigate CFBundleGetMainBundle
// or CFBundleCopyExecutableURL.
raw_print("BETA warning: crashreport_init called without useful info");
# endif
goto skip;
}
#endif
#ifdef __linux__
char binfile[PATH_MAX+1];
int len = readlink("/proc/self/exe", binfile, sizeof(binfile)-1);
if(len>0) {
binfile[len] = '\0';
} else {
goto skip;
}
#endif
int fd = open(binfile, O_RDONLY, 0);
if(fd == -1) {
# ifdef BETA
raw_printf("open e=%s",strerror(errno));
# endif
goto skip;
}
int segsize;
char segment[4096];
while(0 < (segsize = read(fd, segment,sizeof(segment)))){
HASH_UPDATE(&ctx, segment, segsize);
}
HASH_FINISH(&ctx, tmp);
close(fd);
char *p = bid;
unsigned char *in = &tmp[0];
char cnt=HASH_RESULT_SIZE;
while(cnt--){
p += snprintf(p, HASH_RESULT_SIZE-(p-bid), "%02x",*(in++));
}
*p = '\0';
return;
skip:
strncpy((char *)bid,"unknown",sizeof(bid)-1);
HASH_PRAGMA_END
}
#undef HASH_CONTEXT
#undef HASH_INIT
#undef HASH_UPDATE
#undef HASH_FINISH
#undef HASH_RESULT_SIZE
#undef HASH_PRAGMA_START
#undef HASH_PRAGMA_END
void
crashreport_bidshow(void){
raw_print(bid);
}
boolean
submit_web_report(const char *msg, char *why){
if(sysopt.crashreporturl){
const char *xargv[SWR_LINES];
char version[100];
char versionstring[200]; // used twice as a temp
int xargc = 0;
char wholetrace[SWR_LINES*80]; // XXX roughly 71 on MacOS, plus buffer
int prelines = 0; // count of lines in trace header
char nbuf[6]; // number buffer
extern char **environ;
pid_t pid;
SWR_ADD(CRASHREPORT);
SWR_ADD(sysopt.crashreporturl);
// then pairs of key value
// subject, generate something useful
SWR_ADD("subject");
snprintf(version, sizeof(version), "%s report for NetHack %s",
msg, version_string(versionstring, sizeof(versionstring)));
SWR_ADD(version);
// name: someday, this might be stored in nethackcnf
// email: someday, this might be stored in nethackcnf
// gitver, pull from version.c
SWR_ADD("gitver");
SWR_ADD(getversionstring(versionstring, sizeof(versionstring)));
// hardware: leave for user
// software: leave for user
// comments: leave for user
// details: stack trace
SWR_ADD("details");
// XXX header for wholetrace - what other info do we want?
// NB: prelines not tested against size of SWR_FRAMES.
#define SWR_HDR(line) \
if(endp<&wholetrace[sizeof(wholetrace)]){ \
endp+=snprintf(endp, sizeof(wholetrace)-(endp-wholetrace), "%s\n",line); \
prelines++; \
}
#define SWR_HDRnonl(line) \
if(endp<&wholetrace[sizeof(wholetrace)]){ \
endp+=snprintf(endp, sizeof(wholetrace)-(endp-wholetrace), "%s",line); \
}
char *endp = wholetrace;
wholetrace[0] = 0;
if(why){
SWR_HDR(why);
}
SWR_HDRnonl("bid: ");
SWR_HDR(bid);
void *bt[SWR_FRAMES];
int count, x;
char **info, buf[BUFSZ];
count = backtrace(bt, SIZE(bt));
info = backtrace_symbols(bt, count);
for (x = 0; x < count; x++) {
copynchars(buf, info[x], (int) sizeof buf - 1);
/* try to remove up to 16 blank spaces by removing 8 twice */
(void) strsubst(buf, " ", "");
(void) strsubst(buf, " ", "");
snprintf(endp, SWR_FRAMES*80-(endp-wholetrace), "[%02lu] %s\n",
(unsigned long) x, buf);
endp = eos(endp);
}
*(endp-1) = '\0'; // remove last newline
SWR_ADD(wholetrace);
// detailrows min(actual,50) Guess since we can't know the
// width of the window.
SWR_ADD("detailrows");
(void)snprintf(nbuf,sizeof(nbuf),"%d",count+prelines);
SWR_ADD(nbuf);
xargv[xargc++] = 0; // terminate array
pid = fork();
if( pid == 0 ){
execve(CRASHREPORT, (char * const *)xargv, environ);
char err[100];
sprintf(err, "Can't start " CRASHREPORT ": %s", strerror(errno));
raw_print(err);
} else {
int status;
errno=0;
// XXX do we _really_ know this is the right pid?
(void)waitpid(pid, &status, 0);
if(status){ // XXX check could be more precise
#if 0
// Not useful at the moment. XXX
char err[100];
sprintf(err, "pid=%d e=%d status=%0x",wpid,errno,status);
raw_print(err);
#endif
return FALSE;
}
}
/* free(info); -- Don't risk it. */
return TRUE;
}
return FALSE;
}
#endif // CRASHREPORT
#undef SWR_ADD
#undef SWR_FRAMES
#undef SWR_HDR
#undef SWR_LINES
/*ARGSUSED*/
static boolean
NH_panictrace_libc(void)
NH_panictrace_libc(char *why UNUSED)
{
#ifdef CRASHREPORT
if(submit_web_report("Panic",why)) return TRUE;
#endif
#ifdef PANICTRACE_LIBC
void *bt[20];
int count, x;
@@ -375,7 +600,7 @@ done2(void)
(*soundprocs.sound_exit_nhsound)("done2");
exit_nhwindows((char *) 0);
NH_abort();
NH_abort(NULL);
} else if (c == 'q')
done_stopprint++;
}
@@ -618,11 +843,12 @@ DISABLE_WARNING_FORMAT_NONLITERAL
ATTRNORETURN void
panic VA_DECL(const char *, str)
{
char buf[BUFSZ];
VA_START(str);
VA_INIT(str, char *);
if (gp.program_state.panicking++)
NH_abort(); /* avoid loops - this should never happen*/
NH_abort(NULL); /* avoid loops - this should never happen*/
if (iflags.window_inited) {
raw_print("\r\nOops...");
@@ -651,6 +877,7 @@ panic VA_DECL(const char *, str)
? "."
: "\nand it may be possible to rebuild.";
// XXX this is probably wrong if defined(CRASHREPORT)
if (sysopt.support)
raw_printf("To report this error, %s%s", sysopt.support,
maybe_rebuild);
@@ -674,19 +901,19 @@ panic VA_DECL(const char *, str)
}
}
#endif /* !MICRO */
{
char buf[BUFSZ];
(void) vsnprintf(buf, sizeof buf, str, VA_ARGS);
raw_print(buf);
paniclog("panic", buf);
}
(void) vsnprintf(buf, sizeof buf, str, VA_ARGS);
raw_print(buf);
paniclog("panic", buf);
#ifdef WIN32
interject(INTERJECT_PANIC);
#endif
#if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
# ifndef CRASHREPORT
if (wizard)
NH_abort(); /* generate core dump */
# endif
NH_abort(buf); /* generate core dump */
#endif
VA_END();
really_done(PANICKED);

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 files.c $NHDT-Date: 1680625799 2023/04/04 16:29:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.373 $ */
/* NetHack 3.7 files.c $NHDT-Date: 1693083234 2023/08/26 20:53:54 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.378 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Derek S. Ray, 2015. */
/* NetHack may be freely redistributed. See license for details. */
@@ -194,6 +194,7 @@ static boolean cnf_line_PANICTRACE_LIBC(char *);
static boolean cnf_line_PANICTRACE_GDB(char *);
static boolean cnf_line_GDBPATH(char *);
static boolean cnf_line_GREPPATH(char *);
static boolean cnf_line_CRASHREPORTURL(char *);
static boolean cnf_line_SAVEFORMAT(char *);
static boolean cnf_line_BONESFORMAT(char *);
static boolean cnf_line_ACCESSIBILITY(char *);
@@ -3011,6 +3012,15 @@ cnf_line_GREPPATH(char *bufp)
return TRUE;
}
static boolean
cnf_line_CRASHREPORTURL(char *bufp)
{
if (sysopt.crashreporturl)
free((genericptr_t) sysopt.crashreporturl);
sysopt.crashreporturl = dupstr(bufp);
return TRUE;
}
static boolean
cnf_line_SAVEFORMAT(char *bufp)
{
@@ -3253,6 +3263,7 @@ static const struct match_config_line_stmt {
CNFL_S(LIVELOG, 7),
CNFL_S(PANICTRACE_LIBC, 15),
CNFL_S(PANICTRACE_GDB, 14),
CNFL_S(CRASHREPORTURL, 13),
CNFL_S(GDBPATH, 7),
CNFL_S(GREPPATH, 7),
CNFL_S(SAVEFORMAT, 10),

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 pline.c $NHDT-Date: 1646255375 2022/03/02 21:09:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.109 $ */
/* NetHack 3.7 pline.c $NHDT-Date: 1693083243 2023/08/26 20:54:03 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.124 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2018. */
/* NetHack may be freely redistributed. See license for details. */
@@ -505,6 +505,7 @@ impossible(const char *s, ...)
{
va_list the_args;
char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
char pbuf2[BUFSZ];
va_start(the_args, s);
if (gp.program_state.in_impossible)
@@ -521,16 +522,26 @@ impossible(const char *s, ...)
gp.pline_flags = URGENT_MESSAGE;
pline("%s", pbuf);
gp.pline_flags = 0;
/* reuse pbuf[] */
Strcpy(pbuf, "Program in disorder!");
Strcpy(pbuf2, "Program in disorder!");
if (gp.program_state.something_worth_saving)
Strcat(pbuf, " (Saving and reloading may fix this problem.)");
pline("%s", pbuf);
Strcat(pbuf2, " (Saving and reloading may fix this problem.)");
pline("%s", pbuf2);
pline("Please report these messages to %s.", DEVTEAM_EMAIL);
if (sysopt.support) {
pline("Alternatively, contact local support: %s", sysopt.support);
}
#ifdef CRASHREPORT
if(sysopt.crashreporturl){
boolean report = ('y' == yn_function("Report now?","yn",'n',FALSE));
raw_print(""); // prove to the user the character was accepted
if(report){
submit_web_report("Impossible", pbuf);
}
}
#endif
gp.program_state.in_impossible = 0;
}

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 sys.c $NHDT-Date: 1596498215 2020/08/03 23:43:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.57 $ */
/* NetHack 3.7 sys.c $NHDT-Date: 1693083254 2023/08/26 20:54:14 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.63 $ */
/* Copyright (c) Kenneth Lorber, Kensington, Maryland, 2008. */
/* NetHack may be freely redistributed. See license for details. */
@@ -70,6 +70,7 @@ sys_early_init(void)
#endif
#endif
#endif
sysopt.crashreporturl = NULL;
sysopt.check_save_uid = 1;
sysopt.check_plname = 0;