add CRASHREPORT directly to browser

add CRASHREPORT for Windows
add ^P info to report (via DUMPLOG)

new options: crash_email, crash_name, crash_urlmax
new game command: #bugreport
new config option: CRASHREPORT_EXEC_NOSTDERR
new command line option: --bidshow

deleted helper scripts:
    NetHackCrashReport.Javascript
    nhcrashreport.lua

misc:
    update CRASHREPORTURL (will need to be updated before release)
    update bitrot in winchain
    winchain for Windows
    add missing synch_wait for NetHackW --showpaths
    add PANICTRACE (and CRASHREPORT) in mdlib.c:build_opts

missing:
    packaging (Windows needs the pdb file)
    no testing with MSVC command line build

port status:
    linux: working, but glibc's backtrace doesn't show static functions
    Windows VS: working.  pdb file is large - looking into options
    MacOS: working
    msdos: not supported
    VMS: not supported
    MSVC: planned, but not attempted
    MSYS2: working, but libbacktrace not showing symbols (yet?)
This commit is contained in:
nhkeni
2024-02-06 18:33:59 -05:00
parent cf3cbcf832
commit dbe5c98dca
37 changed files with 1001 additions and 315 deletions

View File

@@ -30,7 +30,7 @@ static void dump_enums(void);
void
early_init(int argc UNUSED, char *argv[] UNUSED)
{
#ifdef CRASHREPORT
#if defined(CRASHREPORT)
// Do this as early as possible, but let ports do other things first.
crashreport_init(argc, argv);
#endif
@@ -911,7 +911,7 @@ static const struct early_opt earlyopts[] = {
#ifdef WIN32
{ ARG_WINDOWS, "windows", 4, TRUE },
#endif
#ifdef CRASHREPORT
#if defined(CRASHREPORT)
{ ARG_BIDSHOW, "bidshow", 7, FALSE },
#endif
};
@@ -935,11 +935,13 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg)
const char *dashdash = "";
for (idx = 0; idx < SIZE(earlyopts); idx++) {
if (earlyopts[idx].e == e_arg)
if (earlyopts[idx].e == e_arg){
break;
}
}
if (idx >= SIZE(earlyopts) || argc < 1)
if (idx >= SIZE(earlyopts) || argc < 1){
return FALSE;
}
for (i = 0; i < argc; ++i) {
if (argv[i][0] != '-')
@@ -1002,10 +1004,12 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg)
dump_glyphids();
return 2;
#endif
#ifdef CRASHREPORT
#if defined(CRASHREPORT)
case ARG_BIDSHOW:
crashreport_bidshow();
return 2;
#else
#warning "CRASHREPORT CODE NOT INCLUDED"
#endif
#ifdef WIN32
case ARG_WINDOWS:

View File

@@ -2558,6 +2558,10 @@ struct ext_func_tab extcmdlist[] = {
doattributes, IFBURIED, NULL },
{ '@', "autopickup", "toggle the 'autopickup' option on/off",
dotogglepickup, IFBURIED, NULL },
#ifdef CRASHREPORT
{ '\0', "bugreport", "file a bug report",
dobugreport, GENERALCMD | NOFUZZERCMD, NULL },
#endif
{ 'C', "call", "name a monster, specific object, or type of object",
docallcmd, IFBURIED, NULL },
{ 'Z', "cast", "zap (cast) a spell",

View File

@@ -272,6 +272,10 @@ const struct instance_globals_c g_init_c = {
DUMMY, /* context */
/* dog.c */
DUMMY, /* catname */
/* end.c */
NULL, /* crash_email */
NULL, /* crash_name */
-1, /* crash_urlmax */
/* symbols.c */
0, /* currentgraphics */
/* files.c */

559
src/end.c
View File

@@ -21,9 +21,9 @@
#ifndef NO_SIGNAL
static void done_intr(int);
#if defined(UNIX) || defined(VMS) || defined(__EMX__)
# if defined(UNIX) || defined(VMS) || defined(__EMX__)
static void done_hangup(int);
#endif
# endif
#endif
static void disclose(int, boolean);
static void get_valuables(struct obj *);
@@ -37,6 +37,9 @@ static boolean should_query_disclose_option(int, char *);
static void dump_plines(void);
#endif
static void dump_everything(int, time_t);
#ifdef CRASHREPORT
static const char *get_saved_pline(int);
#endif
#if defined(__BEOS__) || defined(MICRO) || defined(OS2) || defined(WIN32)
ATTRNORETURN extern void nethack_exit(int) NORETURN;
@@ -46,9 +49,10 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN;
#define done_stopprint gp.program_state.stopprint
#ifndef PANICTRACE
#define NH_abort(x) NH_abort_
#endif
// XXX is there a configuration that still needs this?
//#ifndef PANICTRACE
//#define NH_abort(x) NH_abort_
//#endif
#ifdef AMIGA
#define NH_abort_ Abort(0)
@@ -64,7 +68,8 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN;
#endif /* !SYSV */
#endif /* !AMIGA */
#ifdef PANICTRACE
/* NB: CRASHREPORT implies PANICTRACE */
#if defined(PANICTRACE)
#include <errno.h>
#ifdef PANICTRACE_LIBC
#include <execinfo.h>
@@ -79,32 +84,30 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN;
* -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
* we gather the stacktrace (etc) and launch a browser to submit a bug report
* otherwise we just use stdout. Requires libc for now.
*/
#ifdef SYSCF
#define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
#ifdef PANICTRACE_LIBC
#define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
# define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
# ifdef PANICTRACE_LIBC
# define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
# else
# define SYSOPT_PANICTRACE_LIBC 0
# endif
#else
#define SYSOPT_PANICTRACE_LIBC 0
#endif
#else
#define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
#ifdef PANICTRACE_LIBC
#define SYSOPT_PANICTRACE_LIBC 1
#else
#define SYSOPT_PANICTRACE_LIBC 0
#endif
# define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
# ifdef PANICTRACE_LIBC
# define SYSOPT_PANICTRACE_LIBC 1
# else
# define SYSOPT_PANICTRACE_LIBC 0
# endif
#endif
#ifdef PANICTRACE
static void NH_abort(char *);
#endif
#ifndef NO_SIGNAL
static void panictrace_handler(int);
#endif
static boolean NH_panictrace_libc(char *);
static boolean NH_panictrace_libc(void);
static boolean NH_panictrace_gdb(void);
#ifndef NO_SIGNAL
@@ -171,7 +174,6 @@ panictrace_setsignals(boolean set)
}
#endif /* NO_SIGNAL */
#ifdef PANICTRACE
static void
NH_abort(char *why)
{
@@ -185,215 +187,394 @@ NH_abort(char *why)
return;
aborting = TRUE;
#ifdef CRASHREPORT
if(!submit_web_report(1, "Panic", why))
#endif
{
#ifndef VMS
if (gdb_prio == libc_prio && gdb_prio > 0)
gdb_prio++;
if (gdb_prio == libc_prio && gdb_prio > 0)
gdb_prio++;
if (gdb_prio > libc_prio) {
(void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc(why)));
} else {
(void) (NH_panictrace_libc(why) || (gdb_prio && NH_panictrace_gdb()));
}
if (gdb_prio > libc_prio) {
(void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc()));
} else {
(void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb()));
}
#else /* VMS */
/* overload otherwise unused priority for debug mode: 1 = show
traceback and exit; 2 = show traceback and stay in debugger */
/* if (wizard && gdb_prio == 1) gdb_prio = 2; */
vms_traceback(gdb_prio);
nhUse(libc_prio);
/* overload otherwise unused priority for debug mode: 1 = show
traceback and exit; 2 = show traceback and stay in debugger */
/* if (wizard && gdb_prio == 1) gdb_prio = 2; */
vms_traceback(gdb_prio);
nhUse(libc_prio);
#endif /* ?VMS */
}
#ifndef NO_SIGNAL
panictrace_setsignals(FALSE);
#endif
NH_abort_;
}
#endif
/* 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>
# define HASH_PRAGMA_START \
# ifdef WIN32
# define HASH_PRAGMA_START
# define HASH_PRAGMA_END
# else
# define HASH_PRAGMA_START \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
# define HASH_PRAGMA_END _Pragma("GCC diagnostic pop");
# define HASH_PRAGMA_END _Pragma("GCC diagnostic pop");
# endif
# ifdef MACOS
# include <CommonCrypto/CommonDigest.h>
# 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
# define HASH_CONTEXTPTR(CTXP) \
unsigned char tmp[CC_MD4_DIGEST_LENGTH]; \
CC_MD4_CTX CTXP ## _; \
CC_MD4_CTX *CTXP = &CTXP ## _
# define HASH_INIT(ctxp) !CC_MD4_Init(ctxp)
# define HASH_UPDATE(ctx, ptr, len) !CC_MD4_Update(ctx, ptr, len)
# define HASH_FINISH(ctxp) !CC_MD4_Final(tmp, ctxp)
# define HASH_RESULT_SIZE(ctxp) CC_MD4_DIGEST_LENGTH
# define HASH_RESULT(ctx, inp) *inp = (unsigned char *)ctx
# define HASH_CLEANUP(ctxp)
# define HASH_OFLAGS O_RDONLY
# define HASH_BINFILE_DECL char *binfile = argv[0];
# ifdef BETA
# define HASH_BINFILE \
if (!binfile || !*binfile) { \
/* If this triggers, investigate CFBundleGetMainBundle */ \
/* or CFBundleCopyExecutableURL. */ \
raw_print("BETA warning: crashreport_init called without useful info"); \
goto skip; \
}
# else
# define HASH_BINFILE() \
if (!binfile || !*binfile) { \
goto skip; \
}
# endif
# endif
# ifdef __linux__
# include <openssl/md4.h>
# 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
# define HASH_CONTEXTPTR(CTXP) \
unsigned char tmp[MD4_DIGEST_LENGTH]; \
MD4_CTX CTXP ## _; \
MD4_CTX *CTXP = &CTXP ## _
# define HASH_INIT(ctxp) !MD4_Init(ctxp)
# define HASH_UPDATE(ctx, ptr, len) !MD4_Update(ctx, ptr, len)
# define HASH_FINISH(ctxp) !MD4_Final(tmp, ctxp)
# define HASH_RESULT_SIZE(ctxp) MD4_DIGEST_LENGTH
# define HASH_RESULT(ctx, inp) *inp = (unsigned char *)ctx
# define HASH_CLEANUP(ctxp)
# define HASH_OFLAGS O_RDONLY
# define HASH_BINFILE_DECL char binfile[PATH_MAX+1];
# define HASH_BINFILE() \
int len = readlink("/proc/self/exe", binfile, sizeof(binfile)-1); \
if (len>0) { \
binfile[len] = '\0'; \
} else { \
goto skip; \
}
# endif // __linux__
# ifdef WIN32
/* WIN32 takes too much code and is dependent on OS includes we can't
* pull in here, so we call out to code in sys/windows/windsys.c */
# define HASH_CONTEXTPTR(CTXP)
# define HASH_INIT(ctxp) win32_cr_helper('i',ctxp, NULL, 0)
# define HASH_UPDATE(ctxp, ptr, len) win32_cr_helper('u',ctxp, ptr, len)
# define HASH_FINISH(ctxp) win32_cr_helper('f',ctxp,NULL,0)
# define HASH_CLEANUP(ctxp) win32_cr_helper('c',ctxp, NULL, 0)
# define HASH_RESULT_SIZE(ctxp) win32_cr_helper('s',ctxp,NULL,0)
# define HASH_RESULT(ctxp, inp) win32_cr_helper('r',ctxp,inp,0)
# define HASH_OFLAGS _O_RDONLY | _O_BINARY
# define HASH_BINFILE_DECL char *binfile;
# define HASH_BINFILE() \
if(win32_cr_helper('b',NULL,&binfile,0)){ \
goto skip; \
}
# endif // WIN32
// 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];
static char bid[40];
/* ARGSUSED */
void
crashreport_init(int argc UNUSED, char *argv[] UNUSED){
unsigned char tmp[HASH_RESULT_SIZE];
static int once=0; if(once++) return; // NetHackW.exe calls us twice
HASH_BINFILE_DECL;
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);
HASH_CONTEXTPTR(ctxp);
if(HASH_INIT(ctxp)) goto skip;
HASH_BINFILE(); // Does "goto skip" on error.
int fd = open(binfile, HASH_OFLAGS, 0);
if (fd == -1) {
# ifdef BETA
raw_printf("open e=%s",strerror(errno));
# endif
goto skip;
}
int segsize;
char segment[4096];
unsigned char segment[4096];
while (0 < (segsize = read(fd, segment,sizeof(segment)))) {
HASH_UPDATE(&ctx, segment, segsize);
if(HASH_UPDATE(ctxp, segment, segsize)) goto skip;
}
HASH_FINISH(&ctx, tmp);
if(segsize < 0) {
close(fd);
goto skip;
}
if(HASH_FINISH(ctxp)) goto skip;
close(fd);
char *p = bid;
unsigned char *in = &tmp[0];
char cnt=HASH_RESULT_SIZE;
unsigned char *in;
HASH_RESULT(ctxp,&in);
/* Just in case, make sure not to overflow the bid buffer. */
char cnt=min(HASH_RESULT_SIZE(ctxp), (sizeof(bid)-1));
while (cnt--) {
p += snprintf(p, HASH_RESULT_SIZE-(p-bid), "%02x",*(in++));
p += snprintf(p, HASH_RESULT_SIZE(ctxp) - (p - bid), "%02x", *(in++));
}
*p = '\0';
return;
skip:
strncpy((char *)bid,"unknown",sizeof(bid)-1);
HASH_CLEANUP(ctxp);
HASH_PRAGMA_END
}
#undef HASH_CONTEXT
#undef HASH_CONTEXTPTR
#undef HASH_INIT
#undef HASH_UPDATE
#undef HASH_FINISH
#undef HASH_CLEANUP
#undef HASH_RESULT
#undef HASH_RESULT_SIZE
#undef HASH_PRAGMA_START
#undef HASH_PRAGMA_END
#undef HASH_BINFILE_DECL
#undef HASH_BINFILE
void
crashreport_bidshow(void){
raw_print(bid);
#if defined(WIN32) && !defined(WIN32CON)
if(0==win32_cr_helper('D', ctxp, bid, 0))
#endif
{
raw_print(bid);
#ifdef WIN32notyet
wait_synch();
#endif
}
}
/* 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
* (on most platforms) (but libc can still do anything it wants). */
// No theoretial limit for URL length but reality is messy.
// This should work on all modern platforms.
#ifndef MAX_URL
# define MAX_URL 8192
#endif
#ifndef SWR_FRAMES
# define SWR_FRAMES 20
#endif
// mark holds the initial eos; if we can't get the value in
// then we can remove the whole item if desired. For other
// semantics, caller can handle mark.
#define SWR_ADD(str) \
utmp = strlen(str); \
mark = uend; \
if(utmp >= urem) goto full; \
strncpy(uend, str, utmp); \
uend += utmp; urem -= utmp; \
*uend = '\0';
// NB: on overflow this rolls us back to mark, so if we don't
// want to roll back to the last SWR_ADD, update mark before
// calling this macro.
#define SWR_ADD_URIcoded(str) \
if(swr_add_uricoded(str, &uend, &urem, mark))goto full;
// On overflow, truncate to markp (but only if markp != NULL).
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_add_uricoded(const char *in, char **out, int *remaining, char *markp){
while(*in){
if(
isalnum(*in) ||
*in == '_' ||
*in == '-' ||
*in == '.' ||
*in == '~' // ||
){
**out = *in;
(*out)++;
(*remaining)--;
} else if(*in == ' '){
**out = '+';
(*out)++;
(*remaining)--;
} else {
if(*remaining <= 3){
if(markp) *out = markp, *remaining = 0;
**out = '\0';
return TRUE;
}
int x = snprintf(*out, *remaining, "%%%02X", *in);
*out += x;
*remaining -= x;
}
in++;
if(! *remaining){
if(markp) *out = markp, *remaining = 0;
**out = '\0';
return TRUE;
}
**out = '\0';
}
return FALSE; // normal return
}
SWR_HDRnonl("bid: ");
SWR_HDR(bid);
static char url[MAX_URL]; // XXX too bad this isn't allocated as needed
static int urem = MAX_URL; // adjusted for gc.crash_urlmax below
static char *uend = url;
static int utmp; // used inside macros
static char *mark; // holds previous terminator (generally)
boolean
submit_web_report(int cos, const char *msg, const char *why){
urem = (gc.crash_urlmax < 0 || gc.crash_urlmax > MAX_URL)
? MAX_URL : min(MAX_URL,gc.crash_urlmax);
char temp[200];
char temp2[200];
int countpp=0; // pre and post traceback lines
// URL loaded for creating reports to the NetHack DevTeam
// CRASHREPORTURL=https://nethack.org/links/cr-37BETA.html
if(!sysopt.crashreporturl) return FALSE;
SWR_ADD(sysopt.crashreporturl);
/* cos - operation, v - version */
snprintf(temp, sizeof(temp), "?cos=%d&v=1",cos);
SWR_ADD(temp);
/* msg==NULL for #bugreport */
if(msg){
SWR_ADD("&subject=");
snprintf(temp, sizeof(temp), "%s report for NetHack %s",
msg, version_string(temp2, sizeof(temp2)));
SWR_ADD_URIcoded(temp);
}
SWR_ADD("&gitver=");
SWR_ADD_URIcoded(getversionstring(temp2, sizeof(temp2)));
if(gc.crash_name){
SWR_ADD("&name=");
SWR_ADD_URIcoded(gc.crash_name);
}
if(gc.crash_email){
SWR_ADD("&email=");
SWR_ADD_URIcoded(gc.crash_email);
}
// hardware: leave for user
// software: leave for user
// comments: leave for user
SWR_ADD("&details=");
if (why) {
SWR_ADD_URIcoded(why);
SWR_ADD_URIcoded("\n");
mark=uend;
countpp++;
}
SWR_ADD_URIcoded("bid: ");
SWR_ADD_URIcoded(bid);
SWR_ADD_URIcoded("\n");
mark=uend;
countpp++;
int count = 0;
if(cos==1){
#ifdef WIN32
count=win32_cr_gettrace(SWR_FRAMES,uend, MAX_URL-(uend-url));
uend=eos(url);
#else
void *bt[SWR_FRAMES];
int count, x;
char **info, buf[BUFSZ];
int x;
char **info;
count = backtrace(bt, SIZE(bt));
info = backtrace_symbols(bt, count);
for (x = 0; x < count; x++) {
copynchars(buf, info[x], (int) sizeof buf - 1);
copynchars(temp, info[x], (int) sizeof temp - 1 - 1); // \n\0
/* 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);
(void) strsubst(temp, " ", "");
(void) strsubst(temp, " ", "");
strncat(temp, "\n", sizeof temp - 1);
# if 0 // __linux__
// not needed for MacOS
// XXX is it actually needed for linux? TBD
snprintf(temp2, sizeof(temp2), "[%02lu]\n", (unsigned long) x);
uend--; // remove the \n we added above
SWR_ADD_URIcoded(temp2);
# endif // linux
SWR_ADD_URIcoded(temp);
mark=uend;
}
*(endp-1) = '\0'; // remove last newline
SWR_ADD(wholetrace);
#endif // !WIN32
}
// detailrows min(actual,50) Guess since we can't know the
#ifdef DUMPLOG
// config.h turns this on, but make it easy to turn off if needed
if(cos==1) {
int k;
SWR_ADD_URIcoded("Latest messages:\n");
mark=uend;
countpp++;
for(k=0;k<5;k++){
const char *line = get_saved_pline(k);
if(!line) break;
SWR_ADD_URIcoded(line);
SWR_ADD_URIcoded("\n");
countpp++;
mark=uend;
}
}
#endif
// detailrows: 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
SWR_ADD("&detailrows=");
(void)snprintf(temp,sizeof(temp),"%d",min(count+countpp,30));
SWR_ADD_URIcoded(temp);
pid = fork();
full: ;
//printf("URL=%ld '%s'\n",strlen(url),url);
#ifdef WIN32
int *rv = win32_cr_shellexecute(url);
// XXX TESTING
printf("ShellExecute returned: %p\n",rv); // >32 is ok
#else
const char *xargv[] = {
CRASHREPORT,
url,
NULL
};
int pid = fork();
extern char **environ;
if (pid == 0) {
#ifdef CRASHREPORT_EXEC_NOSTDERR
/* Keep the output clean - firefox spews useless errors on
* my system. */
(void)close(2);
(void)open("/dev/null", O_WRONLY);
#endif
execve(CRASHREPORT, (char * const *)xargv, environ);
char err[100];
sprintf(err, "Can't start " CRASHREPORT ": %s", strerror(errno));
@@ -401,36 +582,45 @@ submit_web_report(const char *msg, char *why){
} 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
#ifdef BETA
// Not useful at the moment. XXX
char err[100];
sprintf(err, "pid=%d e=%d status=%0x",wpid,errno,status);
sprintf(err, "pid=%d e=%d status=%0x",pid,errno,status);
raw_print(err);
#endif
return FALSE;
}
}
/* free(info); -- Don't risk it. */
#endif
return TRUE;
}
return FALSE;
}
int
dobugreport(void){
if(!submit_web_report(2, NULL, "#bugreport command")){
pline("Unable to send bug report. Please visit %s instead.",
sysopt.crashreporturl
? sysopt.crashreporturl
: "https://www.nethack.org"
);
}
return ECMD_OK;
}
#endif /* CRASHREPORT */
#undef SWR_ADD
#undef SWR_ADD_URIcoded
#undef SWR_FRAMES
#undef SWR_HDR
#undef SWR_LINES
/*ARGSUSED*/
static boolean
NH_panictrace_libc(char *why UNUSED)
NH_panictrace_libc(void)
{
#ifdef CRASHREPORT
if(submit_web_report("Panic",why)) return TRUE;
#endif
#ifdef PANICTRACE_LIBC
void *bt[20];
@@ -459,13 +649,13 @@ NH_panictrace_libc(char *why UNUSED)
* fooVAR (possibly const) variable containing fooPATH
*/
#ifdef PANICTRACE_GDB
#ifdef SYSCF
#define GDBVAR sysopt.gdbpath
#define GREPVAR sysopt.greppath
#else /* SYSCF */
#define GDBVAR GDBPATH
#define GREPVAR GREPPATH
#endif /* SYSCF */
# ifdef SYSCF
# define GDBVAR sysopt.gdbpath
# define GREPVAR sysopt.greppath
# else /* SYSCF */
# define GDBVAR GDBPATH
# define GREPVAR GREPPATH
# endif /* SYSCF */
#endif /* PANICTRACE_GDB */
static boolean
@@ -878,7 +1068,7 @@ panic VA_DECL(const char *, str)
? "."
: "\nand it may be possible to rebuild.";
// XXX this is probably wrong if defined(CRASHREPORT)
// XXX this may need an update if defined(CRASHREPORT) TBD
if (sysopt.support)
raw_printf("To report this error, %s%s", sysopt.support,
maybe_rebuild);
@@ -984,6 +1174,27 @@ dump_plines(void)
}
}
}
// lineno==0 gives the most recent message (e.g. "Do you want to call panic..."
// if called from #panic)
static const char *
get_saved_pline(int lineno){
int p;
int limit = DUMPLOG_MSG_COUNT;
if(lineno >= DUMPLOG_MSG_COUNT) return NULL;
p = (gs.saved_pline_index-1) % DUMPLOG_MSG_COUNT;
while(limit--){
if(gs.saved_plines[p]){ // valid line
if(lineno--){
p = (p-1+DUMPLOG_MSG_COUNT) % DUMPLOG_MSG_COUNT;
} else {
return gs.saved_plines[p];
}
}
}
return NULL;
}
#endif
/*ARGSUSED*/

View File

@@ -4736,6 +4736,9 @@ reveal_paths(void)
#endif /* ?UNIX */
raw_print("");
#if defined(WIN32) && !defined(WIN32CON)
wait_synch();
#endif
}
/* ---------- BEGIN TRIBUTE ----------- */

View File

@@ -678,6 +678,12 @@ static const char *const build_opts[] = {
#endif
#ifdef SYSCF
"system configuration at run-time",
#endif
#ifdef PANICTRACE
"show stack trace on error",
#endif
#ifdef CRASHREPORT
"launch browser to report issues",
#endif
save_bones_compat_buf,
"and basic NetHack features"

View File

@@ -522,7 +522,7 @@ parseoptions(
* determine_ambiguities()
* figured out exactly how many characters are required to
* unambiguously differentiate one option from all others, and it
* placed that number into each option's alloption[n].minmatch.
* placed that number into each option's allopt[n].minmatch.
*
*/
if (!got_match)
@@ -1205,6 +1205,80 @@ optfn_catname(
return optn_ok;
}
static int
optfn_crash_email(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op)
{
if (req == do_init) {
return optn_ok;
}
if (req == do_set) {
if ((op = string_for_opt(opts, FALSE))
!= empty_optstr) {
gc.crash_email = dupstr(op);
} else
return optn_err;
return optn_ok;
}
if (req == get_val || req == get_cnf_val) {
if (!opts)
return optn_err;
Sprintf(opts, "%s", gc.crash_email);
return optn_ok;
}
return optn_ok;
}
static int
optfn_crash_name(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op)
{
if (req == do_init) {
return optn_ok;
}
if (req == do_set) {
if ((op = string_for_opt(opts, FALSE))
!= empty_optstr) {
gc.crash_name = dupstr(op);
} else
return optn_err;
return optn_ok;
}
if (req == get_val || req == get_cnf_val) {
if (!opts)
return optn_err;
Sprintf(opts, "%s", gc.crash_name);
return optn_ok;
}
return optn_ok;
}
static int
optfn_crash_urlmax(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op)
{
if (req == do_init) {
return optn_ok;
}
if (req == do_set) {
if ((op = string_for_opt(opts, FALSE))
!= empty_optstr) {
int temp = atoi(op);
if(temp < 75){
config_error_add("Invalid value %d for crash_urlmax. Minimum value is 75.",temp);
return optn_err;
}
gc.crash_urlmax = temp;
} else
return optn_err;
return optn_ok;
}
if (req == get_val || req == get_cnf_val) {
if (!opts)
return optn_err;
Sprintf(opts, "%d", gc.crash_urlmax);
return optn_ok;
}
return optn_ok;
}
#ifdef CURSES_GRAPHICS
static int
optfn_cursesgraphics(int optidx, int req, boolean negated,
@@ -6694,8 +6768,6 @@ initoptions(void)
{
int i;
go.opt_phase = builtin_opt;
initoptions_init();
/*
* Call each option function with an init flag and give it a chance
* to make any preparations that it might require. We do this
@@ -6740,6 +6812,7 @@ initoptions_init(void)
#endif
int i;
go.opt_phase = builtin_opt; // Did I need to move this here?
memcpy(allopt, allopt_init, sizeof(allopt));
determine_ambiguities();

View File

@@ -537,7 +537,7 @@ impossible(const char *s, ...)
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);
submit_web_report(1,"Impossible", pbuf);
}
}
#endif