/***************************************************************************** ** Project : pDirt (Aber IV MUD Daemon) ** Module : recover.c ** Description : A completely rewritten reboot/update system. Quite stable, ** fast and complete. It will store virtually all changes to ** the original start state of a mud and store it to be reloaded ** after a reboot or crash. ** Author : Peter Eussen (Marty) ** Copyright : (c) 1997 Eradicated Worlds ** Date : 1 October 1997 ** Version : 0.1 ***************************************************************************/ #define RECOVER_C #include "kernel.h" #include <stdio.h> #include <time.h> #include <unistd.h> #include "quest.h" #include "quests.h" #include "sendsys.h" #include "log.h" #include "bprintf.h" #include "uaf.h" #include "mail.h" #include "mud.h" #include "writer.h" #include "main.h" #include "editor.h" #include "rooms.h" #include "mobile.h" #include "special.h" #include "fight.h" #include "bootstrap.h" #include "commands.h" #include "tables.h" #include "objsys.h" #include "zones.h" #include "recover.h" #define MAX_ZONE_LEN 50 typedef struct _Players { char name[PNAME_LEN+1]; char host[MAXHOSTNAMELEN]; char realhost[MAXHOSTNAMELEN]; char awaymsg[SETIN_MAX+1]; time_t last_cmd; time_t logged_on; time_t last_command; time_t last_on; int fd; long loc; int vis; int slot; Boolean linkdead; Boolean iamon; int invct; int drunkct; } PlayerRec; typedef struct _Objects { long id; int dam; int armor; OFLAGS oflags; int state; int loc; int cflags; int vis; int size; int value; } ObjectRec; typedef struct _Mobiles { long id; int loc; int armor; int dam; int speed; MFLAGS mflags; PFLAGS pflags; SFLAGS sflags; int score; int strength; int fighting; int agg; } MobileRec; typedef struct _World { int port; int master_sock; int dnsfd; int syslogfd; int reboots; int resets; int crashes; time_t startup; time_t reset_time; time_t last_backup; time_t last_healall; int plrct; int mobct; int objct; int zonct; Boolean full_recover; int tempzones; char season; char modifier; char daytime; char daycount; char light; char temp; unsigned int countdown; int qdsize; /* int qlsize; int pdsize;*/ Boolean qdone[96]; /*nt qlist[96][PNAME_LEN+1];*/ } WorldRec; int dump_players(char *base); int dump_mobiles(char *base); int dump_objects(char *base); int dump_zones(char *base); int load_players(char *base); void recover_error(char *str,char *fn, int line); int recover_mobiles(char *base); int recover_objects(char *base); int recover_zones(char *base); extern int dnsfd; int load_recovery(pid_t pidnum,Boolean *was_update) { FILE *fp; char basefn[255]; char fn[255]; WorldRec w; extern fd_set sockets_fds, buffer_fds; extern int syslogfd; sprintf(basefn,"%s/REBOOT/%d",DATA_DIR,pidnum); sprintf(fn,"%s.info",basefn); if ((fp = fopen(fn,"r")) == NULL) { recover_error("load_recovery()",__FILE__,__LINE__); exit(2); } else { extern int port_number; extern int main_socket; extern CALENDAR calendar; if (fread(&w,sizeof(WorldRec),1,fp) != 1) { recover_error("load_recovery",__FILE__,__LINE__); exit(3); } if (bootstrap() < 0) { recover_error("bootstrap() in load_recovery()",__FILE__,__LINE__); exit(4); } FD_ZERO(&sockets_fds); FD_ZERO(&buffer_fds); FD_SET(w.master_sock,&sockets_fds); if (recover_zones(basefn) != w.zonct) { recover_error("recover_zones() in load_recovery()",__FILE__,__LINE__); exit(3); } boot_reset(); numreboots = w.reboots; numresets = w.resets; numcrashes = w.crashes; last_startup = w.startup; last_reset = w.reset_time; last_backup = w.last_backup; last_healall = w.last_healall; port_number = w.port; main_socket = w.master_sock; dnsfd = w.dnsfd; syslogfd = w.syslogfd; calendar.season = w.season; calendar.modifier = w.modifier; calendar.daytime = w.daytime; calendar.daycount = w.daycount; calendar.light = w.light; calendar.temp = w.temp; calendar.countdown = w.countdown; bzero(questsdone,w.qdsize * sizeof(Boolean)); /* bzero(QuestDoneBy,w.qlsize * (sizeof(char) * (PNAME_LEN +1)));*/ if (load_players(basefn) != w.plrct) { mudlog("RECOVER: Amount of players in file does not correspond with info."); exit(3); } if ((*was_update = w.full_recover)) { bcopy(w.qdone,questsdone,sizeof(Boolean) * w.qdsize); /* bcopy(w.qlist,QuestDoneBy,w.qlsize * (sizeof(char) * (PNAME_LEN+1)));*/ if (recover_mobiles(basefn) != w.mobct) { mudlog("RECOVER: Amount of mobiles in file does not correspond with info."); exit(3); } if (recover_objects(basefn) != w.objct) { mudlog("RECOVER: Amount of objects in file does not correspond with info."); exit(3); } } } mudlog("RECOVER: %s finished",*was_update ? "Update" : "Reboot"); fclose(fp); remove(fn); return 0; } int dump_recovery(Boolean full) { FILE *fp; char fn2[255]; char fn[255]; WorldRec w; sprintf(fn,"%s/REBOOT/%d",DATA_DIR, getpid()); sprintf(fn2,"%s.info",fn); if ((fp = fopen(fn2,"w")) == NULL) return -1; else { extern int port_number; extern int main_socket; extern int syslogfd; extern CALENDAR calendar; w.qdsize = LAST_QUEST; /* w.pdsize = ARRAYSIZE(PuzzlesDone);*/ bzero(w.qdone,sizeof(w.qdone)); /* bzero(w.qlist,sizeof(w.qlist)); bzero(w.pdone,sizeof(w.pdone));*/ if (full) { w.mobct = dump_mobiles(fn); w.objct = dump_objects(fn); bcopy(questsdone,w.qdone,sizeof(w.qdone)); /*bcopy(QuestDoneBy,w.qlist,sizeof(w.qlist));*/ } w.zonct = dump_zones(fn); w.plrct = dump_players(fn); w.full_recover = full; w.port = port_number; w.master_sock = main_socket; w.dnsfd = dnsfd; w.syslogfd = syslogfd; w.reboots = numreboots; w.resets = numresets; w.crashes = numcrashes; w.startup = last_startup; w.reset_time = last_reset; w.last_backup = last_backup; w.last_healall = last_healall; w.season = calendar.season; w.modifier = calendar.modifier; w.daytime = calendar.daytime; w.daycount = calendar.daycount; w.light = calendar.light; w.temp = calendar.temp; w.countdown = calendar.countdown; if (fwrite((void *)&w,sizeof(WorldRec),1,fp) == -1) { recover_error("dump_recovery()",__FILE__,__LINE__); exit(1); } fclose(fp); } return 0; } int dump_players(char *base) { FILE *fp; char fn[255]; int i,ct = 0; PlayerRec p; sprintf(fn,"%s.players",base); if ((fp = fopen(fn,"w")) == NULL) return -1; else { for (i = 0;i < max_players; i++) { if (players[i].iamon) { strcpy(p.name,pname(i)); strcpy(p.host,players[i].hostname); strcpy(p.realhost,players[i].realhostname); strcpy(p.awaymsg,players[i].awaymsg); p.last_cmd = players[i].last_cmd; p.logged_on = players[i].logged_on; p.last_command = players[i].last_command; p.last_on = players[i].p_last_on; p.fd = players[i].fil_des; p.loc = loc_id(ploc(i)); p.vis = pvis(i); p.slot = i; p.linkdead = players[i].linkdead; p.iamon = players[i].iamon; /* Clear out all editors etc */ if (players[i].inmailer) { int temp = mynum; setup_globals(i); abort_mail(); setup_globals(temp); } terminate_all_writers(i); save_player(i,True); if (fwrite((void *)&p,sizeof(PlayerRec),1,fp) == -1) { recover_error("player_dump()",__FILE__,__LINE__); exit(1); } ct++; } else if (players[i].inp_handler != NULL) { setup_globals(i); bprintf("\n\r"MUDNAME" is about to reboot and can therefore can not service you.\n" "Please try to login in a few minutes. Thanks for your cooperation.\n"); bflush(); if (cur_player->stream != NULL) fclose(cur_player->stream); close(cur_player->fil_des); mudlog("RECOVER: Disconnecting connection from host %s",cur_player->hostname); } } terminate_editors(); fclose(fp); } return ct; } int dump_mobiles(char *base) { char fn[255]; FILE *fp; int i=0,ct = 0; MobileRec m; sprintf(fn,"%s.NPC",base); if ((fp = fopen(fn,"w")) == NULL) return -1; else { for (i = max_players; i < numchars; i++) { if (loc_id(ploc(i)) != ploc_reset(i) || parmor(i) != parmor_reset(i) || pdam(i) != pdam_reset(i) || pspeed(i) != pspeed_reset(i) || pflags(i).b1 != pflags_reset(i).b1 || pflags(i).b2 != pflags_reset(i).b2 || pflags(i).b3 != pflags_reset(i).b3 || mflags(i).h != mflags_reset(i).l || mflags(i).l != mflags_reset(i).h || sflags(i).b1 != sflags_reset(i).b1 || sflags(i).b2 != sflags_reset(i).b2 || sflags(i).b3 != sflags_reset(i).b3 || pscore(i) != 0 || pstr(i) != pstr_reset(i) || pfighting(i) != -1 || pagg(i) != pagg_reset(i)) { m.id = pnum(i); m.loc = loc_id(ploc(i)); m.armor = parmor(i); m.dam = pdam(i); m.speed = pspeed(i); m.pflags = pflags(i); m.mflags = mflags(i); m.sflags = sflags(i); m.score = pscore(i); m.strength = pstr(i); m.fighting = pfighting(i); m.agg = pagg(i); if (fwrite((void *)&m,sizeof(MobileRec),1,fp) == -1) { recover_error("dump_mobiles()",__FILE__,__LINE__); exit(1); } ct++; } } fclose(fp); } return ct; } int dump_objects(char *base) { char fn[255]; FILE *fp; int i,ct = 0; ObjectRec o; sprintf(fn,"%s.objects",base); if ((fp = fopen(fn,"w")) == NULL) return -1; else { for (i = 0; i < numobs; i++) { if (odamage(i) != odamage_reset(i) || oarmor(i) != oarmor_reset(i) || obits(i).b1 != obits_reset(i).b1 || obits(i).b2 != obits_reset(i).b2 || obits(i).b3 != obits_reset(i).b3 || state(i) != state_reset(i) || oloc(i) != oloc_reset(i) || ocarrf(i) != ocarrf_reset(i) || ovis(i) != ovis_reset(i) || osize(i) != osize_reset(i) || obaseval(i) != ovalue_reset(i)) { o.id = obj_id(i); o.dam = odamage(i); o.armor = oarmor(i); o.oflags.b1 = obits(i).b1; o.oflags.b2 = obits(i).b2; o.oflags.b3 = obits(i).b3; o.state = state(i); o.loc = oloc(i); o.cflags = ocarrf(i); o.vis = ovis(i); o.size = osize(i); o.value = obaseval(i); if (fwrite((void *)&o,sizeof(ObjectRec),1,fp) == -1) { recover_error("dump_objects()",__FILE__,__LINE__); exit(1); } ct++; } } fclose(fp); } return ct; } int dump_zones(char *base) { char fn[255]; FILE *fp; char z[MAX_ZONE_LEN]; int ct = 0, i; sprintf(fn,"%s.zones",base); if ((fp = fopen(fn,"w")) == NULL) return -1; else { for (i = num_const_zon; i < numzon; i++) { if (!ztemporary(i)) { strncpy((char *)z,zname(i),MAX_ZONE_LEN-1); ct++; fwrite((void *)z,MAX_ZONE_LEN,1,fp); } } } fclose(fp); return ct; } int recover_zones(char *base) { char fn[255]; FILE *fp; int ct = 0; char z[MAX_ZONE_LEN]; sprintf(fn,"%s.zones",base); if ((fp = fopen(fn,"r")) == NULL) return -1; else { fread((void *)z,MAX_ZONE_LEN,1,fp); while (!feof(fp)) { ct++; if (load_zone(z,NULL,NULL,NULL,NULL,NULL,NULL) == -1) { mudlog("RECOVER: zone %s not found.\n",z); } fread((void *)z,MAX_ZONE_LEN,1,fp); } } fclose(fp); remove(fn); return ct; } int load_players(char *base) { FILE *fp; char fn[255]; int i,ct = 0,loc = 0; PlayerRec p; extern int width; extern fd_set sockets_fds; sprintf(fn,"%s.players",base); if ((fp = fopen(fn,"r")) == NULL) { recover_error("load_players()",__FILE__,__LINE__); return -1; } else { for (i=0; i < max_players; i++) initialize_slot(i); fread(&p,sizeof(PlayerRec),1,fp); while (!feof(fp)) { ct++; i = p.slot; setup_globals(i); getuafinfo(p.name); fetchprmpt(mynum); strcpy(pname(i), p.name); strcpy(players[i].hostname,p.host); strcpy(players[i].realhostname,p.realhost); strcpy(players[i].awaymsg,p.awaymsg); players[i].last_cmd = p.last_cmd; players[i].logged_on = p.logged_on; players[i].last_command = p.last_command; players[i].p_last_on = p.last_on; players[i].iamon = p.iamon; players[i].linkdead = p.linkdead; players[i].fil_des = p.fd; loc = find_loc_by_id(p.loc); if (exists(loc)) setploc(i,loc); else if (exists((loc = find_loc_by_id(phome(i)))) ) setploc(i,loc); else setploc(i,get_rand_start()); cur_player->isawiz = (OPERATOR(p.name) || privileged_user(p.name)); setpvis(i,p.vis); setpwpn(i,-1); setphelping(i,-1); setpfighting(i,-1); setpsitting(i,0); push_input_handler(get_command); if (!p.linkdead && p.fd >= 0) { if (cur_player->fil_des >= width) width = cur_player->fil_des; FD_SET(p.fd,&sockets_fds); cur_player->sock_buffer_p = cur_player->sock_buffer_end = cur_player->sock_buffer; cur_player->stream = fdopen(p.fd,"w"); if (cur_player->stream == NULL) { initialize_slot(i); mudlog("RECOVER: Could not initialise slot for %s.",p.name); } else fprintf(cur_player->stream,"You feel yourself floating in an infinite void, as you wait for the world\n" "to recover from the disaster.\n"); } if (cur_player->stream != NULL) { setplayerfunc(i); get_command(NULL); } fread(&p,sizeof(PlayerRec),1,fp); } } fclose(fp); remove(fn); return ct; } int recover_mobiles(char *base) { char fn[255]; FILE *fp; int ct = 0,loc=0; MobileRec m; sprintf(fn,"%s.NPC",base); if ((fp = fopen(fn,"r")) == NULL) return -1; else { fread(&m,sizeof(MobileRec),1,fp); while (!feof(fp)) { ct++; m.id += max_players; loc = find_loc_by_id(m.loc); if (exists(loc)) setploc(m.id,loc); else setploc(m.id,dead_loc); setparmor(m.id,m.armor); setpdam(m.id,m.dam); setpspeed(m.id,m.speed); pscore(m.id) = m.score; setpstr(m.id,m.strength); setpfighting(m.id,m.fighting); setpagg(m.id,m.agg); pflags(m.id) = m.pflags; mflags(m.id) = m.mflags; sflags(m.id) = m.sflags; fread(&m,sizeof(MobileRec),1,fp); } } fclose(fp); remove(fn); return ct; } int recover_objects(char *base) { char fn[255]; FILE *fp; int ob,ct = 0; ObjectRec o; sprintf(fn,"%s.objects",base); if ((fp = fopen(fn,"r")) == NULL) return -1; else { fread(&o,sizeof(ObjectRec),1,fp); while (!feof(fp)) { ct++; ob = find_object_by_id(o.id); if (ob >= 0) { osetdamage(ob,o.dam); osetarmor(ob,o.armor); obits(ob) = o.oflags; state(ob) = o.state; setoloc(ob,o.loc,o.cflags); osetvis(ob,o.vis); osetsize(ob,o.size); osetbaseval(ob,o.value); } fread(&o,sizeof(ObjectRec),1,fp); } } fclose(fp); remove(fn); return ct; } void recover_error(char *fun,char *fn, int line) { sprintf(globalbuf,"Recover Failed: %s [%s:%d]",fun,fn,line); progerror(globalbuf); exit(1); }