// cque.cpp -- commands and functions for manipulating the command queue. // // $Id: cque.cpp,v 1.29 2005/10/14 17:34:09 sdennis Exp $ // // MUX 2.4 // Copyright (C) 1998 through 2004 Solid Vertical Domains, Ltd. All // rights not explicitly given are reserved. // #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include <signal.h> #include "attrs.h" #include "command.h" #include "interface.h" #include "powers.h" extern int a_Queue(dbref, int); extern int QueueMax(dbref); bool break_called = false; CLinearTimeDelta GetProcessorUsage(void) { CLinearTimeDelta ltd; #ifdef WIN32 if (platform == VER_PLATFORM_WIN32_NT) { FILETIME ftCreate; FILETIME ftExit; FILETIME ftKernel; FILETIME ftUser; fpGetProcessTimes(hGameProcess, &ftCreate, &ftExit, &ftKernel, &ftUser); ltd.Set100ns(*(INT64 *)(&ftUser)); return ltd; } #endif #if !defined(WIN32) && defined(HAVE_GETRUSAGE) struct rusage usage; getrusage(RUSAGE_SELF, &usage); ltd.SetTimeValueStruct(&usage.ru_utime); return ltd; #else // Either this Unix doesn't have getrusage or this is a // fall-through case for Win32. // CLinearTimeAbsolute ltaNow; ltaNow.GetLocal(); ltd = ltaNow - mudstate.start_time; return ltd; #endif } // --------------------------------------------------------------------------- // add_to: Adjust an object's queue or semaphore count. // static int add_to(dbref executor, int am, int attrnum) { int aflags; dbref aowner; char *atr_gotten = atr_get(executor, attrnum, &aowner, &aflags); int num = mux_atol(atr_gotten); free_lbuf(atr_gotten); num += am; char buff[20]; int nlen = 0; *buff = '\0'; if (num) { nlen = mux_ltoa(num, buff); } atr_add_raw_LEN(executor, attrnum, buff, nlen); return num; } // This Task assumes that pEntry is already unlinked from any lists it may // have been related to. // void Task_RunQueueEntry(void *pEntry, int iUnused) { BQUE *point = (BQUE *)pEntry; dbref executor = point->executor; if ( Good_obj(executor) && !Going(executor)) { giveto(executor, mudconf.waitcost); mudstate.curr_enactor = point->enactor; mudstate.curr_executor = executor; a_Queue(Owner(executor), -1); point->executor = NOTHING; if (!Halted(executor)) { // Load scratch args. // for (int i = 0; i < MAX_GLOBAL_REGS; i++) { if (point->scr[i]) { int n = strlen(point->scr[i]); memcpy(mudstate.global_regs[i], point->scr[i], n+1); mudstate.glob_reg_len[i] = n; } else { mudstate.global_regs[i][0] = '\0'; mudstate.glob_reg_len[i] = 0; } } char *command = point->comm; mux_assert(!mudstate.inpipe); mux_assert(mudstate.pipe_nest_lev == 0); mux_assert(mudstate.poutobj == NOTHING); mux_assert(!mudstate.pout); break_called = false; while ( command && !break_called) { mux_assert(!mudstate.poutnew); mux_assert(!mudstate.poutbufc); char *cp = parse_to(&command, ';', 0); if ( cp && *cp) { // Will command be piped? // if ( command && *command == '|' && mudstate.pipe_nest_lev < mudconf.ntfy_nest_lim) { command++; mudstate.pipe_nest_lev++; mudstate.inpipe = true; mudstate.poutnew = alloc_lbuf("process_command.pipe"); mudstate.poutbufc = mudstate.poutnew; mudstate.poutobj = executor; } else { mudstate.inpipe = false; mudstate.poutobj = NOTHING; } CLinearTimeAbsolute ltaBegin; ltaBegin.GetUTC(); MuxAlarm.Set(mudconf.max_cmdsecs); CLinearTimeDelta ltdUsageBegin = GetProcessorUsage(); char *log_cmdbuf = process_command(executor, point->caller, point->enactor, false, cp, point->env, point->nargs); CLinearTimeAbsolute ltaEnd; ltaEnd.GetUTC(); if (MuxAlarm.bAlarmed) { notify(executor, "GAME: Expensive activity abbreviated."); s_Flags(point->enactor, FLAG_WORD1, Flags(point->enactor) | HALT); s_Flags(point->executor, FLAG_WORD1, Flags(point->executor) | HALT); halt_que(point->enactor, NOTHING); halt_que(executor, NOTHING); } MuxAlarm.Clear(); CLinearTimeDelta ltdUsageEnd = GetProcessorUsage(); CLinearTimeDelta ltd = ltdUsageEnd - ltdUsageBegin; db[executor].cpu_time_used += ltd; ltd = ltaEnd - ltaBegin; if (ltd > mudconf.rpt_cmdsecs) { STARTLOG(LOG_PROBLEMS, "CMD", "CPU"); log_name_and_loc(executor); char *logbuf = alloc_lbuf("do_top.LOG.cpu"); sprintf(logbuf, " queued command taking %s secs (enactor #%d): ", ltd.ReturnSecondsString(4), point->enactor); log_text(logbuf); free_lbuf(logbuf); log_text(log_cmdbuf); ENDLOG; } } // Transition %| value. // if (mudstate.pout) { free_lbuf(mudstate.pout); mudstate.pout = NULL; } if (mudstate.poutnew) { *mudstate.poutbufc = '\0'; mudstate.pout = mudstate.poutnew; mudstate.poutnew = NULL; mudstate.poutbufc = NULL; } } // Clean up %| value. // if (mudstate.pout) { free_lbuf(mudstate.pout); mudstate.pout = NULL; } mudstate.pipe_nest_lev = 0; mudstate.inpipe = false; mudstate.poutobj = NOTHING; } MEMFREE(point->text); point->text = NULL; free_qentry(point); } for (int i = 0; i < MAX_GLOBAL_REGS; i++) { mudstate.global_regs[i][0] = '\0'; mudstate.glob_reg_len[i] = 0; } } // --------------------------------------------------------------------------- // que_want: Do we want this queue entry? // static bool que_want(BQUE *entry, dbref ptarg, dbref otarg) { if ( ptarg != NOTHING && ptarg != Owner(entry->executor)) { return false; } return ( otarg == NOTHING || otarg == entry->executor); } void Task_SemaphoreTimeout(void *pExpired, int iUnused) { // A semaphore has timed out. // BQUE *point = (BQUE *)pExpired; add_to(point->sem, -1, point->attr); point->sem = NOTHING; Task_RunQueueEntry(point, 0); } #ifdef QUERY_SLAVE void Task_SQLTimeout(void *pExpired, int iUnused) { // A SQL Query has timed out. // BQUE *point = (BQUE *)pExpired; Task_RunQueueEntry(point, 0); } #endif // QUERY_SLAVE dbref Halt_Player_Target; dbref Halt_Object_Target; int Halt_Entries; dbref Halt_Player_Run; dbref Halt_Entries_Run; int CallBack_HaltQueue(PTASK_RECORD p) { if ( p->fpTask == Task_RunQueueEntry #ifdef QUERY_SLAVE || p->fpTask == Task_SQLTimeout #endif // QUERY_SLAVE || p->fpTask == Task_SemaphoreTimeout) { // This is a @wait, timed Semaphore Task, or timed SQL Query. // BQUE *point = (BQUE *)(p->arg_voidptr); if (que_want(point, Halt_Player_Target, Halt_Object_Target)) { // Accounting for pennies and queue quota. // dbref dbOwner = point->executor; if (!isPlayer(dbOwner)) { dbOwner = Owner(dbOwner); } if (dbOwner != Halt_Player_Run) { if (Halt_Player_Run != NOTHING) { giveto(Halt_Player_Run, mudconf.waitcost * Halt_Entries_Run); a_Queue(Halt_Player_Run, -Halt_Entries_Run); } Halt_Player_Run = dbOwner; Halt_Entries_Run = 0; } Halt_Entries++; Halt_Entries_Run++; if (p->fpTask == Task_SemaphoreTimeout) { add_to(point->sem, -1, point->attr); } MEMFREE(point->text); point->text = NULL; free_qentry(point); return IU_REMOVE_TASK; } } return IU_NEXT_TASK; } // ------------------------------------------------------------------ // // halt_que: Remove all queued commands that match (executor, object). // // (NOTHING, NOTHING) matches all queue entries. // (NOTHING, <object>) matches only queue entries run from <object>. // (<executor>, NOTHING) matches only queue entries owned by <executor>. // (<executor>, <object>) matches only queue entries run from <objects> // and owned by <executor>. // int halt_que(dbref executor, dbref object) { Halt_Player_Target = executor; Halt_Object_Target = object; Halt_Entries = 0; Halt_Player_Run = NOTHING; Halt_Entries_Run = 0; // Process @wait, timed semaphores, and untimed semaphores. // scheduler.TraverseUnordered(CallBack_HaltQueue); if (Halt_Player_Run != NOTHING) { giveto(Halt_Player_Run, mudconf.waitcost * Halt_Entries_Run); a_Queue(Halt_Player_Run, -Halt_Entries_Run); Halt_Player_Run = NOTHING; } return Halt_Entries; } // --------------------------------------------------------------------------- // do_halt: Command interface to halt_que. // void do_halt(dbref executor, dbref caller, dbref enactor, int key, char *target) { dbref executor_targ, obj_targ; if ((key & HALT_ALL) && !Can_Halt(executor)) { notify(executor, NOPERM_MESSAGE); return; } // Figure out what to halt. // if (!target || !*target) { obj_targ = NOTHING; if (key & HALT_ALL) { executor_targ = NOTHING; } else { executor_targ = Owner(executor); if (!isPlayer(executor)) { obj_targ = executor; } } } else { if (Can_Halt(executor)) { obj_targ = match_thing(executor, target); } else { obj_targ = match_controlled(executor, target); } if (!Good_obj(obj_targ)) { return; } if (key & HALT_ALL) { notify(executor, "Can't specify a target and /all"); return; } if (isPlayer(obj_targ)) { executor_targ = obj_targ; obj_targ = NOTHING; } else { executor_targ = NOTHING; } } int numhalted = halt_que(executor_targ, obj_targ); if (Quiet(executor)) { return; } notify(Owner(executor), tprintf("%d queue entr%s removed.", numhalted, numhalted == 1 ? "y" : "ies")); } int Notify_Key; int Notify_Num_Done; int Notify_Num_Max; int Notify_Sem; int Notify_Attr; // NFY_DRAIN or NFY_NFYALL // int CallBack_NotifySemaphoreDrainOrAll(PTASK_RECORD p) { if (p->fpTask == Task_SemaphoreTimeout) { // This represents a semaphore. // BQUE *point = (BQUE *)(p->arg_voidptr); if ( point->sem == Notify_Sem && ( point->attr == Notify_Attr || !Notify_Attr)) { Notify_Num_Done++; if (Notify_Key == NFY_DRAIN) { // Discard the command // giveto(point->executor, mudconf.waitcost); a_Queue(Owner(point->executor), -1); MEMFREE(point->text); point->text = NULL; free_qentry(point); return IU_REMOVE_TASK; } else { // Allow the command to run. The priority may have been // PRIORITY_SUSPEND, so we need to change it. // if (isPlayer(point->enactor)) { p->iPriority = PRIORITY_PLAYER; } else { p->iPriority = PRIORITY_OBJECT; } p->ltaWhen.GetUTC(); p->fpTask = Task_RunQueueEntry; return IU_UPDATE_TASK; } } } return IU_NEXT_TASK; } // NFY_NFY or NFY_QUIET // int CallBack_NotifySemaphoreFirstOrQuiet(PTASK_RECORD p) { // If we've notified enough, exit. // if ( Notify_Key == NFY_NFY && Notify_Num_Done >= Notify_Num_Max) { return IU_DONE; } if (p->fpTask == Task_SemaphoreTimeout) { // This represents a semaphore. // BQUE *point = (BQUE *)(p->arg_voidptr); if ( point->sem == Notify_Sem && ( point->attr == Notify_Attr || !Notify_Attr)) { Notify_Num_Done++; // Allow the command to run. The priority may have been // PRIORITY_SUSPEND, so we need to change it. // if (isPlayer(point->enactor)) { p->iPriority = PRIORITY_PLAYER; } else { p->iPriority = PRIORITY_OBJECT; } p->ltaWhen.GetUTC(); p->fpTask = Task_RunQueueEntry; return IU_UPDATE_TASK; } } return IU_NEXT_TASK; } // --------------------------------------------------------------------------- // nfy_que: Notify commands from the queue and perform or discard them. int nfy_que(dbref sem, int attr, int key, int count) { int cSemaphore = 1; if (attr) { int aflags; dbref aowner; char *str = atr_get(sem, attr, &aowner, &aflags); cSemaphore = mux_atol(str); free_lbuf(str); } Notify_Num_Done = 0; if (cSemaphore > 0) { Notify_Key = key; Notify_Sem = sem; Notify_Attr = attr; Notify_Num_Max = count; if ( key == NFY_NFY || key == NFY_QUIET) { scheduler.TraverseOrdered(CallBack_NotifySemaphoreFirstOrQuiet); } else { scheduler.TraverseUnordered(CallBack_NotifySemaphoreDrainOrAll); } } // Update the sem waiters count. // if (key == NFY_NFY) { add_to(sem, -count, attr); } else { atr_clr(sem, attr); } return Notify_Num_Done; } // --------------------------------------------------------------------------- // do_notify: Command interface to nfy_que void do_notify ( dbref executor, dbref caller, dbref enactor, int key, int nargs, char *what, char *count ) { char *obj = parse_to(&what, '/', 0); init_match(executor, obj, NOTYPE); match_everything(0); dbref thing = noisy_match_result(); if (!Good_obj(thing)) { return; } if (!Controls(executor, thing) && !Link_ok(thing)) { notify(executor, NOPERM_MESSAGE); } else { int atr = A_SEMAPHORE; if ( what && what[0] != '\0') { int i = mkattr(executor, what); if (0 < i) { atr = i; if (atr != A_SEMAPHORE) { // Do they have permission to set this attribute? // ATTR *ap = (ATTR *)anum_get(atr); if (!bCanSetAttr(executor, thing, ap)) { notify_quiet(executor, NOPERM_MESSAGE); return; } } } } int loccount; if ( count && count[0] != '\0') { loccount = mux_atol(count); } else { loccount = 1; } if (loccount > 0) { nfy_que(thing, atr, key, loccount); if ( (!(Quiet(executor) || Quiet(thing))) && key != NFY_QUIET) { if (key == NFY_DRAIN) { notify_quiet(executor, "Drained."); } else { notify_quiet(executor, "Notified."); } } } } } // --------------------------------------------------------------------------- // setup_que: Set up a queue entry. // static BQUE *setup_que(dbref executor, dbref caller, dbref enactor, char *command, char *args[], int nargs, char *sargs[]) { int a; BQUE *tmp; // Can we run commands at all? // if (Halted(executor)) return NULL; // Make sure executor can afford to do it. // a = mudconf.waitcost; if (mudconf.machinecost && RandomINT32(0, mudconf.machinecost-1) == 0) { a++; } if (!payfor(executor, a)) { notify(Owner(executor), "Not enough money to queue command."); return NULL; } // Wizards and their objs may queue up to db_top+1 cmds. Players are // limited to QUEUE_QUOTA. -mnp // a = QueueMax(Owner(executor)); if (a_Queue(Owner(executor), 1) > a) { notify(Owner(executor), "Run away objects: too many commands queued. Halted."); halt_que(Owner(executor), NOTHING); // Halt also means no command execution allowed. // s_Halted(executor); return NULL; } // We passed all the tests. // // Calculate the length of the save string. // unsigned int tlen = 0; static unsigned int nCommand; static unsigned int nLenEnv[NUM_ENV_VARS]; static unsigned int nLenRegs[MAX_GLOBAL_REGS]; if (command) { nCommand = strlen(command) + 1; tlen = nCommand; } if (nargs > NUM_ENV_VARS) { nargs = NUM_ENV_VARS; } for (a = 0; a < nargs; a++) { if (args[a]) { nLenEnv[a] = strlen(args[a]) + 1; tlen += nLenEnv[a]; } } if (sargs) { for (a = 0; a < MAX_GLOBAL_REGS; a++) { if (sargs[a]) { nLenRegs[a] = strlen(sargs[a]) + 1; tlen += nLenRegs[a]; } } } // Create the qeue entry and load the save string. // tmp = alloc_qentry("setup_que.qblock"); tmp->comm = NULL; char *tptr = tmp->text = (char *)MEMALLOC(tlen); ISOUTOFMEMORY(tptr); if (command) { memcpy(tptr, command, nCommand); tmp->comm = tptr; tptr += nCommand; } for (a = 0; a < nargs; a++) { if (args[a]) { memcpy(tptr, args[a], nLenEnv[a]); tmp->env[a] = tptr; tptr += nLenEnv[a]; } else { tmp->env[a] = NULL; } } for ( ; a < NUM_ENV_VARS; a++) { tmp->env[a] = NULL; } for (a = 0; a < MAX_GLOBAL_REGS; a++) { tmp->scr[a] = NULL; } if (sargs) { for (a = 0; a < MAX_GLOBAL_REGS; a++) { if (sargs[a]) { memcpy(tptr, sargs[a], nLenRegs[a]); tmp->scr[a] = tptr; tptr += nLenRegs[a]; } } } // Load the rest of the queue block. // tmp->executor = executor; tmp->IsTimed = false; tmp->sem = NOTHING; tmp->attr = 0; tmp->enactor = enactor; tmp->caller = caller; tmp->nargs = nargs; return tmp; } // --------------------------------------------------------------------------- // wait_que: Add commands to the wait or semaphore queues. // void wait_que ( dbref executor, dbref caller, dbref enactor, bool bTimed, CLinearTimeAbsolute <aWhen, dbref sem, int attr, char *command, char *args[], int nargs, char *sargs[] ) { if (!(mudconf.control_flags & CF_INTERP)) { return; } BQUE *tmp = setup_que(executor, caller, enactor, command, args, nargs, sargs); if (!tmp) { return; } int iPriority; if (isPlayer(tmp->enactor)) { iPriority = PRIORITY_PLAYER; } else { iPriority = PRIORITY_OBJECT; } tmp->IsTimed = bTimed; tmp->waittime = ltaWhen; tmp->sem = sem; tmp->attr = attr; if (sem == NOTHING) { // Not a semaphore, so let it run it immediately or put it on // the wait queue. // if (tmp->IsTimed) { scheduler.DeferTask(tmp->waittime, iPriority, Task_RunQueueEntry, tmp, 0); } else { scheduler.DeferImmediateTask(iPriority, Task_RunQueueEntry, tmp, 0); } } else { if (!tmp->IsTimed) { // In this case, the timeout task below will never run, // but it allows us to manage all semaphores together in // the same data structure. // iPriority = PRIORITY_SUSPEND; } scheduler.DeferTask(tmp->waittime, iPriority, Task_SemaphoreTimeout, tmp, 0); } } #ifdef QUERY_SLAVE // --------------------------------------------------------------------------- // sql_que: Add commands to the sql queue. // void sql_que ( dbref executor, dbref caller, dbref enactor, bool bTimed, CLinearTimeAbsolute <aWhen, dbref thing, int attr, char *command, char *args[], int nargs, char *sargs[] ) { if (!(mudconf.control_flags & CF_INTERP)) { return; } BQUE *tmp = setup_que(executor, caller, enactor, command, args, nargs, sargs); if (!tmp) { return; } tmp->IsTimed = bTimed; tmp->waittime = ltaWhen; tmp->sem = thing; tmp->attr = attr; int iPriority; if (!tmp->IsTimed) { // In this case, the timeout task below will never run, // but it allows us to manage all semaphores together in // the same data structure. // iPriority = PRIORITY_SUSPEND; } else { iPriority = PRIORITY_OBJECT; } scheduler.DeferTask(tmp->waittime, iPriority, Task_SQLTimeout, tmp, 0); } #endif // QUERY_SLAVE // --------------------------------------------------------------------------- // do_wait: Command interface to wait_que // void do_wait ( dbref executor, dbref caller, dbref enactor, int key, char *event, char *cmd, char *cargs[], int ncargs ) { CLinearTimeAbsolute ltaWhen; CLinearTimeDelta ltd; // If arg1 is all numeric, do simple (non-sem) timed wait. // if (is_rational(event)) { if (key & WAIT_UNTIL) { ltaWhen.SetSecondsString(event); } else { ltaWhen.GetUTC(); ltd.SetSecondsString(event); ltaWhen += ltd; } wait_que(executor, caller, enactor, true, ltaWhen, NOTHING, 0, cmd, cargs, ncargs, mudstate.global_regs); return; } // Semaphore wait with optional timeout. // char *what = parse_to(&event, '/', 0); init_match(executor, what, NOTYPE); match_everything(0); dbref thing = noisy_match_result(); if (!Good_obj(thing)) { return; } else if (!Controls(executor, thing) && !Link_ok(thing)) { notify(executor, NOPERM_MESSAGE); } else { // Get timeout, default 0. // int atr = A_SEMAPHORE; bool bTimed = false; if (event && *event) { if (is_rational(event)) { if (key & WAIT_UNTIL) { ltaWhen.SetSecondsString(event); } else { ltaWhen.GetUTC(); ltd.SetSecondsString(event); ltaWhen += ltd; } bTimed = true; } else { ATTR *ap = atr_str(event); if (!ap) { atr = mkattr(executor, event); if (atr <= 0) { notify_quiet(executor, "Invalid attribute."); return; } ap = atr_num(atr); } else { atr = ap->number; } if (!bCanSetAttr(executor, thing, ap)) { notify_quiet(executor, NOPERM_MESSAGE); return; } } } int num = add_to(thing, 1, atr); if (num <= 0) { // Thing over-notified, run the command immediately. // thing = NOTHING; bTimed = false; } wait_que(executor, caller, enactor, bTimed, ltaWhen, thing, atr, cmd, cargs, ncargs, mudstate.global_regs); } } // --------------------------------------------------------------------------- // do_query: Command interface to sql_que // void do_query ( dbref executor, dbref caller, dbref enactor, int key, char *query, char *cmd, char *cargs[], int ncargs ) { #ifdef QUERY_SLAVE if (key & QUERY_SQL) { // SQL Query. // } else { notify_quiet(executor, "At least one query option is required."); } #else notify_quiet(executor, "@query support is not enabled."); #endif } CLinearTimeAbsolute Show_lsaNow; int Total_SystemTasks; int Total_RunQueueEntry; int Shown_RunQueueEntry; int Total_SemaphoreTimeout; int Shown_SemaphoreTimeout; dbref Show_Player_Target; dbref Show_Object_Target; int Show_Key; dbref Show_Player; int Show_bFirstLine; #ifdef QUERY_SLAVE int Total_SQLTimeout; int Shown_SQLTimeout; #endif // QUERY_SLAVE #ifdef WIN32 extern void Task_FreeDescriptor(void *arg_voidptr, int arg_Integer); extern void Task_DeferredClose(void *arg_voidptr, int arg_Integer); #endif void dispatch_DatabaseDump(void *pUnused, int iUnused); void dispatch_FreeListReconstruction(void *pUnused, int iUnused); void dispatch_IdleCheck(void *pUnused, int iUnused); void dispatch_CheckEvents(void *pUnused, int iUnused); #ifndef MEMORY_BASED void dispatch_CacheTick(void *pUnused, int iUnused); #endif int CallBack_ShowDispatches(PTASK_RECORD p) { Total_SystemTasks++; CLinearTimeDelta ltd = p->ltaWhen - Show_lsaNow; if (p->fpTask == dispatch_DatabaseDump) { notify(Show_Player, tprintf("[%d]auto-@dump", ltd.ReturnSeconds())); } else if (p->fpTask == dispatch_FreeListReconstruction) { notify(Show_Player, tprintf("[%d]auto-@dbck", ltd.ReturnSeconds())); } else if (p->fpTask == dispatch_IdleCheck) { notify(Show_Player, tprintf("[%d]Check for idle players", ltd.ReturnSeconds())); } else if (p->fpTask == dispatch_CheckEvents) { notify(Show_Player, tprintf("[%d]Test for @daily time", ltd.ReturnSeconds())); } #ifndef MEMORY_BASED else if (p->fpTask == dispatch_CacheTick) { notify(Show_Player, tprintf("[%d]Database cache tick", ltd.ReturnSeconds())); } #endif else if (p->fpTask == Task_ProcessCommand) { notify(Show_Player, tprintf("[%d]Further command quota", ltd.ReturnSeconds())); } #ifdef WIN32 else if (p->fpTask == Task_FreeDescriptor) { notify(Show_Player, tprintf("[%d]Delayed descriptor deallocation", ltd.ReturnSeconds())); } else if (p->fpTask == Task_DeferredClose) { notify(Show_Player, tprintf("[%d]Delayed socket close", ltd.ReturnSeconds())); } #endif else { Total_SystemTasks--; } return IU_NEXT_TASK; } void ShowPsLine(BQUE *tmp) { char *bufp = unparse_object(Show_Player, tmp->executor, false); if (tmp->IsTimed && (Good_obj(tmp->sem))) { CLinearTimeDelta ltd = tmp->waittime - Show_lsaNow; notify(Show_Player, tprintf("[#%d/%d]%s:%s", tmp->sem, ltd.ReturnSeconds(), bufp, tmp->comm)); } else if (tmp->IsTimed) { CLinearTimeDelta ltd = tmp->waittime - Show_lsaNow; notify(Show_Player, tprintf("[%d]%s:%s", ltd.ReturnSeconds(), bufp, tmp->comm)); } else if (Good_obj(tmp->sem)) { notify(Show_Player, tprintf("[#%d]%s:%s", tmp->sem, bufp, tmp->comm)); } else { notify(Show_Player, tprintf("%s:%s", bufp, tmp->comm)); } char *bp = bufp; if (Show_Key == PS_LONG) { for (int i = 0; i < tmp->nargs; i++) { if (tmp->env[i] != NULL) { safe_str("; Arg", bufp, &bp); safe_chr((char)(i + '0'), bufp, &bp); safe_str("='", bufp, &bp); safe_str(tmp->env[i], bufp, &bp); safe_chr('\'', bufp, &bp); } } *bp = '\0'; bp = unparse_object(Show_Player, tmp->enactor, false); notify(Show_Player, tprintf(" Enactor: %s%s", bp, bufp)); free_lbuf(bp); } free_lbuf(bufp); } int CallBack_ShowWait(PTASK_RECORD p) { if (p->fpTask != Task_RunQueueEntry) { return IU_NEXT_TASK; } Total_RunQueueEntry++; BQUE *tmp = (BQUE *)(p->arg_voidptr); if (que_want(tmp, Show_Player_Target, Show_Object_Target)) { Shown_RunQueueEntry++; if (Show_Key == PS_SUMM) { return IU_NEXT_TASK; } if (Show_bFirstLine) { notify(Show_Player, "----- Wait Queue -----"); Show_bFirstLine = false; } ShowPsLine(tmp); } return IU_NEXT_TASK; } int CallBack_ShowSemaphore(PTASK_RECORD p) { if (p->fpTask != Task_SemaphoreTimeout) { return IU_NEXT_TASK; } Total_SemaphoreTimeout++; BQUE *tmp = (BQUE *)(p->arg_voidptr); if (que_want(tmp, Show_Player_Target, Show_Object_Target)) { Shown_SemaphoreTimeout++; if (Show_Key == PS_SUMM) { return IU_NEXT_TASK; } if (Show_bFirstLine) { notify(Show_Player, "----- Semaphore Queue -----"); Show_bFirstLine = false; } ShowPsLine(tmp); } return IU_NEXT_TASK; } #ifdef QUERY_SLAVE int CallBack_ShowSQLQueries(PTASK_RECORD p) { if (p->fpTask != Task_SQLTimeout) { return IU_NEXT_TASK; } Total_SQLTimeout++; BQUE *tmp = (BQUE *)(p->arg_voidptr); if (que_want(tmp, Show_Player_Target, Show_Object_Target)) { Shown_SQLTimeout++; if (Show_Key == PS_SUMM) { return IU_NEXT_TASK; } if (Show_bFirstLine) { notify(Show_Player, "----- SQL Queries -----"); Show_bFirstLine = false; } ShowPsLine(tmp); } return IU_NEXT_TASK; } #endif // --------------------------------------------------------------------------- // do_ps: tell executor what commands they have pending in the queue // void do_ps(dbref executor, dbref caller, dbref enactor, int key, char *target) { char *bufp; dbref executor_targ, obj_targ; // Figure out what to list the queue for. // if ((key & PS_ALL) && !See_Queue(executor)) { notify(executor, NOPERM_MESSAGE); return; } if (!target || !*target) { obj_targ = NOTHING; if (key & PS_ALL) { executor_targ = NOTHING; } else { executor_targ = Owner(executor); if (!isPlayer(executor)) { obj_targ = executor; } } } else { executor_targ = Owner(executor); obj_targ = match_controlled(executor, target); if (obj_targ == NOTHING) { return; } if (key & PS_ALL) { notify(executor, "Can't specify a target and /all"); return; } if (isPlayer(obj_targ)) { executor_targ = obj_targ; obj_targ = NOTHING; } } key = key & ~PS_ALL; switch (key) { case PS_BRIEF: case PS_SUMM: case PS_LONG: break; default: notify(executor, "Illegal combination of switches."); return; } Show_lsaNow.GetUTC(); Total_SystemTasks = 0; Total_RunQueueEntry = 0; Shown_RunQueueEntry = 0; Total_SemaphoreTimeout = 0; Shown_SemaphoreTimeout = 0; Show_Player_Target = executor_targ; Show_Object_Target = obj_targ; Show_Key = key; Show_Player = executor; Show_bFirstLine = true; scheduler.TraverseOrdered(CallBack_ShowWait); Show_bFirstLine = true; scheduler.TraverseOrdered(CallBack_ShowSemaphore); #ifdef QUERY_SLAVE Show_bFirstLine = true; scheduler.TraverseOrdered(CallBack_ShowSQLQueries); #endif // QUERY_SLAVE if (Wizard(executor)) { notify(executor, "----- System Queue -----"); scheduler.TraverseOrdered(CallBack_ShowDispatches); } // Display stats. // bufp = alloc_mbuf("do_ps"); #ifdef QUERY_SLAVE sprintf(bufp, "Totals: Wait Queue...%d/%d Semaphores...%d/%d SQL %d/%d", Shown_RunQueueEntry, Total_RunQueueEntry, Shown_SemaphoreTimeout, Total_SemaphoreTimeout, Shown_SQLTimeout, Total_SQLTimeout); #else sprintf(bufp, "Totals: Wait Queue...%d/%d Semaphores...%d/%d", Shown_RunQueueEntry, Total_RunQueueEntry, Shown_SemaphoreTimeout, Total_SemaphoreTimeout); #endif // QUERY_SLAVE notify(executor, bufp); if (Wizard(executor)) { sprintf(bufp, " System Tasks.....%d", Total_SystemTasks); notify(executor, bufp); } free_mbuf(bufp); } CLinearTimeDelta ltdWarp; int CallBack_Warp(PTASK_RECORD p) { if ( p->fpTask == Task_RunQueueEntry #ifdef QUERY_SLAVE || p->fpTask == Task_SQLTimeout #endif // QUERY_SLAVE || p->fpTask == Task_SemaphoreTimeout) { BQUE *point = (BQUE *)(p->arg_voidptr); if (point->IsTimed) { point->waittime -= ltdWarp; return IU_UPDATE_TASK; } } return IU_NEXT_TASK; } // --------------------------------------------------------------------------- // do_queue: Queue management // void do_queue(dbref executor, dbref caller, dbref enactor, int key, char *arg) { if (key == QUEUE_KICK) { int i = mux_atol(arg); int save_minPriority = scheduler.GetMinPriority(); if (save_minPriority <= PRIORITY_CF_DEQUEUE_DISABLED) { notify(executor, "Warning: automatic dequeueing is disabled."); scheduler.SetMinPriority(PRIORITY_CF_DEQUEUE_ENABLED); } CLinearTimeAbsolute lsaNow; lsaNow.GetUTC(); scheduler.ReadyTasks(lsaNow); int ncmds = scheduler.RunTasks(i); scheduler.SetMinPriority(save_minPriority); if (!Quiet(executor)) { notify(executor, tprintf("%d commands processed.", ncmds)); } } else if (key == QUEUE_WARP) { int iWarp = mux_atol(arg); CLinearTimeDelta ltdWarp; ltdWarp.SetSeconds(iWarp); if (scheduler.GetMinPriority() <= PRIORITY_CF_DEQUEUE_DISABLED) { notify(executor, "Warning: automatic dequeueing is disabled."); } scheduler.TraverseUnordered(CallBack_Warp); if (Quiet(executor)) { return; } if (iWarp > 0) { notify(executor, tprintf("WaitQ timer advanced %d seconds.", iWarp)); } else if (iWarp < 0) { notify(executor, tprintf("WaitQ timer set back %d seconds.", iWarp)); } else { notify(executor, "Object queue appended to player queue."); } } }