/****************************************************************************
* [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
* and we did -Karn
*/
/*
* Check for Mystic
*/
for( vch = first_char; vch; vch = vch->next )
{
if( vch->tmystic > 0 )
{
ch_printf( ch, "&R%s is teaching mystic.\n\r", vch->name );
ch_printf( ch, "&RHotboot Failed.\n\r" );
return FALSE;
}
}
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 %s reshapes the world.\r\n", ch->name );
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;
// 4 %s is one too many. 03.29.05 --Saiyr
fscanf( fp, "%d %d %d %d %d %s %s %s\r", // %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;
}