/* NetHack 3.5 decl.h $Date$ $Revision$ */
-/* SCCS Id: @(#)decl.h 3.5 2008/07/20 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
};
#endif /* AUTOPICKUP_EXCEPTIONS */
+#ifdef PANICTRACE
+E char *ARGV0;
+#endif
+
#undef E
#endif /* DECL_H */
E void FDECL(save_killers, (int,int));
E void FDECL(restore_killers, (int));
E char *FDECL(build_english_list, (char *));
+#if defined(PANICTRACE) && !defined(NO_SIGNAL)
+E void FDECL(panictrace_setsignals, (boolean));
+#endif
/* ### engrave.c ### */
/* NetHack 3.5 global.h $Date$ $Revision$ */
-/* SCCS Id: @(#)global.h 3.5 2007/01/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
#define MAXMONNO 120 /* extinct monst after this number created */
#define MHPMAX 500 /* maximum monster hp */
+#ifdef BETA
+/* see end.c */
+# ifndef PANICTRACE
+# define PANICTRACE
+# endif
+#endif
+/* The following are meaningless if PANICTRACE is not defined: */
+#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ >= 2)
+# define PANICTRACE_GLIBC
+#endif
+#ifdef UNIX
+# define PANICTRACE_GDB
+#endif
+
#endif /* GLOBAL_H */
/* NetHack 3.5 sys.h $Date$ $Revision$ */
-/* SCCS Id: @(#)sys.h 3.5 2008/01/30 */
/* Copyright (c) Kenneth Lorber, Kensington, Maryland, 2008. */
/* NetHack may be freely redistributed. See license for details. */
int pers_is_uid;
int entrymax;
int pointsmin;
+#ifdef PANICTRACE
+ /* panic options */
+ char *gdbpath;
+ int panictrace_gdb;
+# ifdef PANICTRACE_GLIBC
+ int panictrace_glibc;
+# endif
+#endif
};
E struct sysopt sysopt;
wiz_panic(VOID_ARGS)
{
if (yn("Do you want to call panic() and end your game?") == 'y')
- panic("crash test.");
+ panic("Crash test.");
return 0;
}
#endif
};
+#ifdef PANICTRACE
+char *ARGV0;
+#endif
+
/* dummy routine used to force linkage */
void
decl_init()
/* NetHack 3.5 end.c $Date$ $Revision$ */
-/* SCCS Id: @(#)end.c 3.5 2008/02/08 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
#define done_stopprint program_state.stopprint
+#ifndef PANICTRACE
+# define NH_abort NH_abort_
+#endif
+
#ifdef AMIGA
-# define NH_abort() Abort(0)
+# define NH_abort_() Abort(0)
#else
# ifdef SYSV
-# define NH_abort() (void) abort()
+# define NH_abort_() (void) abort()
# else
# ifdef WIN32
-# define NH_abort() win32_abort()
+# define NH_abort_() win32_abort()
+# else
+# define NH_abort_() abort()
+# endif
+# endif
+#endif
+
+#ifdef PANICTRACE
+# include <errno.h>
+# ifdef PANICTRACE_GLIBC
+# include <execinfo.h>
+# endif
+
+/* What do we try and in what order? Tradeoffs:
+ * glibc: +no external programs required
+ * -requires newish glibc
+ * -requires -rdynamic
+ * gdb: +gives more detailed information
+ * +works on more OS versions
+ * -requires -g, which may preclude -O on some compilers
+ */
+# ifdef SYSCF
+# define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
+# ifdef PANICTRACE_GLIBC
+# define SYSOPT_PANICTRACE_GLIBC sysopt.panictrace_glibc
+# else
+# define SYSOPT_PANICTRACE_GLIBC 0
+# endif
+# else
+# define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB")==0?0:2)
+# ifdef PANICTRACE_GLIBC
+# define SYSOPT_PANICTRACE_GLIBC 1
# else
-# define NH_abort() abort()
+# define SYSOPT_PANICTRACE_GLIBC 0
# endif
# endif
+
+static void NDECL(NH_abort);
+#ifndef NO_SIGNAL
+static void FDECL(panictrace_handler, (int));
+#endif
+static boolean NDECL(NH_panictrace_glibc);
+static boolean NDECL(NH_panictrace_gdb);
+
+#ifndef NO_SIGNAL
+/*ARGSUSED*/
+void
+panictrace_handler(sig_unused) /* called as signal() handler, so sent at least one arg */
+int sig_unused;
+{
+# define SIG_MSG "\nSignal received.\n"
+ (void) write(2, SIG_MSG, sizeof(SIG_MSG)-1);
+ NH_abort();
+}
+
+void
+panictrace_setsignals(set)
+boolean set;
+{
+# define SETSIGNAL(sig) (void) signal(sig, set?(SIG_RET_TYPE)panictrace_handler:SIG_DFL);
+# ifdef SIGILL
+ SETSIGNAL(SIGILL);
+# endif
+# ifdef SIGTRAP
+ SETSIGNAL(SIGTRAP);
+# endif
+# ifdef SIGIOT
+ SETSIGNAL(SIGIOT);
+# endif
+# ifdef SIGBUS
+ SETSIGNAL(SIGBUS);
+# endif
+# ifdef SIGFPE
+ SETSIGNAL(SIGFPE);
+# endif
+# ifdef SIGSEGV
+ SETSIGNAL(SIGSEGV);
+# endif
+# ifdef SIGSTKFLT
+ SETSIGNAL(SIGSTKFLT);
+# endif
+# ifdef SIGSYS
+ SETSIGNAL(SIGSYS);
+# endif
+# ifdef SIGEMT
+ SETSIGNAL(SIGEMT);
+# endif
+# undef SETSIGNAL
+}
+#endif
+
+static void
+NH_abort(){
+ int gdb_prio = SYSOPT_PANICTRACE_GDB;
+ int glibc_prio = SYSOPT_PANICTRACE_GLIBC;
+ static boolean aborting = FALSE;
+
+ if(aborting) return;
+ aborting = TRUE;
+
+ if(gdb_prio == glibc_prio && gdb_prio > 0) gdb_prio++;
+
+ if(gdb_prio > glibc_prio){
+ NH_panictrace_gdb() || (glibc_prio && NH_panictrace_glibc());
+ } else {
+ NH_panictrace_glibc() || (gdb_prio && NH_panictrace_gdb());
+ }
+
+ panictrace_setsignals(FALSE);
+ NH_abort_();
+}
+
+static boolean
+NH_panictrace_glibc(){
+# ifdef PANICTRACE_GLIBC
+ void *bt[20];
+ size_t count;
+ char **info;
+ int x;
+
+ raw_print("Generating more information you may report:\n");
+ count = backtrace(bt, SIZE(bt));
+ info = backtrace_symbols(bt, count);
+ for(x=0; x<count; x++){
+ raw_printf("[%d] %s",x,info[x]);
+ }
+ /* free(info); Don't risk it. */
+ return TRUE;
+# else
+ return FALSE;
+# endif
+}
+
+# ifdef PANICTRACE_GDB
+/* I'm going to assume /bin/grep is the right path for grep. */
+# ifdef SYSCF
+# define GDBPATH sysopt.gdbpath
+# else
+# ifndef GDBPATH
+# define GDBPATH "/usr/bin/gdb"
+# endif
+# endif
+# endif
+
+static boolean
+NH_panictrace_gdb(){
+# ifdef PANICTRACE_GDB
+ /* A (more) generic method to get a stack trace - invoke
+ * gdb on ourself. */
+ char *gdbpath = GDBPATH;
+ char buf[BUFSZ];
+
+ if(gdbpath == NULL || gdbpath[0] == 0) return FALSE;
+
+ sprintf(buf, "%s -n -q %s %d 2>&1 | /bin/grep '^#'",
+ GDBPATH, ARGV0, getpid());
+ FILE *gdb = popen(buf, "w");
+ if(gdb){
+ raw_print("Generating more information you may report:\n");
+ fprintf(gdb, "bt\nquit\ny");
+ fflush(gdb);
+ sleep(4); /* ugly */
+ pclose(gdb);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+# else
+ return FALSE;
+# endif
+}
#endif
/*
}
#if defined(WIN32) && !defined(SYSCF)
-#define NOTIFY_NETHACK_BUGS
+# define NOTIFY_NETHACK_BUGS
#endif
/*VARARGS1*/
return 0;
}
sysopt.pointsmin = temp;
+ } else if ( (src==SET_IN_SYS) && match_varname(buf, "PANICTRACE_GLIBC", 16)) {
+ int temp = atoi(bufp);
+ if(temp < 1 || temp > 2){
+ raw_printf("Illegal value in PANICTRACE_GLIBC (not 0,1,2).");
+ return 0;
+ }
+ sysopt.panictrace_glibc = temp;
+ } else if ( (src==SET_IN_SYS) && match_varname(buf, "PANICTRACE_GDB", 14)) {
+ int temp = atoi(bufp);
+ if(temp < 1 || temp > 2){
+ raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2).");
+ return 0;
+ }
+ sysopt.panictrace_gdb = temp;
+ } else if ( (src==SET_IN_SYS) && match_varname(buf, "GDBPATH", 7)) {
+ if(sysopt.gdbpath) free(sysopt.gdbpath);
+ sysopt.gdbpath = (char*)alloc(strlen(bufp)+1);
+ Strcpy(sysopt.gdbpath, bufp);
#endif
} else if (match_varname(buf, "BOULDER", 3)) {
(void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE,
/* NetHack 3.5 options.c $Date$ $Revision$ */
-/* SCCS Id: @(#)options.c 3.5 2008/08/22 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
/* most environment variables will eventually be printed in an error
* message if they don't work, and most error message paths go through
* BUFSZ buffers, which could be overflowed by a maliciously long
- * environment variable. if a variable can legitimately be long, or
+ * environment variable. If a variable can legitimately be long, or
* if it's put in a smaller buffer, the responsible code will have to
* bounds-check itself.
*/
/* NetHack 3.5 pager.c $Date$ $Revision$ */
-/* SCCS Id: @(#)pager.c 3.5 2009/01/30 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
putstr(cwin, 0, "");
} else if(sysopt.wizards){
char *tmp = build_english_list(sysopt.wizards);
- Sprintf(buf, "To contact local support, %s", tmp);
+ Sprintf(buf, "To contact local support, contact %s.", tmp);
free(tmp);
putstr(cwin, 0, buf);
putstr(cwin, 0, "");
sysopt.pointsmin = POINTSMIN;
sysopt.pers_is_uid = PERS_IS_UID;
- /* sanity checks */
+ /* sanity checks */
if(PERSMAX<1) sysopt.persmax = 1;
if(ENTRYMAX<10) sysopt.entrymax = 10;
if(POINTSMIN<1) sysopt.pointsmin = 1;
if(PERS_IS_UID != 0 && PERS_IS_UID != 1)
panic("config error: PERS_IS_UID must be either 0 or 1");
+
+#ifdef PANICTRACE
+ /* panic options */
+ sysopt.gdbpath = NULL;
+# ifdef BETA
+ sysopt.panictrace_gdb = 1;
+# ifdef PANICTRACE_GLIBC
+ sysopt.panictrace_glibc = 2;
+# endif
+# else
+ sysopt.panictrace_gdb = 0;
+# ifdef PANICTRACE_GLIBC
+ sysopt.panictrace_glibc = 0;
+# endif
+# endif
+#endif
}
# for Ubuntu dapper.
-HACKDIR=$(PREFIX)/games/lib/$(GAME)dir
-SHELLDIR = $(PREFIX)/games
#PREFIX=/usr
PREFIX=$(wildcard ~)/nh/install
+HACKDIR=$(PREFIX)/games/lib/$(GAME)dir
+SHELLDIR = $(PREFIX)/games
-CFLAGS=-O -I../include -DNOTPARMDECL $(CFLAGS1) -DDLB
+CFLAGS=-g -O -I../include -DNOTPARMDECL $(CFLAGS1) -DDLB
CFLAGS1=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\"
+CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE
+CFLAGS+=-DHACKDIR=\"$(HACKDIR)\"
LINK=$(CC)
+# Only needed for GLIBC stack trace:
+LFLAGS=-rdynamic
WINSRC = $(WINTTYSRC)
WINOBJ = $(WINTTYOBJ)
# Which users can use WIZARD (debugging) mode (the -D flag).
# A value of * allows anyone to enter debugging mode.
-WIZARDS=root,games
+WIZARDS=root games
# Users allowed to use the ! (shell escape) command or to suspend the game.
# Uses the same syntax as the WIZARDS option above.
# Determine identity of "person" in the score file with name (0) or
# numeric (1) user id.
#PERS_IS_UID=1
+
+# Try to get more info in case of a program bug or crash. Using GDB can
+# get more information and works on more systems but requires gdb be available;
+# using GLIBC only works if NetHack is linked with glibc. Both require
+# certain compilation options. See src/end.c and sys/unix/hints/* for
+# more information.
+GDBPATH=/usr/bin/gdb
+# Values are priorities: 0 - do not use this method, 1 - low priority,
+# 2 - high priority. Non-zero priority methods are tried in order.
+PANICTRACE_GDB=1
+PANICTRACE_GLIBC=2
initoptions_init();
read_config_file(SYSCF_FILE, SET_IN_SYS);
initoptions_finish();
+#endif
+#ifdef PANICTRACE
+ ARGV0 = argv[0]; /* save for possible stack trace */
+# ifndef NO_SIGNAL
+ panictrace_setsignals(TRUE);
+# endif
#endif
prscore(argc, argv);
exit(EXIT_SUCCESS);
read_config_file(SYSCF_FILE, SET_IN_SYS);
#endif
initoptions_finish();
+#ifdef PANICTRACE
+ ARGV0 = argv[0]; /* save for possible stack trace */
+# ifndef NO_SIGNAL
+ panictrace_setsignals(TRUE);
+# endif
+#endif
exact_username = whoami();
/*