/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | \\._.// *
* -----------------------------------------------------------| (0...0) *
* SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by Derek Snider | ).:.( *
* -----------------------------------------------------------| {o o} *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, | / ' ' \ *
* Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek, |~'~.VxvxV.~'~*
* Tricops and Fireblade | *
* ------------------------------------------------------------------------ *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* ------------------------------------------------------------------------ *
* Hotboot support code *
****************************************************************************/
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include "mud.h"
#ifdef USE_IMC
#include "imc.h"
#include "icec.h"
#endif
// bool write_to_descriptor( DESCRIPTOR_DATA *d, char *txt, int length );
bool write_to_descriptor( int desc, char *txt, int length );
bool write_to_descriptor_old( int desc, char *txt, int length );
#ifdef MCCP
#define TELOPT_COMPRESS 85
#define TELOPT_COMPRESS2 86
bool compressStart args( ( DESCRIPTOR_DATA *d, unsigned char telopt ) );
bool compressEnd args( ( DESCRIPTOR_DATA *d ) );
const char compress2_on_str_2 [] = { '\xFF', '\xFB', TELOPT_COMPRESS2, '\0' };
#endif
extern int control; /* Controlling descriptor */
/* Used by hotboot code */
void save_areas( void )
{
AREA_DATA *tarea;
char filename[256];
for( tarea = first_build; tarea; tarea = tarea->next )
{
if( !IS_SET( tarea->status, AREA_LOADED ) )
continue;
sprintf( filename, "%s%s", BUILD_DIR, tarea->filename );
fold_area( tarea, filename, FALSE );
}
return;
}
/*
* Check to see if people are busy doing stuff that they wouldn't
* want a hotboot to off during it -Goku
*/
bool isSafeToHotboot(CHAR_DATA *ch)
{
ROOM_INDEX_DATA *tRoom;
CHAR_DATA *vch;
bool found = FALSE;
bool extrafound = FALSE;
char buf [MAX_STRING_LENGTH];
/* Check for people in the HBTC */
tRoom = get_room_index(ROOM_HBTC);
/*
if (tRoom->first_person)
{
send_to_char( "The HBTC is in use, hotboot aborted.\n\r", ch );
return FALSE;
}
*/
/* Fixed so that hotboot will still occur if the only person(s)
* in the HBTC are mobs or immortals. - Karma
*/
for( vch = tRoom->first_person; vch; vch = vch->next_in_room )
{
if( IS_NPC(vch) )
continue;
if( !IS_NPC(vch) && !IS_IMMORTAL(vch) )
{
if( !found )
{
sprintf(buf,"%s",vch->name);
found = TRUE;
}
else
{
strcat(buf," and ");
strcat(buf,vch->name);
extrafound = TRUE;
}
//send_to_char( "The HBTC is in use, hotboot aborted.\n\r", ch );
}
}
if( found )
{
if( buf[0] == '\0' )
bug("Hotboot hbtc check: NULL buf",0);
if( extrafound )
ch_printf(ch,"%s are in the HBTC. Hotboot aborted.\n\r",buf);
else
ch_printf(ch,"%s is in the HBTC. Hotboot aborted.\n\r",buf);
return FALSE;
}
/* we could check for other stuff as well -Goku */
return TRUE;
}
/* Warm reboot stuff, gotta make sure to thank Erwin for this :) */
void do_hotboot( CHAR_DATA *ch, char *argument )
{
FILE *fp;
DESCRIPTOR_DATA *d, *de_next;
char buf [100], buf2[100]/*, buf3[100], buf4[100], buf5[100]*/;
char arg [MAX_STRING_LENGTH];
one_argument(argument, arg);
/* typing "hotboot force" will force a hotboot */
if (str_cmp(arg, "force"))
if (!isSafeToHotboot(ch))
return;
sprintf( log_buf, "Hotboot initiated by %s.", ch->name );
log_string( log_buf );
fp = fopen( HOTBOOT_FILE, "w" );
if( !fp )
{
send_to_char( "Hotboot file not writeable, aborted.\n\r", ch );
sprintf( log_buf, "Could not write to hotboot file: %s. Hotboot aborted.", HOTBOOT_FILE );
log_string( log_buf );
perror( "do_copyover:fopen" );
return;
}
/* Consider changing all loaded prototype areas here, if you use OLC */
log_string( "Saving modified area files..." );
save_areas();
#ifdef USE_IMC
log_string( "Initiating IMC2 shutdown...." );
imc_shutdown(); /* shut down IMC */
log_string( "IMC2 shutdown completed." );
#endif
log_string( "Saving player files and connection states...." );
if( ch && ch->desc )
write_to_descriptor( ch->desc->descriptor, "\e[0m", 0 );
sprintf( buf, "\n\rThe flow of time is halted momentarily as the world is reshaped!\n\r" );
if ( auction->item )
{
sprintf(buf, "Sale of %s has been stopped by mud.\n\r",
auction->item->short_descr);
talk_auction(buf);
obj_to_char(auction->item, auction->seller);
auction->item = NULL;
if ( auction->buyer && auction->buyer != auction->seller )
{
auction->buyer->gold += auction->bet;
send_to_char("Your money has been returned.\n\r", auction->buyer);
}
}
/* For each playing descriptor, save its state */
for( d = first_descriptor; d ; d = de_next )
{
CHAR_DATA * och = CH(d);
de_next = d->next; /* We delete from the list , so need to save this */
if( !d->character || d->connected < CON_PLAYING ) /* drop those logging on */
{
write_to_descriptor( d->descriptor, "\n\rSorry, we are rebooting. Come back in a few minutes.\n\r", 0 );
close_socket( d, FALSE ); /* throw'em out */
}
else
{
fprintf( fp, "%d %d %d %d %d %s %s %s\r",
d->descriptor,
#ifdef MCCP
(int)d->compressing,
#else
0,
#endif
och->in_room->vnum, d->port, d->idle, och->name, d->user, d->host );
save_char_obj( och );
#ifdef I3
I3_close_char( d );
#endif
write_to_descriptor( d->descriptor, buf, 0 );
#ifdef MCCP
compressEnd( d );
#endif
}
}
fprintf( fp, "-1 0 0 0 0 x x x\r" );
FCLOSE( fp );
#ifdef I3
log_string( "Initiating Intermud-3 shutdown...." );
I3_shutdown( 0 );
log_string( "Intermud-3 shutdown completed." );
#endif
log_string( "Executing hotboot...." );
/* Close reserve and other always-open files and release other resources */
FCLOSE( fpReserve );
FCLOSE( fpLOG );
/* exec - descriptors are inherited */
sprintf( buf, "%d", port );
sprintf( buf2, "%d", control );
// execl( EXE_FILE, "dbsaga", buf, "hotboot", buf2, buf3, buf4, buf5, (char *)NULL );
execl( EXE_FILE, "dbsaga", buf, "hotboot", buf2, (char *)NULL );
/* Failed - sucessful exec will not return */
perror( "do_hotboot: execl" );
/* Here you might want to reopen fpReserve */
/* Since I'm a neophyte type guy, I'll assume this is a good idea and cut and past from main() */
if( ( fpReserve = fopen( NULL_FILE, "r" ) ) == NULL )
{
perror( NULL_FILE );
exit( 1 );
}
if( ( fpLOG = fopen( NULL_FILE, "r" ) ) == NULL )
{
perror( NULL_FILE );
exit( 1 );
}
log_string( "Hotboot execution failed!!" );
send_to_char( "Hotboot FAILED!\n\r", ch );
}
/* Recover from a hotboot - load players */
void hotboot_recover()
{
DESCRIPTOR_DATA *d = NULL;
FILE *fp;
char name[100];
char host[MAX_STRING_LENGTH];
char user[MAX_STRING_LENGTH];
int desc, dcompress, room, dport, idle;
char *lock = NULL;
bool fOld;
fp = fopen( HOTBOOT_FILE, "r" );
if (!fp) /* there are some descriptors open which will hang forever then ? */
{
perror( "hotboot_recover: fopen" );
log_string( "Hotboot file not found. Exitting.\n\r" );
exit( 1 );
}
unlink( HOTBOOT_FILE ); /* In case something crashes - doesn't prevent reading */
for( ; ; )
{
d = NULL;
fscanf( fp, "%d %d %d %d %d %s %s %s %s\r", &desc, &dcompress, &room, &dport, &idle, name, user, host, lock );
if (desc == -1 || feof(fp) )
{
break;
}
/* Write something, and check if it goes error-free */
// if( !dcompress && !write_to_descriptor( desc, "\n\rThe ether swirls in chaos.\n\r", 0 ) )
// if( !dcompress)
// {
//bug("desc:%d dcompress:%d - %d %d %d %s %s %s", desc,dcompress, room, dport, idle, name, user, host );
// if (!write_to_descriptor( desc, "\n\rThe ether swirls in chaos.\n\r", 0 ) )
// {
// close( desc ); /* nope */
// continue;
// }
// }
#ifdef MCCP
if( !dcompress && !write_to_descriptor_old( desc, "\n\rThe ether swirls in chaos.\n\r", 0 ) )
#else
if( !write_to_descriptor( desc, "\n\rThe ether swirls in chaos.\n\r", 0 ) )
#endif
{
close( desc ); /* nope */
continue;
}
CREATE( d, DESCRIPTOR_DATA, 1 );
d->next = NULL;
d->descriptor = desc;
d->connected = CON_GET_NAME;
d->outsize = 2000;
d->idle = 0;
d->lines = 0;
d->scrlen = 24;
d->newstate = 0;
d->prevcolor = 0x07;
d->ansi = TRUE;
d->ifd = -1;
d->ipid = -1;
CREATE( d->outbuf, char, d->outsize );
d->user = STRALLOC( user );
d->host = STRALLOC( host );
d->port = dport;
d->idle = idle;
#ifdef MCCP
/* Start MCCP back up for who ever had it enabled */
if (dcompress)
write_to_buffer( d, compress2_on_str_2, 0 );
// compressStart(d, TELOPT_COMPRESS2);
#endif
LINK( d, first_descriptor, last_descriptor, next, prev );
d->connected = CON_COPYOVER_RECOVER; /* negative so close_socket will cut them off */
/* Now, find the pfile */
fOld = load_char_obj( d, name, FALSE );
if( !fOld ) /* Player file not found?! */
{
write_to_descriptor( d->descriptor, "\n\rSomehow, your character was lost during hotboot. Contact the immortals ASAP.\n\r", 0 );
close_socket( d, FALSE );
}
else /* ok! */
{
write_to_descriptor( d->descriptor, "\n\rTime resumes its normal flow.\n\r", 0 );
d->character->in_room = get_room_index( room );
if( !d->character->in_room )
d->character->in_room = get_room_index( ROOM_VNUM_TEMPLE );
/* Insert in the char_list */
add_char( d->character );
char_to_room( d->character, d->character->in_room );
act( AT_MAGIC, "A puff of ethereal smoke disipates around you!", d->character, NULL, NULL, TO_CHAR );
act( AT_MAGIC, "$n appears in a puff of ethereal smoke!", d->character, NULL, NULL, TO_ROOM );
d->connected = CON_PLAYING;
if ( ++num_descriptors > sysdata.maxplayers )
sysdata.maxplayers = num_descriptors;
#ifdef I3
I3_char_login( d->character );
#endif
}
}
FCLOSE( fp );
log_string( "Hotboot recovery complete." );
return;
}