/*--------------------------------------------------------------------------- * Wizard List * *--------------------------------------------------------------------------- * The Wizard List ("wizlist") started out as a means to determine a high * score list of the most popular castles. Over the time, this list * was generalized into a simple user-id management with driver-internal * purposes. * * A user-id ("wizard name", "creator") is a unique string, and every object * is associated with a uid through the master-apply get_wiz_name(object-name). * The reverse doesn't hold true: the mudlib can use uids which are not * associated with any object. * * A special uid is the number 0: this is denotes the 'default' wizard, * ie. the system itself. Together with the .extra information feature, * the wizlist entry for uid 0 can be used to store information "on driver * level". * * Every uid represented with a wizlist entry of the following structure. * Object structures even contain a '.user' entry pointing directly * to the associated wizlist entry. * * struct wiz_list_s { * wiz_list_t *next; * string_t *name; * int32 score; * int32 cost; * int32 gigacost; * int32 total_cost; * int32 total_gigacost; * int32 heart_beats; * mp_int size_array; * mp_int mapping_total; #ifdef USE_STRUCTS * mp_int struct_total; #endif * svalue_t extra; * int32 last_call_out; * int32 call_out_cost; * string_t *file_name; * string_t *error_message; * int32 line_number; * }; * * .name is the tabled uid string of this wizlist entry. * .next is the pointer to the next entry in the wizlist. * * .score, .(total_)cost+.(total_)gigacost, .heart_beats, .size_array and * .mapping_total collect statistics about the objects for this uid/wizard. * .score is the number of action functions executed (weighted over time), * .cost+.gigacost the eval ticks spent (weighted over time), * .total_cost+.total_gigacost the eval ticks spent (total), .heart_beats the * number of heart_beat() calls (weighted over time). .size_array and * .mapping_total give the number of values held in arrays and mappings for * this wizard. * * .extra offer space for one svalue which can be used by the mudlib * for its own purposes. The driver can be instructed to fill the .extra * member every newly created entry with an empty array of a specific * size. Especially the .extra member for uid 0 can be used to store * data persistent to the whole driver run - it even survives reloads * of the master object. * * .last_call_out and .call_out_cost are used to manage the call_out. * .call_out_cost is the collected execution cost of all call_outs * executed for time .last_call_out for this .user. When executing * call_outs, this value is used to prime the eval_cost counter, and * thus prevents call_outs from hogging all the cpu. * * .file_name, .error_message and .line_number are used to communicate * errors from the driver to the mudlib. Bit 30 (0x40000000) in line_number * is the "forget flag": it is set when the mudlib queries the error * information. This way the mudlib can distinguish old from new * errors. * * * Part of the wizard list information is stored in the toplevel file * "WIZLIST". The file is organized in lines, one for every wizard. * Each line must at least contain: * * * * No leading space, and and must be separated by one or * more spaces. If existing, this information is read by the driver at * startup and added(!) to the already existing wizlist entries. * * The mudlib is free to add more information after the and read * it itself. For the same reason it is the task of the mudlib to write * the WIZLIST file. *--------------------------------------------------------------------------- */ #include "driver.h" #include "typedefs.h" #include "my-alloca.h" #include #include "wiz_list.h" #include "../mudlib/sys/wizlist.h" #include "array.h" #include "backend.h" #include "gcollect.h" #include "interpret.h" #include "main.h" #include "mapping.h" #include "mstrings.h" #include "object.h" #include "simulate.h" #include "stdstrings.h" #include "svalue.h" #include "xalloc.h" /*-------------------------------------------------------------------------*/ wiz_list_t default_wizlist_entry /* The default wizlist entry which is used for all "system" purposes. */ = { NULL /* next */ , NULL /* name */ , 0 /* score */ , 0 /* cost */ , 0 /* gigacost */ , 0 /* total_cost */ , 0 /* total_gigacost */ , 0 /* heart_beats */ , 0 /* size_array */ , 0 /* mapping_total */ #ifdef USE_STRUCTS , 0 /* struct_total */ #endif , { T_NUMBER } /* extra */ , 0 /* last_call_out */ , 0 /* call_out_cost */ , NULL /* error file_name */ , NULL /* error_message */ , 0 /* error line_number */ }; wiz_list_t *all_wiz = NULL; /* The list of all wizard entries, sorted by the numeric value * of . If the names are queried equally often, this even yields * an average O(1) complexity. */ static int wiz_info_extra_size = -1; /* Default size of the .extra field, -1 if unspecified. */ static int number_of_wiz = 0; /* Number of entries in the list. */ char wizlist_name[MAXPATHLEN+1] = ""; /* Name of the wizlist file, relative to the mudlib directory. */ /*-------------------------------------------------------------------------*/ void name_wizlist_file (const char *name) /* Set the swap file name to a copy of . */ { /* Skip leading '/' */ while (*name == '/') name++; xstrncpy(wizlist_name, name, sizeof wizlist_name); wizlist_name[sizeof wizlist_name - 1] = '\0'; } /* name_wizlist_file()*/ /*-------------------------------------------------------------------------*/ size_t wiz_list_size (void) /* Return the allocated size of the wiz_list. */ { /* The extra wizinfo has been counted with the arrays */ return sizeof(wiz_list_t) * number_of_wiz; } /* wiz_list_size() */ /*-------------------------------------------------------------------------*/ static wiz_list_t * find_wiz (string_t *name) /* Find the entry for the user 'name' and return it's pointer. * Return NULL if it can't be found. */ { wiz_list_t *wl; if ( !( name = find_tabled(name) ) ) return NULL; for (wl = all_wiz; wl; wl = wl->next) { int rc = mstrcmp(wl->name, name); if (!rc) return wl; if (rc > 0) break; } return NULL; } /* find_wiz() */ /*-------------------------------------------------------------------------*/ wiz_list_t * add_name (string_t * str) /* Check if an entry for wizard exists; add it, if it doesn't. * Return the pointer to the wiz_list entry. * Must not change refcount of . */ { wiz_list_t *wl; wiz_list_t *prev, *this; wl = find_wiz(str); if (wl) return wl; number_of_wiz++; wl = xalloc(sizeof (wiz_list_t)); str = make_tabled_from(str); wl->next = NULL; wl->name = str; wl->score = 0; wl->cost = 0; wl->gigacost = 0; wl->total_cost = 0; wl->total_gigacost = 0; wl->heart_beats = 0; wl->size_array = 0; wl->mapping_total = 0; #ifdef USE_STRUCTS wl->struct_total = 0; #endif /* USE_STRUCTS */ #if 0 wl->quota_allowance = 0; wl->quota_usage = 0; #endif wl->file_name = NULL; wl->error_message = NULL; if (wiz_info_extra_size >= 0) put_array(&(wl->extra), allocate_array(wiz_info_extra_size)); else wl->extra = const0; wl->last_call_out = 0; /* Find the insertion point and insert the new entry */ for ( prev = NULL, this = all_wiz ; this && mstrcmp(this->name, str) < 0 ; prev = this, this = this->next ) { NOOP; } if (!prev) { wl->next = all_wiz; all_wiz = wl; } else { wl->next = this; prev->next = wl; } return wl; } /* add_name() */ /*-------------------------------------------------------------------------*/ void wiz_decay (void) /* Called after every complete walkaround of the reset, this 'decays' * the score of every wizard once per hour. * Together with the decay, the wizlist is checked for destructed extra * data. */ { wiz_list_t *wl; static int next_time; /* Only once per hour */ if (next_time > current_time) return; next_time = current_time + 60 * 60; for (wl = all_wiz; wl; wl = wl->next) { wl->score = wl->score * 99 / 100; wl->cost = wl->cost * .9; wl->gigacost = wl->gigacost * .9; wl->heart_beats = wl->heart_beats * 9 / 10; } check_wizlist_for_destr(); } /* wiz_decay() */ /*-------------------------------------------------------------------------*/ static string_t * get_wiz_name (const char *file) /* For the filename return the name of the wizard responsible * for it. This actual query is done with a master apply. * * Result is NULL or an uncounted string reference. */ { svalue_t *ret; /* Don't call the master if it isn't loaded! */ if (!master_ob) return NULL; push_c_string(inter_sp, file); ret = apply_master(STR_GET_WNAME, 1); if (ret == 0 || ret->type != T_STRING) return NULL; return ret->u.str; } /* get_wiz_name() */ /*-------------------------------------------------------------------------*/ void load_wiz_file (void) /* Load the old wizlist from the wizlist file and add it's data to * the wizlist already in memory. * * This function is called at driver start up. * TODO: Since the wizlist is saved from the mudlib, this function * TODO:: should be implemented on mudlib level, too. */ { char buff[1000]; FILE *f; if (wizlist_name[0] == '\0') return; f = fopen(wizlist_name, "r"); if (f == NULL) return; while (fgets(buff, sizeof buff, f) != NULL) { char *p; uint32 score; p = strchr(buff, ' '); if (p == 0) { fprintf(stderr, "%s Bad WIZLIST file '%s'.\n" , time_stamp(), wizlist_name); break; } *p = '\0'; p++; if (*p == '\0') { fprintf(stderr, "%s Bad WIZLIST file '%s'.\n" , time_stamp(), wizlist_name); break; } score = atoi(p); if (score > 0) { string_t * tmp; tmp = new_mstring(buff); add_name(tmp)->score += score; free_mstring(tmp); } } fclose(f); } /* load_wiz_file() */ /*-------------------------------------------------------------------------*/ void remove_wiz_list (void) /* Remove all memory allocated by the wizlist. * * Called from simulate::shutdowngame(). */ { wiz_list_t *wl, *w; for (w = all_wiz; w; w = wl) { free_mstring(w->name); if (w->file_name) free_mstring(w->file_name); if (w->error_message) free_mstring(w->error_message); wl = w->next; xfree(w); } } /* remove_wiz_list() */ /*-------------------------------------------------------------------------*/ void save_error (const char *msg, const char *file, int line) /* A runtime error occured for object in line number . * Store this information in the wizlist so that the mudlib can handle * it later with the efun get_error_file(). * TODO: A proper runtime error handling could put this into the mudlib * TODO:: completely. */ { wiz_list_t *wl; char *copy, *p; string_t *name; size_t len; /* Get the wizard name and the wizlist entry. */ name = get_wiz_name(file); if (!name) return; wl = add_name(name); /* Set the file_name */ if (wl->file_name) free_mstring(wl->file_name); len = strlen(file); copy = alloca(len + 4); /* May add .c plus the null byte, and / */ *copy = '/'; strcpy(copy+1, file); /* If it is a cloned object, we have to find out what the file * name is, and add '.c'. */ if ( NULL != (p = strrchr(copy, '#')) || ((p = copy+len), *p++ != 'c') || p[-2] != '.' ) { p[0] = '.'; p[1] = 'c'; p[2] = '\0'; } wl->file_name = new_mstring(copy); /* Set the error_message */ if (wl->error_message) free_mstring(wl->error_message); wl->error_message = new_mstring(msg); /* Set the line_number */ wl->line_number = line; } /* save_error() */ /*=========================================================================*/ /* EFUNS */ /*-------------------------------------------------------------------------*/ svalue_t * f_wizlist_info (svalue_t *sp) /* EFUN wizlist_info() * * mixed *wizlist_info() * * Returns an array with the interesting entries of the wizlist. * Raises a privilege_violation (wizlist_info, this_object(), 0). * * The result is an array with one entry for every wizard (uid). * Every entry is an array itself: * * string w[WL_NAME] = Name of the wizard. * int w[WL_COMMANDS] = Weighted number of commands execute by objects * of this wizard. * int w[WL_COST] and w[WL_GIGACOST] = Weighted sum of eval_costs. * int w[WL_TOTAL_COST] and w[WL_TOTAL_GIGACOST] = Total sum of * eval_costs. * int w[WL_HEART_BEATS] = Weighted count of heart_beats. * int w[WL_CALL_OUT] = Reserved for call_out() (unused yet). * int w[WL_ARRAY_TOTAL] = Total size of arrays in elements. * int w[WL_MAPPING_TOTAL] = Total size of mappings in elements. #ifdef USE_STRUCTS * int w[WL_STRUCT_TOTAL] = Total size of mappings in elements. #endif * mixed w[WL_EXTRA] = Extra wizlist-info if set. */ { vector_t *all, *entry; svalue_t *wsvp, *svp; wiz_list_t *w; if (!privilege_violation(STR_WIZLIST_INFO, &const0, sp)) { all = allocate_array(0); } else { all = allocate_array(number_of_wiz); wsvp = all->item; for (w = all_wiz; w; w = w->next) { entry = allocate_array(WL_SIZE); put_array(wsvp, entry); wsvp++; svp = entry->item; put_ref_string(&(svp[WL_NAME]), w->name); put_number(&(svp[WL_COMMANDS]), w->score); put_number(&(svp[WL_COST]), w->cost); put_number(&(svp[WL_GIGACOST]), w->gigacost); put_number(&(svp[WL_TOTAL_COST]), w->total_cost); put_number(&(svp[WL_TOTAL_GIGACOST]), w->total_gigacost); put_number(&(svp[WL_HEART_BEATS]), w->heart_beats); put_number(&(svp[WL_CALL_OUT]), 0); /* TODO: Implement me */ put_number(&(svp[WL_ARRAY_TOTAL]), w->size_array); put_number(&(svp[WL_MAPPING_TOTAL]), w->mapping_total); #ifdef USE_STRUCTS put_number(&(svp[WL_STRUCT_TOTAL]), w->struct_total); #else put_number(&(svp[WL_STRUCT_TOTAL]), 0); #endif /* USE_STRUCTS */ if (w->extra.type == T_POINTER) { vector_t *v = w->extra.u.vec; put_array(&(svp[WL_EXTRA]), slice_array(v, 0, VEC_SIZE(v) - 1)); } else assign_svalue_no_free(&(svp[WL_EXTRA]), &w->extra); } /* end for */ } /* end if */ push_array(sp, all); return sp; } /* f_wizlist_info() */ /*-------------------------------------------------------------------------*/ svalue_t * f_set_extra_wizinfo (svalue_t *sp) /* EFUN set_extra_wizinfo() * * void set_extra_wizinfo (object wiz, mixed extra) * void set_extra_wizinfo (string wiz, mixed extra) * void set_extra_wizinfo (int wiz, mixed extra) * * Set the value as the 'extra' information for the wizlist * entry of . * * If is an object, the entry of its creator (uid) is used. * If is a string (a creator aka uid), it names the entry * to use. * If is the number 0, the data is set in the default wizlist * entry. It can be used to store data for the lifetime of this * driver run, like the time of the last reboot. * * can be any value. * * The function causes a privilege violation * ("set_extra_wizinfo", this_object(), ). */ { wiz_list_t *user; short type; if ((type = sp[-1].type) == T_OBJECT) { user = sp[-1].u.ob->user; } else if (type != T_STRING || !(user = find_wiz(sp[-1].u.str))) { if (type == T_NUMBER && sp[-1].u.number == 0) user = NULL; else efun_gen_arg_error(1, sp->type, sp); } if (!privilege_violation(STR_SET_EXTRA_WIZINFO, sp-1, sp)) free_svalue(sp); else transfer_svalue(user ? &user->extra : &default_wizlist_entry.extra, sp); free_svalue(sp-1); return sp - 2; } /* f_set_extra_wizinfo() */ /*-------------------------------------------------------------------------*/ svalue_t * f_get_extra_wizinfo (svalue_t *sp) /* EFUN get_extra_wizinfo() * * mixed get_extra_wizinfo (object wiz) * mixed get_extra_wizinfo (string wiz) * mixed get_extra_wizinfo (int wiz) * * Returns the 'extra' information that was set for the given * wizard in the wizlist. * * If is an object, the entry of its creator (uid) is used. * If is a string (a creator aka uid), it names the entry * to use. * If is the number 0, the data is get from the default wizlist * entry. * * The function causes a privilege violation * ("get_extra_wizinfo", this_object(), ). */ { wiz_list_t *user; short type; if ((type = sp->type) == T_OBJECT) { user = sp->u.ob->user; } else if (type != T_STRING || !(user = find_wiz(sp->u.str))) { if (type == T_NUMBER && sp->u.number == 0) user = NULL; else errorf("Bad arg 1 to get_extra_wizinfo(): no valid uid given.\n"); } if (!privilege_violation(STR_GET_EXTRA_WIZINFO, sp, sp)) errorf("Error in get_extra_wizinfo(): privilege violation.\n"); assign_svalue(sp, user ? &user->extra : &default_wizlist_entry.extra); return sp; } /* get_extra_wizlist_info() */ /*-------------------------------------------------------------------------*/ svalue_t * f_set_extra_wizinfo_size (svalue_t *sp) /* EFUN set_extra_wizinfo_size() * * void set_extra_wizinfo_size(int size) * * Indicate that the wizlist should contain an array of this size * with extra info foreach wizard. A negative size is used to * indicate non-array 'extra' information. * * Causes the privilege violation * ("set_extra_wizinfo_size", this_object(), size). * * The value is only used to allocate a proper empty 'extra' value * for newly created wizlist entries. * * TODO: The extra_wizinfo idea could be applied to single objects * TODO:: and - ta da! - we have driver supported properties. * TODO:: Which then could be used to implement uids/euids etc. */ { if (!privilege_violation(STR_SET_EXTRA_WIZINFO_SIZE, &const0, sp)) wiz_info_extra_size = sp->u.number; sp--; return sp; } /* f_set_extra_wizinfo_size() */ /*-------------------------------------------------------------------------*/ svalue_t * f_get_error_file (svalue_t *sp) /* EFUN get_error_file() * * mixed * get_error_file(string name, int set_forget_flag) * * Return information about the last error which occured for * (where is a valid name from the wiz list). * * Result is an array of four elements: the filename of the * program where the error occured, the linenumber in the * program, the error message (runtime error messages usually * start with a '*'), and a numerical flag (the 'forget flag') if * the error information has been queried already. * * If there is no error stored for the given , 0 is * returned. * * If is non-zero, the 'forget' flag is set * for the error message after it has been returned. */ { string_t *name; int forget; wiz_list_t *wl; vector_t *vec; svalue_t *v; # define FORGET_FLAG 0x4000000 /* 0x80...0 would be the sign! */ /* Get the function arguments */ name = sp[-1].u.str; forget = sp->u.number; wl = find_wiz(name); sp--; free_string_svalue(sp); /* The error_message is used as a flag if there has been any error. */ if (!wl || !wl->error_message) { put_number(sp, 0); return sp; } vec = allocate_array(4); v = vec->item; put_ref_string(v, wl->file_name); put_number(v+1, wl->line_number & ~0x40000000); put_ref_string(v+2, wl->error_message); put_number(v+3, (wl->line_number & 0x40000000) != 0); if (forget) wl->line_number |= 0x40000000; put_array(sp, vec); return sp; # undef FORGET_FLAG } /* f_get_error_file() */ /*=========================================================================*/ /*-------------------------------------------------------------------------*/ void check_wizlist_for_destr (void) /* Check the 'extra' info in all wizinfo and remove destructed objects * and closures. */ { wiz_list_t *wl; for (wl = &default_wizlist_entry; wl; ) { size_t num; svalue_t *item; if (wl->extra.type == T_POINTER) { num = VEC_SIZE(wl->extra.u.vec); item = &(wl->extra.u.vec->item[0]); } else { num = 1; item = &(wl->extra); } for ( ; num != 0 ; item++, num--) { switch(item->type) { case T_POINTER: check_for_destr(item->u.vec); break; case T_MAPPING: check_map_for_destr(item->u.map); break; case T_OBJECT: case T_CLOSURE: if (destructed_object_ref(item)) assign_svalue(item, &const0); break; default: NOOP; break; } } if (wl == &default_wizlist_entry) wl = all_wiz; else wl = wl->next; } } /* check_wizlist_for_destr() */ /*-------------------------------------------------------------------------*/ #ifdef GC_SUPPORT void clear_ref_from_wiz_list (void) /* GC support: Clear the refs for the wiz_list memory. */ { wiz_list_t *w; for (w = all_wiz; w; w = w->next) { clear_ref_in_vector(&w->extra, 1); if(w->file_name) clear_string_ref(w->file_name); if(w->error_message) clear_string_ref(w->error_message); } clear_ref_in_vector(&default_wizlist_entry.extra, 1); } /* clear_ref_from_wiz_list() */ /*-------------------------------------------------------------------------*/ void count_ref_from_wiz_list (void) /* GC support: Count the refs for the wiz_list memory. */ { wiz_list_t *w; for (w = all_wiz; w; w = w->next) { count_ref_from_string(w->name); count_ref_in_vector(&w->extra, 1); if(w->file_name) count_ref_from_string(w->file_name); if (w->error_message) count_ref_from_string(w->error_message); note_malloced_block_ref((char *)w); } count_ref_in_vector(&default_wizlist_entry.extra, 1); } /* count_ref_from_wiz_list() */ #endif /* GC_SUPPORT */ /*-------------------------------------------------------------------------*/ #ifdef USE_PARANOIA void count_extra_ref_from_wiz_list (void) /* DEBUG support: Count the extra refs for the wiz_list memory. */ { wiz_list_t *w; for (w = all_wiz; w; w = w->next) { count_extra_ref_in_vector(&w->extra, 1); } count_extra_ref_in_vector(&default_wizlist_entry.extra, 1); } /* count_extra_ref_from_wiz_list() */ #endif /***************************************************************************/