2010-07-13 12:15:46 +00:00
|
|
|
/*
|
|
|
|
Serval Distributed Numbering Architecture (DNA)
|
|
|
|
Copyright (C) 2010 Paul Gardner-Stephen
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
2012-07-26 09:01:23 +00:00
|
|
|
#include <signal.h>
|
2012-05-08 02:49:52 +00:00
|
|
|
#include <unistd.h>
|
2012-09-27 01:32:58 +00:00
|
|
|
#include <time.h>
|
2012-02-05 05:45:19 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
2012-05-08 01:57:07 +00:00
|
|
|
#include <sys/stat.h>
|
2012-02-05 05:45:19 +00:00
|
|
|
|
2012-02-23 02:15:42 +00:00
|
|
|
#include "serval.h"
|
2012-07-02 03:22:21 +00:00
|
|
|
#include "strbuf.h"
|
2012-07-24 06:09:36 +00:00
|
|
|
#include "strbuf_helpers.h"
|
2010-07-13 12:15:46 +00:00
|
|
|
|
2012-05-08 02:49:52 +00:00
|
|
|
#define PIDFILE_NAME "servald.pid"
|
|
|
|
#define STOPFILE_NAME "servald.stop"
|
|
|
|
|
2012-07-24 06:09:36 +00:00
|
|
|
#define EXEC_NARGS 20
|
|
|
|
char *exec_args[EXEC_NARGS + 1];
|
2012-05-09 09:36:44 +00:00
|
|
|
int exec_argc = 0;
|
|
|
|
|
2012-05-14 09:02:10 +00:00
|
|
|
int serverMode=0;
|
2012-07-26 09:01:23 +00:00
|
|
|
int serverRespawnOnCrash = 0;
|
2012-05-09 09:36:44 +00:00
|
|
|
int servalShutdown = 0;
|
|
|
|
|
2012-05-08 01:53:43 +00:00
|
|
|
static int server_getpid = 0;
|
2010-07-13 12:15:46 +00:00
|
|
|
|
2012-05-15 01:39:21 +00:00
|
|
|
char *instrumentation_file=NULL;
|
2011-05-05 09:10:38 +00:00
|
|
|
FILE *i_f=NULL;
|
|
|
|
|
2010-07-13 12:15:46 +00:00
|
|
|
struct in_addr client_addr;
|
|
|
|
int client_port;
|
|
|
|
|
2012-05-09 09:36:44 +00:00
|
|
|
void signal_handler(int signal);
|
2012-07-26 09:01:23 +00:00
|
|
|
void crash_handler(int signal);
|
2012-04-12 23:55:03 +00:00
|
|
|
int getKeyring(char *s);
|
2011-08-08 14:41:46 +00:00
|
|
|
int createServerSocket();
|
|
|
|
|
2012-05-08 01:53:43 +00:00
|
|
|
/** Return the PID of the currently running server process, return 0 if there is none.
|
|
|
|
*/
|
|
|
|
int server_pid()
|
|
|
|
{
|
|
|
|
const char *instancepath = serval_instancepath();
|
|
|
|
struct stat st;
|
2012-05-24 07:41:55 +00:00
|
|
|
if (stat(instancepath, &st) == -1) {
|
|
|
|
WHY_perror("stat");
|
|
|
|
return WHYF("Instance path '%s' non existant or not accessable"
|
2012-05-08 01:53:43 +00:00
|
|
|
" (Set SERVALINSTANCE_PATH to specify an alternate location)",
|
2012-05-24 07:41:55 +00:00
|
|
|
instancepath
|
2012-05-08 01:53:43 +00:00
|
|
|
);
|
2012-05-24 07:41:55 +00:00
|
|
|
}
|
2012-05-08 01:53:43 +00:00
|
|
|
if ((st.st_mode & S_IFMT) != S_IFDIR)
|
2012-05-24 07:41:55 +00:00
|
|
|
return WHYF("Instance path '%s' is not a directory", instancepath);
|
2012-05-08 01:53:43 +00:00
|
|
|
char filename[1024];
|
2012-05-08 02:49:52 +00:00
|
|
|
if (!FORM_SERVAL_INSTANCE_PATH(filename, PIDFILE_NAME))
|
2012-05-08 01:53:43 +00:00
|
|
|
return -1;
|
|
|
|
FILE *f = NULL;
|
|
|
|
if ((f = fopen(filename, "r"))) {
|
|
|
|
char buf[20];
|
|
|
|
fgets(buf, sizeof buf, f);
|
|
|
|
fclose(f);
|
|
|
|
int pid = atoi(buf);
|
|
|
|
if (pid > 0 && kill(pid, 0) != -1)
|
|
|
|
return pid;
|
2012-10-08 04:26:55 +00:00
|
|
|
INFOF("Unlinking stale pidfile %s", filename);
|
2012-05-08 01:53:43 +00:00
|
|
|
unlink(filename);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2011-08-08 14:41:46 +00:00
|
|
|
|
2012-05-09 09:36:44 +00:00
|
|
|
void server_save_argv(int argc, const char *const *argv)
|
|
|
|
{
|
|
|
|
/* Save our argv[] to use for relaunching */
|
2012-07-24 06:09:36 +00:00
|
|
|
for (exec_argc = 0; exec_argc < argc && exec_argc < EXEC_NARGS; ++exec_argc)
|
2012-05-09 09:36:44 +00:00
|
|
|
exec_args[exec_argc] = strdup(argv[exec_argc]);
|
2012-07-24 06:09:36 +00:00
|
|
|
exec_args[exec_argc] = NULL;
|
2012-05-09 09:36:44 +00:00
|
|
|
}
|
|
|
|
|
2012-05-07 09:21:05 +00:00
|
|
|
int server(char *backing_file)
|
2010-07-13 12:15:46 +00:00
|
|
|
{
|
2012-06-26 06:09:20 +00:00
|
|
|
/* For testing, it can be very helpful to delay the start of the server
|
|
|
|
process, for example to check that the start/stop logic is robust.
|
|
|
|
*/
|
|
|
|
const char *delay = getenv("SERVALD_SERVER_START_DELAY");
|
2012-08-21 04:04:29 +00:00
|
|
|
if (delay)
|
|
|
|
sleep_ms(atoi(delay));
|
2012-06-26 06:09:20 +00:00
|
|
|
|
2012-05-14 09:02:10 +00:00
|
|
|
serverMode = 1;
|
2012-07-26 09:01:23 +00:00
|
|
|
serverRespawnOnCrash = confValueGetBoolean("server.respawn_on_crash", 0);
|
|
|
|
|
|
|
|
/* Catch crash signals so that we can log a backtrace before expiring. */
|
|
|
|
struct sigaction sig;
|
|
|
|
sig.sa_handler = crash_handler;
|
|
|
|
sigemptyset(&sig.sa_mask); // Don't block any signals during handler
|
|
|
|
sig.sa_flags = SA_NODEFER | SA_RESETHAND; // So the signal handler can kill the process by re-sending the same signal to itself
|
|
|
|
sigaction(SIGSEGV, &sig, NULL);
|
|
|
|
sigaction(SIGFPE, &sig, NULL);
|
|
|
|
sigaction(SIGILL, &sig, NULL);
|
|
|
|
sigaction(SIGBUS, &sig, NULL);
|
|
|
|
sigaction(SIGABRT, &sig, NULL);
|
|
|
|
|
|
|
|
/* Catch SIGHUP etc so that we can respond to requests to do things, eg, shut down. */
|
|
|
|
sig.sa_handler = signal_handler;
|
|
|
|
sigemptyset(&sig.sa_mask); // Block the same signals during handler
|
|
|
|
sigaddset(&sig.sa_mask, SIGHUP);
|
|
|
|
sigaddset(&sig.sa_mask, SIGINT);
|
|
|
|
sigaddset(&sig.sa_mask, SIGQUIT);
|
|
|
|
sig.sa_flags = 0;
|
|
|
|
sigaction(SIGHUP, &sig, NULL);
|
|
|
|
sigaction(SIGINT, &sig, NULL);
|
|
|
|
sigaction(SIGQUIT, &sig, NULL);
|
2012-05-09 09:36:44 +00:00
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
if (!overlayMode)
|
2011-08-08 14:41:46 +00:00
|
|
|
{
|
|
|
|
/* Create a simple socket for listening on if we are not in overlay mesh mode. */
|
|
|
|
createServerSocket();
|
|
|
|
|
2012-04-13 17:08:09 +00:00
|
|
|
/* Get backing store for keyring (overlay sets it up itself) */
|
|
|
|
getKeyring(backing_file);
|
|
|
|
}
|
|
|
|
|
2012-05-08 01:53:43 +00:00
|
|
|
/* Record PID to advertise that the server is now running */
|
2012-03-04 22:57:31 +00:00
|
|
|
char filename[1024];
|
2012-05-08 02:49:52 +00:00
|
|
|
if (!FORM_SERVAL_INSTANCE_PATH(filename, PIDFILE_NAME))
|
2012-05-07 04:19:38 +00:00
|
|
|
return -1;
|
2012-03-04 22:57:31 +00:00
|
|
|
FILE *f=fopen(filename,"w");
|
|
|
|
if (!f) {
|
2012-05-08 05:15:26 +00:00
|
|
|
WHY_perror("fopen");
|
2012-07-03 06:06:51 +00:00
|
|
|
WHYF("Could not write to PID file %s", filename);
|
2012-05-07 04:19:38 +00:00
|
|
|
return -1;
|
2012-03-04 22:57:31 +00:00
|
|
|
}
|
2012-05-08 01:53:43 +00:00
|
|
|
server_getpid = getpid();
|
|
|
|
fprintf(f,"%d\n", server_getpid);
|
2012-03-04 22:57:31 +00:00
|
|
|
fclose(f);
|
|
|
|
|
2012-06-22 03:55:41 +00:00
|
|
|
overlayServerMode();
|
2011-08-14 08:36:39 +00:00
|
|
|
|
2011-08-08 14:41:46 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-08 01:53:43 +00:00
|
|
|
/* Called periodically by the server process in its main loop.
|
|
|
|
*/
|
2012-07-02 03:49:54 +00:00
|
|
|
void server_shutdown_check(struct sched_ent *alarm)
|
2012-05-08 01:53:43 +00:00
|
|
|
{
|
|
|
|
if (servalShutdown) {
|
2012-06-28 06:07:36 +00:00
|
|
|
INFO("Shutdown flag set -- terminating with cleanup");
|
2012-05-08 02:49:52 +00:00
|
|
|
serverCleanUp();
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
if (server_check_stopfile() == 1) {
|
2012-06-28 06:07:36 +00:00
|
|
|
INFO("Shutdown file exists -- terminating with cleanup");
|
2012-05-08 01:53:43 +00:00
|
|
|
serverCleanUp();
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
/* If this server has been supplanted with another or Serval has been uninstalled, then its PID
|
|
|
|
file will change or be unaccessible. In this case, shut down without all the cleanup.
|
|
|
|
Perform this check at most once per second. */
|
2012-08-09 02:44:32 +00:00
|
|
|
static time_ms_t server_pid_time_ms = 0;
|
|
|
|
time_ms_t now = gettime_ms();
|
|
|
|
if (server_pid_time_ms == 0 || now - server_pid_time_ms > 1000) {
|
|
|
|
server_pid_time_ms = now;
|
2012-05-08 01:53:43 +00:00
|
|
|
if (server_pid() != server_getpid) {
|
2012-06-28 06:07:36 +00:00
|
|
|
WARNF("Server pid file no longer contains pid=%d -- shutting down without cleanup", server_getpid);
|
2012-05-08 01:53:43 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
2012-07-02 03:49:54 +00:00
|
|
|
if (alarm){
|
2012-08-09 02:44:32 +00:00
|
|
|
alarm->alarm = now + 1000;
|
|
|
|
alarm->deadline = alarm->alarm + 5000;
|
2012-07-02 03:49:54 +00:00
|
|
|
schedule(alarm);
|
|
|
|
}
|
2012-05-08 01:53:43 +00:00
|
|
|
}
|
|
|
|
|
2012-05-08 02:49:52 +00:00
|
|
|
int server_create_stopfile()
|
|
|
|
{
|
|
|
|
char stopfile[1024];
|
|
|
|
if (!FORM_SERVAL_INSTANCE_PATH(stopfile, STOPFILE_NAME))
|
|
|
|
return -1;
|
|
|
|
FILE *f;
|
2012-07-03 06:06:51 +00:00
|
|
|
if ((f = fopen(stopfile, "w")) == NULL) {
|
|
|
|
WHY_perror("fopen");
|
|
|
|
return WHYF("Could not create stopfile '%s'", stopfile);
|
|
|
|
}
|
2012-05-08 02:49:52 +00:00
|
|
|
fclose(f);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-08 03:23:59 +00:00
|
|
|
int server_remove_stopfile()
|
|
|
|
{
|
|
|
|
char stopfile[1024];
|
|
|
|
if (!FORM_SERVAL_INSTANCE_PATH(stopfile, STOPFILE_NAME))
|
|
|
|
return -1;
|
|
|
|
if (unlink(stopfile) == -1) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
2012-07-03 06:06:51 +00:00
|
|
|
WHY_perror("unlink");
|
|
|
|
return WHYF("Could not unlink stopfile '%s'", stopfile);
|
2012-05-08 03:23:59 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-05-08 02:49:52 +00:00
|
|
|
int server_check_stopfile()
|
|
|
|
{
|
|
|
|
char stopfile[1024];
|
|
|
|
if (!FORM_SERVAL_INSTANCE_PATH(stopfile, STOPFILE_NAME))
|
|
|
|
return -1;
|
|
|
|
int r = access(stopfile, F_OK);
|
|
|
|
if (r == 0)
|
|
|
|
return 1;
|
|
|
|
if (r == -1 && errno == ENOENT)
|
|
|
|
return 0;
|
2012-07-03 06:06:51 +00:00
|
|
|
WHY_perror("access");
|
|
|
|
WHYF("Cannot access stopfile '%s'", stopfile);
|
2012-05-08 02:49:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void serverCleanUp()
|
|
|
|
{
|
|
|
|
/* Try to remove shutdown and PID files and exit */
|
2012-05-08 03:23:59 +00:00
|
|
|
server_remove_stopfile();
|
2012-05-08 02:49:52 +00:00
|
|
|
char filename[1024];
|
2012-05-08 03:23:59 +00:00
|
|
|
if (FORM_SERVAL_INSTANCE_PATH(filename, PIDFILE_NAME))
|
2012-05-08 02:49:52 +00:00
|
|
|
unlink(filename);
|
2012-09-14 02:17:48 +00:00
|
|
|
|
|
|
|
if (FORM_SERVAL_INSTANCE_PATH(filename, "mdp.socket")) {
|
|
|
|
unlink(filename);
|
2012-05-08 02:49:52 +00:00
|
|
|
}
|
2012-07-19 08:29:45 +00:00
|
|
|
dna_helper_shutdown();
|
2012-05-08 02:49:52 +00:00
|
|
|
}
|
|
|
|
|
2012-05-09 09:36:44 +00:00
|
|
|
static void signame(char *buf, size_t len, int signal)
|
|
|
|
{
|
|
|
|
const char *desc = "";
|
|
|
|
switch(signal) {
|
|
|
|
#ifdef SIGHUP
|
|
|
|
case SIGHUP: desc = "HUP"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGINT
|
|
|
|
case SIGINT: desc = "INT"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGQUIT
|
|
|
|
case SIGQUIT: desc = "QUIT"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGILL
|
|
|
|
case SIGILL: desc = "ILL (not reset when caught)"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGTRAP
|
|
|
|
case SIGTRAP: desc = "TRAP (not reset when caught)"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGABRT
|
|
|
|
case SIGABRT: desc = "ABRT"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGPOLL
|
|
|
|
case SIGPOLL: desc = "POLL ([XSR] generated, not supported)"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGEMT
|
|
|
|
case SIGEMT: desc = "EMT"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGFPE
|
|
|
|
case SIGFPE: desc = "FPE"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGKILL
|
|
|
|
case SIGKILL: desc = "KILL (cannot be caught or ignored)"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGBUS
|
|
|
|
case SIGBUS: desc = "BUS"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGSEGV
|
|
|
|
case SIGSEGV: desc = "SEGV"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGSYS
|
|
|
|
case SIGSYS: desc = "SYS"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGPIPE
|
|
|
|
case SIGPIPE: desc = "PIPE"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGALRM
|
|
|
|
case SIGALRM: desc = "ALRM"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGTERM
|
|
|
|
case SIGTERM: desc = "TERM"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGURG
|
|
|
|
case SIGURG: desc = "URG"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGSTOP
|
|
|
|
case SIGSTOP: desc = "STOP"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGTSTP
|
|
|
|
case SIGTSTP: desc = "TSTP"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGCONT
|
|
|
|
case SIGCONT: desc = "CONT"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGCHLD
|
|
|
|
case SIGCHLD: desc = "CHLD"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGTTIN
|
|
|
|
case SIGTTIN: desc = "TTIN"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGTTOU
|
|
|
|
case SIGTTOU: desc = "TTOU"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGIO
|
|
|
|
#if SIGIO != SIGPOLL
|
|
|
|
case SIGIO: desc = "IO"; break;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef SIGXCPU
|
|
|
|
case SIGXCPU: desc = "XCPU"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGXFSZ
|
|
|
|
case SIGXFSZ: desc = "XFSZ"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGVTALRM
|
|
|
|
case SIGVTALRM: desc = "VTALRM"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGPROF
|
|
|
|
case SIGPROF: desc = "PROF"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGWINCH
|
|
|
|
case SIGWINCH: desc = "WINCH"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGINFO
|
|
|
|
case SIGINFO: desc = "INFO"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGUSR1
|
|
|
|
case SIGUSR1: desc = "USR1"; break;
|
|
|
|
#endif
|
|
|
|
#ifdef SIGUSR2
|
|
|
|
case SIGUSR2: desc = "USR2"; break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
snprintf(buf, len, "SIG%s (%d) %s", desc, signal, strsignal(signal));
|
|
|
|
buf[len - 1] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
void signal_handler(int signal)
|
|
|
|
{
|
|
|
|
char buf[80];
|
|
|
|
signame(buf, sizeof(buf), signal);
|
2012-07-03 06:06:51 +00:00
|
|
|
INFOF("Caught %s", buf);
|
2012-05-09 09:36:44 +00:00
|
|
|
switch (signal) {
|
|
|
|
case SIGHUP:
|
|
|
|
case SIGINT:
|
|
|
|
/* Terminate the server process. The shutting down should be done from the main-line code
|
|
|
|
rather than here, so we first try to tell the mainline code to do so. If, however, this is
|
|
|
|
not the first time we have been asked to shut down, then we will do it here. */
|
2012-07-02 03:49:54 +00:00
|
|
|
server_shutdown_check(NULL);
|
2012-05-09 09:36:44 +00:00
|
|
|
WHY("Asking Serval process to shutdown cleanly");
|
|
|
|
servalShutdown = 1;
|
|
|
|
return;
|
|
|
|
}
|
2012-07-26 09:01:23 +00:00
|
|
|
serverCleanUp();
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void crash_handler(int signal)
|
|
|
|
{
|
|
|
|
char buf[80];
|
|
|
|
signame(buf, sizeof(buf), signal);
|
|
|
|
WHYF("Caught %s", buf);
|
|
|
|
dump_stack();
|
|
|
|
BACKTRACE;
|
|
|
|
if (serverRespawnOnCrash) {
|
|
|
|
if (sock>-1)
|
|
|
|
close(sock);
|
|
|
|
int i;
|
|
|
|
for(i=0;i<overlay_interface_count;i++)
|
|
|
|
if (overlay_interfaces[i].alarm.poll.fd>-1)
|
|
|
|
close(overlay_interfaces[i].alarm.poll.fd);
|
|
|
|
char execpath[160];
|
|
|
|
if (get_self_executable_path(execpath, sizeof execpath) != -1) {
|
|
|
|
strbuf b = strbuf_alloca(1024);
|
|
|
|
for (i = 0; i < exec_argc; ++i)
|
|
|
|
strbuf_append_shell_quotemeta(strbuf_puts(b, i ? " " : ""), exec_args[i]);
|
|
|
|
INFOF("Respawning %s as %s", execpath, strbuf_str(b));
|
|
|
|
execv(execpath, exec_args);
|
|
|
|
/* Quit if the exec() fails */
|
|
|
|
WHY_perror("execv");
|
|
|
|
} else {
|
|
|
|
WHY("Cannot respawn");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now die of the same signal, so that our exit status reflects the cause.
|
|
|
|
INFOF("Re-sending signal %d to self", signal);
|
|
|
|
kill(getpid(), signal);
|
|
|
|
// If that didn't work, then die normally.
|
|
|
|
INFOF("exit(%d)", -signal);
|
|
|
|
exit(-signal);
|
2012-05-09 09:36:44 +00:00
|
|
|
}
|
|
|
|
|
2012-04-12 23:55:03 +00:00
|
|
|
int getKeyring(char *backing_file)
|
2011-08-08 14:41:46 +00:00
|
|
|
{
|
|
|
|
if (!backing_file)
|
2012-04-12 23:55:03 +00:00
|
|
|
{
|
|
|
|
exit(WHY("Keyring requires a backing file"));
|
2010-07-13 12:15:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-13 00:05:22 +00:00
|
|
|
if (keyring)
|
2012-04-12 23:55:03 +00:00
|
|
|
exit(WHY("Keyring being opened twice"));
|
2012-04-13 00:05:22 +00:00
|
|
|
keyring=keyring_open(backing_file);
|
|
|
|
/* unlock all entries with blank pins */
|
|
|
|
keyring_enter_pins(keyring,"");
|
2010-07-13 12:15:46 +00:00
|
|
|
}
|
2012-04-12 23:55:03 +00:00
|
|
|
keyring_seed(keyring);
|
2011-08-09 06:10:27 +00:00
|
|
|
|
2012-04-12 23:55:03 +00:00
|
|
|
return 0;
|
2010-07-13 12:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int processRequest(unsigned char *packet,int len,
|
|
|
|
struct sockaddr *sender,int sender_len,
|
2012-02-05 05:45:19 +00:00
|
|
|
unsigned char *transaction_id,int recvttl, char *did,char *sid)
|
2010-07-13 12:15:46 +00:00
|
|
|
{
|
|
|
|
/* Find HLR entry by DID or SID, unless creating */
|
|
|
|
int prev_pofs=0;
|
|
|
|
int pofs=OFS_PAYLOAD;
|
|
|
|
|
2012-07-27 01:56:19 +00:00
|
|
|
while (pofs < len) {
|
|
|
|
if (debug & DEBUG_DNARESPONSES)
|
|
|
|
DEBUGF("len=%d, pofs=%d, pofs_prev=%d",len,pofs,prev_pofs);
|
|
|
|
/* Avoid infinite loops */
|
|
|
|
if (pofs<=prev_pofs) break;
|
|
|
|
prev_pofs=pofs;
|
|
|
|
|
|
|
|
if (debug & DEBUG_DNARESPONSES)
|
|
|
|
DEBUGF("action code 0x%02x @ packet offset 0x%x", packet[pofs], pofs);
|
|
|
|
switch (packet[pofs]) {
|
|
|
|
case ACTION_CREATEHLR: {
|
|
|
|
/* Creating an HLR requires an initial DID number and definitely no SID -
|
|
|
|
you can't choose a SID. */
|
|
|
|
if (debug & DEBUG_DNARESPONSES)
|
|
|
|
DEBUGF("Creating a new HLR record. did='%s', sid='%s'",did,sid);
|
|
|
|
if (!did[0] || sid[0])
|
|
|
|
return respondSimple(NULL, ACTION_DECLINED, NULL, 0, transaction_id, recvttl, sender, CRYPT_CIPHERED|CRYPT_SIGNED);
|
|
|
|
if (debug & DEBUG_DNARESPONSES)
|
|
|
|
DEBUG("Verified that create request supplies DID but not SID");
|
|
|
|
/* Creating an identity is nice and easy now with the new keyring */
|
|
|
|
keyring_identity *id=keyring_create_identity(keyring,keyring->contexts[0], "");
|
|
|
|
if (id)
|
|
|
|
keyring_set_did(id, did, "Mr. Smith");
|
|
|
|
if (id==NULL||keyring_commit(keyring))
|
|
|
|
return respondSimple(NULL, ACTION_DECLINED, NULL, 0, transaction_id, recvttl, sender, CRYPT_CIPHERED|CRYPT_SIGNED);
|
|
|
|
else
|
|
|
|
return respondSimple(id, ACTION_OKAY, NULL, 0, transaction_id, recvttl, sender, CRYPT_CIPHERED|CRYPT_SIGNED);
|
|
|
|
pofs += 1;
|
|
|
|
pofs += 1 + SID_SIZE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACTION_PAD: /* Skip padding */
|
|
|
|
pofs++;
|
|
|
|
pofs+=1+packet[pofs];
|
|
|
|
break;
|
|
|
|
case ACTION_EOT: /* EOT */
|
|
|
|
pofs=len;
|
|
|
|
break;
|
|
|
|
case ACTION_STATS: {
|
|
|
|
/* short16 variable id,
|
|
|
|
int32 value */
|
|
|
|
pofs++;
|
|
|
|
short field=packet[pofs+1]+(packet[pofs]<<8);
|
|
|
|
int value=packet[pofs+5]+(packet[pofs+4]<<8)+(packet[pofs+3]<<16)+(packet[pofs+2]<<24);
|
|
|
|
pofs+=6;
|
|
|
|
if (instrumentation_file)
|
|
|
|
{
|
|
|
|
if (!i_f) { if (strcmp(instrumentation_file,"-")) i_f=fopen(instrumentation_file,"a"); else i_f=stdout; }
|
2012-09-27 01:32:58 +00:00
|
|
|
if (i_f) fprintf(i_f,"%ld:%02x%02x%02x%02x:%d:%d\n",(long)time(0),sender->sa_data[0],sender->sa_data[1],sender->sa_data[2],sender->sa_data[3],field,value);
|
2012-07-27 01:56:19 +00:00
|
|
|
if (i_f) fflush(i_f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACTION_SET:
|
|
|
|
WHY("You can only set keyring variables locally");
|
|
|
|
return respondSimple(NULL,ACTION_ERROR,
|
|
|
|
(unsigned char *)"Would be insecure",
|
|
|
|
0,transaction_id,recvttl,
|
|
|
|
sender,CRYPT_CIPHERED|CRYPT_SIGNED);
|
|
|
|
break;
|
|
|
|
case ACTION_GET: {
|
|
|
|
/* Limit transfer size to MAX_DATA_BYTES, plus an allowance for variable packing. */
|
|
|
|
unsigned char data[MAX_DATA_BYTES+16];
|
|
|
|
int dlen=0;
|
|
|
|
int sendDone=0;
|
|
|
|
|
|
|
|
if (debug&DEBUG_DNARESPONSES)
|
|
|
|
dump("Request bytes", &packet[pofs], 8);
|
|
|
|
|
|
|
|
pofs++;
|
|
|
|
int var_id=packet[pofs];
|
|
|
|
int instance=-1;
|
|
|
|
if (var_id&0x80) instance=packet[++pofs];
|
|
|
|
if (instance==0xff) instance=-1;
|
|
|
|
pofs++;
|
|
|
|
int offset=(packet[pofs]<<8)+packet[pofs+1]; pofs+=2;
|
|
|
|
keyring_identity *responding_id=NULL;
|
|
|
|
|
|
|
|
pofs+=2;
|
|
|
|
|
|
|
|
if (debug&DEBUG_DNARESPONSES) {
|
|
|
|
DEBUGF("Processing ACTION_GET (var_id=%02x, instance=%02x, pofs=0x%x, len=%d)",var_id,instance,pofs,len);
|
|
|
|
DEBUGF("Looking for identities with sid=%s did='%s'", (sid&&sid[0])?sid:"null",did?did:"null");
|
|
|
|
}
|
2010-07-13 12:15:46 +00:00
|
|
|
|
2012-07-27 01:56:19 +00:00
|
|
|
/* Keyring only has DIDs in it for now. Location is implied, so we allow that */
|
|
|
|
switch (var_id) {
|
|
|
|
case VAR_DIDS:
|
|
|
|
case VAR_LOCATIONS:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return respondSimple(NULL,ACTION_ERROR,
|
|
|
|
(unsigned char *)"Unsupported variable",
|
|
|
|
0,transaction_id,recvttl,
|
|
|
|
sender,CRYPT_CIPHERED|CRYPT_SIGNED);
|
2010-07-13 12:15:46 +00:00
|
|
|
}
|
2012-07-27 01:56:19 +00:00
|
|
|
|
2010-07-13 12:15:46 +00:00
|
|
|
{
|
2012-07-27 01:56:19 +00:00
|
|
|
int cn=0,in=0,kp=0;
|
|
|
|
int found=0;
|
|
|
|
int count=0;
|
|
|
|
while(cn<keyring->context_count) {
|
|
|
|
found=0;
|
|
|
|
if (sid&&sid[0]) {
|
|
|
|
unsigned char packedSid[SID_SIZE];
|
|
|
|
stowSid(packedSid,0,sid);
|
|
|
|
found=keyring_find_sid(keyring,&cn,&in,&kp,packedSid);
|
|
|
|
} else {
|
|
|
|
found=keyring_find_did(keyring,&cn,&in,&kp,did);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct response r;
|
|
|
|
unsigned char packedDid[64];
|
|
|
|
|
|
|
|
if (found&&(instance==-1||instance==count)) {
|
|
|
|
/* We have a matching identity/DID, now see what variable
|
|
|
|
they want.
|
|
|
|
VAR_DIDS and VAR_LOCATIONS are the only ones we support
|
|
|
|
with the new keyring file format for now. */
|
|
|
|
r.var_id=var_id;
|
|
|
|
r.var_instance=instance;
|
|
|
|
switch(var_id) {
|
|
|
|
case VAR_DIDS:
|
|
|
|
/* We need to pack the DID before sending off */
|
|
|
|
r.value_len=0;
|
|
|
|
stowDid(packedDid,&r.value_len,
|
|
|
|
(char *)keyring->contexts[cn]->identities[in]
|
|
|
|
->keypairs[kp]->private_key);
|
|
|
|
r.response=packedDid;
|
|
|
|
break;
|
|
|
|
case VAR_LOCATIONS:
|
|
|
|
r.response=(unsigned char *)"4000@";
|
|
|
|
r.value_len=strlen((char *)r.response);
|
|
|
|
break;
|
2011-05-05 09:10:38 +00:00
|
|
|
}
|
2011-04-27 02:47:26 +00:00
|
|
|
|
2012-07-27 01:56:19 +00:00
|
|
|
/* For multiple packet responses, we want to tag only the
|
|
|
|
last one with DONE, so we queue up the most recently generated
|
|
|
|
packet, and only dispatch it when we are about to produce
|
|
|
|
another. Then at the end of the loop, if we have a packet
|
|
|
|
waiting we simply mark that with with DONE, and everything
|
|
|
|
falls into place. */
|
|
|
|
if (sendDone>0)
|
|
|
|
/* Send previous packet */
|
|
|
|
respondSimple(responding_id,ACTION_DATA,data,dlen,
|
|
|
|
transaction_id,recvttl,sender,
|
|
|
|
CRYPT_CIPHERED|CRYPT_SIGNED);
|
|
|
|
/* Prepare new packet */
|
|
|
|
dlen=0;
|
|
|
|
if (packageVariableSegment(data,&dlen,&r,offset, MAX_DATA_BYTES+16))
|
|
|
|
return WHY("packageVariableSegment() failed.");
|
|
|
|
responding_id = keyring->contexts[cn]->identities[in];
|
|
|
|
|
|
|
|
/* Remember that we need to send this new packet */
|
|
|
|
sendDone++;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* look for next record.
|
|
|
|
Here the placing of DONE at the end of the response stream
|
|
|
|
becomes challenging, as we may be responding as multiple
|
|
|
|
identities. This means we have to DONE after each identity. */
|
|
|
|
int lastin=in,lastcn=cn;
|
|
|
|
kp++;
|
|
|
|
keyring_sanitise_position(keyring,&cn,&in,&kp);
|
|
|
|
if (lastin!=in||lastcn!=cn) {
|
|
|
|
/* moved off last identity, so send waiting packet if there is
|
|
|
|
one. */
|
|
|
|
if (sendDone) {
|
|
|
|
data[dlen++]=ACTION_DONE;
|
|
|
|
data[dlen++]=sendDone&0xff;
|
|
|
|
respondSimple(responding_id,ACTION_DATA,data,dlen,
|
|
|
|
transaction_id,
|
|
|
|
recvttl,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
|
|
|
|
}
|
|
|
|
sendDone=0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-12 23:55:03 +00:00
|
|
|
|
2012-07-27 01:56:19 +00:00
|
|
|
/* Now, see if we have a final queued packet which needs marking with
|
|
|
|
DONE and then sending. */
|
|
|
|
if (sendDone) {
|
|
|
|
data[dlen++]=ACTION_DONE;
|
|
|
|
data[dlen++]=sendDone&0xff;
|
|
|
|
respondSimple(responding_id,ACTION_DATA,data,dlen,transaction_id,
|
|
|
|
recvttl,sender,CRYPT_CIPHERED|CRYPT_SIGNED);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gatewayspec&&(var_id==VAR_LOCATIONS)&&did&&strlen(did))
|
|
|
|
{
|
|
|
|
/* We are a gateway, so offer connection via the gateway as well */
|
|
|
|
unsigned char data[MAX_DATA_BYTES+16];
|
|
|
|
int dlen=0;
|
|
|
|
struct response fake;
|
|
|
|
unsigned char uri[1024];
|
|
|
|
|
|
|
|
/* We use asterisk to provide the gateway service,
|
|
|
|
so we need to create a temporary extension in extensions.conf,
|
|
|
|
ask asterisk to re-read extensions.conf, and then make sure it has
|
|
|
|
a functional SIP gateway.
|
|
|
|
*/
|
|
|
|
if (!asteriskObtainGateway(sid,did,(char *)uri))
|
|
|
|
{
|
2012-04-12 23:55:03 +00:00
|
|
|
|
2012-07-27 01:56:19 +00:00
|
|
|
fake.value_len=strlen((char *)uri);
|
|
|
|
fake.var_id=var_id;
|
|
|
|
fake.response=uri;
|
|
|
|
|
|
|
|
if (packageVariableSegment(data,&dlen,&fake,offset,MAX_DATA_BYTES+16))
|
|
|
|
return WHY("packageVariableSegment() of gateway URI failed.");
|
|
|
|
|
|
|
|
WHY("Gateway claims to be 1st identity, when it should probably have its own identity");
|
|
|
|
respondSimple(keyring->contexts[0]->identities[0],
|
|
|
|
ACTION_DATA,data,dlen,
|
|
|
|
transaction_id,recvttl,sender,
|
|
|
|
CRYPT_CIPHERED|CRYPT_SIGNED);
|
2011-04-27 02:47:26 +00:00
|
|
|
}
|
2012-07-27 01:56:19 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Should we indicate the gateway is not available? */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (debug & DEBUG_PACKETFORMATS) {
|
|
|
|
DEBUGF("Asked to perform unsipported action at Packet offset = 0x%x", pofs);
|
|
|
|
dump("Packet", packet, len);
|
|
|
|
}
|
|
|
|
return WHY("unsupported action");
|
2010-07-13 12:15:46 +00:00
|
|
|
}
|
2012-07-27 01:56:19 +00:00
|
|
|
}
|
2010-07-13 12:15:46 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-12 23:55:03 +00:00
|
|
|
int respondSimple(keyring_identity *id,
|
|
|
|
int action,unsigned char *action_text,int action_len,
|
2012-02-05 05:45:19 +00:00
|
|
|
unsigned char *transaction_id,int recvttl,
|
|
|
|
struct sockaddr *recvaddr,int cryptoFlags)
|
2010-07-13 12:15:46 +00:00
|
|
|
{
|
|
|
|
unsigned char packet[8000];
|
|
|
|
int pl=0;
|
|
|
|
int *packet_len=&pl;
|
|
|
|
int packet_maxlen=8000;
|
|
|
|
int i;
|
|
|
|
|
2011-05-16 07:28:25 +00:00
|
|
|
/* XXX Complain about invalid crypto flags.
|
|
|
|
XXX We don't do anything with the crypto flags right now
|
|
|
|
XXX Other packet sending routines need this as well. */
|
2012-01-08 18:00:23 +00:00
|
|
|
if (!cryptoFlags) return WHY("Crypto-flags not set.");
|
2011-05-16 07:28:25 +00:00
|
|
|
|
2010-07-13 12:15:46 +00:00
|
|
|
/* ACTION_ERROR is associated with an error message.
|
|
|
|
For syntactic simplicity, we do not require the respondSimple() call to provide
|
|
|
|
the length of the error message. */
|
|
|
|
if (action==ACTION_ERROR) {
|
|
|
|
action_len=strlen((char *)action_text);
|
|
|
|
/* Make sure the error text isn't too long.
|
|
|
|
IF it is, trim it, as we still need to communicate the error */
|
|
|
|
if (action_len>255) action_len=255;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare the request packet */
|
2012-01-08 18:00:23 +00:00
|
|
|
if (packetMakeHeader(packet,8000,packet_len,transaction_id,cryptoFlags))
|
|
|
|
return WHY("packetMakeHeader() failed.");
|
2012-04-12 23:55:03 +00:00
|
|
|
if (id)
|
|
|
|
{ if (packetSetSidFromId(packet,8000,packet_len,id))
|
2012-05-24 07:41:55 +00:00
|
|
|
return WHY("invalid SID in reply"); }
|
2010-07-13 12:15:46 +00:00
|
|
|
else
|
|
|
|
{ if (packetSetDid(packet,8000,packet_len,""))
|
2012-05-24 07:41:55 +00:00
|
|
|
return WHY("Could not set empty DID in reply"); }
|
2010-07-13 12:15:46 +00:00
|
|
|
|
|
|
|
CHECK_PACKET_LEN(1+1+action_len);
|
|
|
|
packet[(*packet_len)++]=action;
|
|
|
|
if (action==ACTION_ERROR) packet[(*packet_len)++]=action_len;
|
|
|
|
for(i=0;i<action_len;i++) packet[(*packet_len)++]=action_text[i];
|
|
|
|
|
2012-01-10 05:26:40 +00:00
|
|
|
if (debug&DEBUG_DNARESPONSES) dump("Simple response octets",action_text,action_len);
|
2010-07-13 12:15:46 +00:00
|
|
|
|
2012-02-05 05:45:19 +00:00
|
|
|
if (packetFinalise(packet,8000,recvttl,packet_len,cryptoFlags))
|
2012-01-08 18:00:23 +00:00
|
|
|
return WHY("packetFinalise() failed.");
|
2010-07-13 12:15:46 +00:00
|
|
|
|
2012-07-02 03:22:21 +00:00
|
|
|
if (debug&DEBUG_DNARESPONSES) DEBUGF("Sending response of %d bytes",*packet_len);
|
2010-07-13 12:15:46 +00:00
|
|
|
|
2012-01-08 18:00:23 +00:00
|
|
|
if (packetSendRequest(REQ_REPLY,packet,*packet_len,NONBATCH,transaction_id,recvaddr,NULL))
|
|
|
|
return WHY("packetSendRequest() failed.");
|
2010-07-13 12:15:46 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-08-08 14:41:46 +00:00
|
|
|
|
|
|
|
int createServerSocket()
|
|
|
|
{
|
|
|
|
struct sockaddr_in bind_addr;
|
|
|
|
|
|
|
|
sock=socket(PF_INET,SOCK_DGRAM,0);
|
|
|
|
if (sock<0) {
|
2012-05-08 05:15:26 +00:00
|
|
|
WHY_perror("socket");
|
2012-07-03 06:06:51 +00:00
|
|
|
WHY("Could not create UDP socket.");
|
2011-08-08 14:41:46 +00:00
|
|
|
exit(-3);
|
|
|
|
}
|
|
|
|
|
2012-02-23 01:29:06 +00:00
|
|
|
/* Automatically close socket on calls to exec().
|
|
|
|
This makes life easier when we restart with an exec after receiving
|
|
|
|
a bad signal. */
|
|
|
|
fcntl(sock, F_SETFL,
|
2012-09-05 09:23:22 +00:00
|
|
|
fcntl(sock, F_GETFL, NULL)|
|
|
|
|
#ifdef FD_CLOEXEC
|
|
|
|
FD_CLOEXEC
|
|
|
|
#else
|
|
|
|
O_CLOEXEC
|
|
|
|
#endif
|
|
|
|
);
|
2012-02-23 01:29:06 +00:00
|
|
|
|
2012-05-21 02:52:50 +00:00
|
|
|
int i=1;
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i));
|
2012-02-05 05:45:19 +00:00
|
|
|
|
|
|
|
errno=0;
|
2012-05-21 02:52:50 +00:00
|
|
|
if(setsockopt(sock, IPPROTO_IP, IP_RECVTTL, &i,sizeof(i))<0)
|
2012-05-08 05:15:26 +00:00
|
|
|
WHY_perror("setsockopt(IP_RECVTTL)");
|
2012-02-05 05:45:19 +00:00
|
|
|
|
2011-08-08 14:41:46 +00:00
|
|
|
bind_addr.sin_family = AF_INET;
|
|
|
|
bind_addr.sin_port = htons( PORT_DNA );
|
|
|
|
bind_addr.sin_addr.s_addr = htonl( INADDR_ANY );
|
|
|
|
if(bind(sock,(struct sockaddr *)&bind_addr,sizeof(bind_addr))) {
|
2012-05-08 05:15:26 +00:00
|
|
|
WHY_perror("bind");
|
2012-07-03 06:06:51 +00:00
|
|
|
WHYF("MP HLR server could not bind to UDP port %d", PORT_DNA);
|
2011-08-08 14:41:46 +00:00
|
|
|
exit(-3);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-15 13:08:23 +00:00
|
|
|
#ifdef DEBUG_MEM_ABUSE
|
|
|
|
unsigned char groundzero[65536];
|
2012-02-15 13:21:12 +00:00
|
|
|
int memabuseInitP=0;
|
2012-02-15 13:08:23 +00:00
|
|
|
|
|
|
|
int memabuseInit()
|
|
|
|
{
|
2012-02-15 13:21:12 +00:00
|
|
|
if (memabuseInitP) {
|
2012-07-02 03:22:21 +00:00
|
|
|
WARN("memabuseInit() called more than once");
|
2012-02-15 13:21:12 +00:00
|
|
|
return memabuseCheck();
|
|
|
|
}
|
|
|
|
|
2012-02-15 13:08:23 +00:00
|
|
|
unsigned char *zero=(unsigned char *)0;
|
|
|
|
int i;
|
2012-02-15 13:21:12 +00:00
|
|
|
for(i=0;i<65536;i++) {
|
|
|
|
groundzero[i]=zero[i];
|
2012-07-02 03:22:21 +00:00
|
|
|
//printf("%04x\n",i);
|
2012-02-15 13:21:12 +00:00
|
|
|
}
|
|
|
|
memabuseInitP=1;
|
2012-02-15 13:08:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-15 13:21:12 +00:00
|
|
|
int _memabuseCheck(const char *func,const char *file,const int line)
|
2012-02-15 13:08:23 +00:00
|
|
|
{
|
|
|
|
unsigned char *zero=(unsigned char *)0;
|
|
|
|
int firstAddr=-1;
|
|
|
|
int lastAddr=-1;
|
|
|
|
int i;
|
|
|
|
for(i=0;i<65536;i++) if (groundzero[i]!=zero[i]) {
|
|
|
|
lastAddr=i;
|
|
|
|
if (firstAddr==-1) firstAddr=i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastAddr>0) {
|
2012-07-02 03:22:21 +00:00
|
|
|
WARN("Memory corruption in first 64KB of RAM detected");
|
|
|
|
DEBUGF(" Changed bytes exist in range 0x%04x - 0x%04x",firstAddr,lastAddr);
|
2012-02-15 13:08:23 +00:00
|
|
|
dump("Changed memory content",&zero[firstAddr],lastAddr-firstAddr+1);
|
|
|
|
dump("Initial memory content",&groundzero[firstAddr],lastAddr-firstAddr+1);
|
|
|
|
sleep(1);
|
|
|
|
} else {
|
2012-07-02 03:22:21 +00:00
|
|
|
DEBUGF("All's well at %s() %s:%d",func,file,line);
|
2012-02-15 13:08:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|