/*
** Code originally derived from Lpmud 1.3 (Lars Penj|)
**
** Significantly modified: Geoff Wong and Mike McGaughey.
*/
#include <sys/types.h>
#ifdef __WIN32__
#include <signal.h>
#else
#include <sys/wait.h>
#include <sys/signal.h>
#endif
#ifdef FCNTL
#include <fcntl.h>
#endif
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include "security.h"
#include "proto.h"
#include "apply.h"
#include "comm.h"
#include "consts.h"
#include "fsecure.h"
#include "lpc.h"
/*
* The 'current_time' is updated whenever update_current_time is called.
*/
int current_time;
int current_time_us; /* microseconds */
void update_current_time()
{
struct timeval t;
gettimeofday(&t, (struct timezone *)0);
current_time = t.tv_sec;
current_time_us = t.tv_usec;
}
extern Obj *command_giver;
extern int num_player, num_made;
extern char * mud_lib;
void load_first_objects();
extern void call_out(),
unroot(),
prepare_ipc(),
open_error_log(),
ed_cmd (char *),
shutdowngame();
extern int get_message (char *, int),
player_parser (char *),
child_termination();
extern int game_is_being_shut_down;
int time_to_call_heart_beat = 0;
int comm_time_to_call_heart_beat = 0; /* this is set by
* interrupt, */
/*
* There are global variables that must be zeroed before any execution. In
* case of errors, there will not be a longjmp(), and the variables will have to
* be cleared explicitely. They are normally maintained by the code that use
* them.
*/
void
clear_state()
{
Sset_current(NULL, "clear state");
command_giver = NULL;
}
void
logon(Obj *ob)
{
/*
* Set current object to ob, so that the static function "logon" can be
* called.
*/
/* NOTE: this uses call_out, which currently sets command_giver to
the logon object - beware of subtle interactions if you are using
switch_interactive, etc, in the LPC.
*/
apply_clean(C("logon"), ob, 0);
}
#if 0
/*
* Take a player command and parse it. The command can also come from a NPC.
* Beware that 'str' can be modified and extended !
*/
int remote_command = 0; /* see if parse is being called
* through command(). */
int
parse_command(char *str, Obj *ob, int from_command)
{
Obj *save = command_giver;
int res;
if (from_command) remote_command = 1;
command_giver = ob;
res = player_parser(str);
command_giver = save;
if (from_command) remote_command = 0;
return res;
}
#endif
/*
* This is the backend. We will stay here for ever (almost).
* NB: profiling version. Every 5 minutes, dumps updated profile.
*/
void
backend()
{
char buff[MAX_TEXT_IN];
(void) printf("Setting up ipc.\n");
/* open sockets for connection from the outside world */
prepare_ipc();
open_error_log();
#ifndef __WIN32__
/* chroot() if we can */
if (geteuid() == 0) chroot(mud_lib);
/* go into secure mode - should we do this before loading objects? */
unroot();
#endif
(void) printf("Loading core objects.\n");
load_first_objects();
#ifndef __WIN32__
(void) signal(SIGHUP, startshutdowngame);
(void) signal(SIGCHLD, child_termination);
(void) signal(SIGPIPE, SIG_IGN);
#endif
/* We come here after errors, and have to clear some global variables. */
clear_state();
/* This is the backend which drives the entire process */
while (1)
{
if (game_is_being_shut_down)
{
shutdowngame();
break;
}
command_giver = 0;
if (get_message(buff, sizeof buff))
{
update_load_av();
/*
* Now we have a string from the player. This string can go to
* one of several places. If it is prepended with a '!', then it
* is an escape from the 'ed' editor, so we send it as a command
* to the parser. If any object function is waiting for an input
* string, then send it there. Otherwise, send the string to the
* parser. The player_parser() will find that current_object is
* 0, and then set current_object to point to the object that
* defines the command. This will enable such functions to be
* static.
*/
if (set_timer(MAX_LOOP_TIME))
{
apply_clean(C("catch_input"), command_giver, make_string(buff));
#if 0
gameval = apply_single(C("catch_input"), command_giver,make_string(bbuff));
// ? wipe(1);
#endif
}
reset_timer();
}
command_giver = 0;
//printf("\nBefore call_out() rCT=%d\n", (rCT - (int) control)/4);
call_out();
//printf("After call_out() rCT=%d\n", (rCT - (int) control)/4);
flush_players();
//clear_stack();
}
// get here on shutdown
}
/*
* There is a file with a list of objects to be initialized at start up.
* Also loads secure/master.
*/
Obj * Master = 0;
Obj * Emulate = 0;
void
load_first_objects()
{
Obj *o;
/* clear_stack(); */
#ifdef EMULATE_EFUN
if ((Emulate = load_object(C(EMULATE_EFUN))))
{
/* hash emulate functions! (fast lookup) */
add_functions(Emulate->code, Emulate->code, 0);
}
else
{
printf("Terminating: Failed to load %s\n", EMULATE_EFUN);
exit(1);
}
#endif
if (!(o = load_object(C(OBJ_MASTER))))
{
printf("Terminating: Unable to load %s.\n", OBJ_MASTER);
exit(1);
}
Master = o;
setuid_me((Obj *)0, o, GOD*2, 0);
}
static double load_av = 0.0;
void update_load_av()
{
extern double consts[5];
extern int current_time;
static int last_time;
int n;
double c;
static int acc = 0;
acc++;
if (current_time == last_time) return;
n = current_time - last_time;
if (n < sizeof consts / sizeof consts[0]) c = consts[n];
else c = 0; /*exp(-n / 900.0); */
load_av = c * load_av + acc * (1 - c) / n;
last_time = current_time;
acc = 0;
}
float LPC_load_av()
{
return (float)load_av;
}
#if 0
Val * query_load_av()
{
char buff[100];
sprintf(buff, "%.2f cmds/s, %.2f comp lines/s", load_av, LPC_compile_av());
return make_string(buff);
}
#endif