Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

unigremlin.cc

Go to the documentation of this file.
00001 #include <ctime>
00002 #include <stdio.h>
00003 #include <regex.h>
00004 #include <ctype.h>
00005 #include "unigremlin.h"
00006 
00007 #define MAX_DEPTH       5
00008 #define TYPE_STRING     0
00009 #define TYPE_IP         1
00010 #define TYPE_IP_NETWORK 2
00011 #define TYPE_HOSTNAME   3
00012 #define TYPE_BOOL       4
00013 #define TYPE_INT        5
00014 #define TYPE_FLOAT      6
00015 #define NUM_TYPES       7
00016 
00017 VictimDict UniConfGremlin::victims(500);
00018 
00019 UniConfGremlin::UniConfGremlin(WvString moniker, const UniConfKey _key,
00020         int _max_runlevel)
00021     : cfg(moniker), key(_key), max_runlevel(_max_runlevel)
00022 {
00023     runlevel = 1;
00024     num_victims = 0;
00025     if (max_runlevel > 5 || max_runlevel < 1)
00026         max_runlevel = 4;
00027 }
00028 
00029 /* start(unsigned int seed)
00030  * Starts the gremlin out using the specified seed to seed the random numbers,
00031  * or if no seed is specified, uses the current time to seed them.
00032  */
00033 void UniConfGremlin::start(unsigned int seed)
00034 {
00035     if (seed == 0)
00036         seed = time(0);
00037     
00038     srand(seed);
00039     printf("Using seed %i\n", seed);
00040     find_victims(key);
00041     for (runlevel = 1; runlevel <= max_runlevel; runlevel ++)
00042         start_trouble(runlevel);
00043     runlevel = max_runlevel - 1;
00044 }
00045 
00046 /* find_victims(UniConfKey _key)
00047  * Iterates through each element in the tree and adds a new victim to the list
00048  * victims with the element's key and a guess at what type of data should be
00049  * stored there.
00050  */
00051 void UniConfGremlin::find_victims(UniConfKey _key)
00052 {
00053     UniConf::Iter i(cfg[_key]);
00054     Victim *victim;
00055     for (i.rewind(); i.next(); )
00056     {   
00057         if (i().get() == "")
00058             find_victims(i().fullkey());
00059         else
00060         {
00061             victim = new Victim(num_victims, inspect(i().get()), i().fullkey());
00062             victims.add(victim, true);
00063             num_victims ++;
00064         }
00065     }
00066 }
00067 
00068 /* inspect(WvString element)
00069  * Inspects the string element in an attempt to guess what type of data should
00070  * be stored there.  Returns an integer representing what type the gremlin
00071  * thinks it is.
00072  */
00073 int UniConfGremlin::inspect(WvString element)
00074 {
00075     char *elem = element.edit();
00076     // rough expression for an ip network
00077     char *ipNetExp = "[0-9].*\\.[0-9].*\\.[0-9].*\\.[0-9].*";
00078     if (is_bool(elem))
00079         return TYPE_BOOL;
00080     
00081     if (isdigit(elem[0]))
00082     {
00083         regex_t preg1;
00084         regmatch_t pmatch1[1];
00085         regcomp(&preg1, ipNetExp, REG_EXTENDED | REG_NOSUB);
00086         
00087         if (!regexec(&preg1, elem, 1, pmatch1, 0))
00088         {
00089             if (elem[element.len() - 2] == '/' || 
00090                 elem[element.len() - 3] == '/')
00091                 return TYPE_IP_NETWORK;
00092             else
00093                 return TYPE_IP;
00094         }
00095     }
00096     // now we just brute force the rest of the string
00097     for (size_t i = 1; i < element.len(); i++)
00098     {
00099         if (!isdigit(elem[i]))
00100         // either string or hostname, return string for now
00101             return TYPE_STRING;
00102     }
00103     // either int or float, return int for now
00104     return TYPE_INT;
00105 }
00106 
00107 /* is_bool(char *elem)
00108  * Returns true if the string stored in elem represents a boolean valuation
00109  */
00110 bool UniConfGremlin::is_bool(char *elem)
00111 {
00112     const char *strs[] = {
00113         "true", "yes", "on", "enabled", "1",
00114         "false", "no", "off", "disabled", "0"
00115     };
00116     
00117     for (size_t i = 0; i < sizeof(strs) / sizeof(const char*); ++i)
00118         if (strcasecmp(elem, strs[i]) == 0)
00119             return true;
00120     
00121     return false;
00122 }
00123 
00124 /* change_value(bool use_valid_data)
00125  * Changes a random value from the list of victims, the victim list includes
00126  * previously deleted victims as well, so this could potentially add previously
00127  * deleted values back in as well.  Changes it with new random data.
00128  */
00129 void UniConfGremlin::change_value(bool use_valid_data)
00130 {
00131     int r = num_victims + 1;
00132     while (r >= num_victims)
00133     {
00134         r = (int)(((double)rand() / (double)RAND_MAX) * (num_victims));
00135     }
00136     last_change = WvString("Changed %s to be the value", victims[r]->name);
00137     WvString new_value;
00138     if (use_valid_data)
00139     {
00140         if (victims[r]->type == TYPE_INT)
00141         {
00142             int new_value = rand();
00143             cfg[victims[r]->name].setint(new_value);
00144             last_change = WvString("%s %s\n", last_change, new_value);
00145         }
00146         else 
00147         {
00148             WvString new_value = rand_str(victims[r]->type);
00149             cfg[victims[r]->name].set(new_value);
00150             last_change = WvString("%s %s\n", last_change, new_value);
00151         }
00152     }
00153     else
00154     {
00155         int s = (int)(((double)rand() / (double)RAND_MAX) * 2);
00156         if (s)
00157         {
00158             int new_value = rand();
00159             cfg[victims[r]->name].setint(rand());
00160             last_change = WvString("%s %s\n", last_change, new_value);
00161         }
00162         else
00163         {
00164             WvString new_value = rand_str(victims[r]->type);
00165             int t = (int)(((double)rand() / (double)RAND_MAX) * NUM_TYPES);
00166             cfg[victims[r]->name].set(rand_str(t));
00167             last_change = WvString("%s %s\n", last_change, new_value);
00168         }
00169             
00170     }
00171     cfg[victims[r]->name].commit();
00172             
00173 }
00174 
00175 /* add_value()
00176  * Adds a random value into the UniConf also randomly selecting the type and 
00177  * contents.
00178  */
00179 void UniConfGremlin::add_value()
00180 {
00181     int depth = (int)(((double)rand() / (double)RAND_MAX) * MAX_DEPTH);
00182     WvString key_name = "", elem;
00183     
00184     // generate a key name 32 characters or less
00185     for (int i = 0; i < depth; i++)
00186     {
00187         elem = "";
00188         int length = (int)(((double)rand() / (double)RAND_MAX) * 8);
00189         for (int j = 0; j < length; j++)
00190         {
00191             // generate a valid written character (lower case right now)
00192             int num = (int)((((double)rand() / (double)RAND_MAX) * 25)
00193                      + 97);
00194             char c[2];
00195             c[0] = (char)num;
00196             c[1] = '\0';
00197             elem = WvString("%s%s", elem, c);
00198         }
00199         if (i > 0)
00200             key_name = WvString("%s/%s", key_name, elem);
00201         else
00202             key_name = elem;
00203     }
00204     key_name = WvString("%s/%s", key, key_name);
00205     
00206     last_change = WvString("Added the key %s with the new value", key_name);
00207     // generate a value
00208     int num = (int)((double)rand() / (double)RAND_MAX);
00209     Victim *victim;
00210     if (num)
00211     // generate number
00212     {
00213         int new_value = rand();
00214         cfg[key_name].setint(new_value);
00215         victim = new Victim(num_victims, TYPE_INT, key_name);
00216         last_change = WvString("%s %s\n", last_change, new_value);
00217     }
00218     else
00219     // generate string
00220     {
00221         int type = (int)(((double)rand() / (double)RAND_MAX) * 5 
00222                    + TYPE_STRING);
00223         WvString new_value = rand_str(type);
00224         cfg[key_name].set(new_value);
00225         victim = new Victim(num_victims, TYPE_STRING, key_name);
00226         last_change = WvString("%s %s\n", last_change, new_value);
00227     }
00228     victims.add(victim, true);
00229     num_victims ++;
00230     cfg[key_name].commit();
00231 }
00232 
00233 /* start_trouble(int curr_runlevel)
00234  * This is where it makes the calls for 1000 actions on the specified runlevel
00235  * and calls the appropriate method.
00236  */
00237 void UniConfGremlin::start_trouble(int curr_runlevel)
00238 {
00239     printf("%s\n", UniConfGremlin::curr_runlevel().cstr());
00240     int r = curr_runlevel;
00241     for (int i = 0; i < 1000; i ++)
00242     {
00243         if (curr_runlevel == 5)
00244             r = (int)(((double)rand() / (double)RAND_MAX) * 4) + 1;
00245         
00246         if (r == 1)
00247             change_value(true);
00248         else if (r == 2)
00249             change_value(false);
00250         else if (r == 3)
00251             add_value();
00252         else if (r >= 4)
00253         // randomly delete value, but always remember as a victim for fun
00254         {
00255             int r = (int)(((double)rand() / (double)RAND_MAX) * (num_victims));
00256             cfg[victims[r]->name].remove();
00257             cfg[victims[r]->name].commit();
00258             last_change = WvString("Deleted the key %s\n", victims[r]->name);
00259         }
00260     }
00261 }
00262 
00263 /* rand_str(int type)
00264  * Returns a random string of the specified format(by type)
00265  * - not great random numbers, had to increase the range to 1 above the max 
00266  *  range I wanted, just since when casting back to int it chops off the 
00267  *  fractional part, and there is very low probability of getting 
00268  *  rand() = RAND_MAX but it will happen from time to time.
00269  */
00270 WvString UniConfGremlin::rand_str(int type)
00271 {
00272     if (type == TYPE_STRING || type == TYPE_HOSTNAME)
00273     {
00274         int a = (int)(((double)rand() / (double)RAND_MAX) * 200);
00275         WvString result = "";
00276         for (int i = 0; i < a; i++)
00277         {
00278             int b = (int)((((double)rand() / (double)RAND_MAX) * 26) + 97); 
00279             char c[2];
00280             c[0] = (char)b;
00281             c[1] = '\0';
00282             result = WvString("%s%s", result, c);
00283         }
00284         return WvString("%s", result);
00285     }  
00286     else if (type == TYPE_BOOL)
00287     {
00288         const char *strs[] = {
00289             "true", "yes", "on", "enabled", "1",
00290             "false", "no", "off", "disabled", "0"
00291         };
00292         int a = (int)(((double)rand() / (double)RAND_MAX) * 10);
00293         return WvString("%s", strs[a]);
00294     }
00295     else if (type == TYPE_IP || type == TYPE_IP_NETWORK)
00296     {
00297         int a = (int)(((double)rand() / (double)RAND_MAX) * 256), 
00298             b = (int)(((double)rand() / (double)RAND_MAX) * 256), 
00299             c = (int)(((double)rand() / (double)RAND_MAX) * 256),
00300             d = (int)(((double)rand() / (double)RAND_MAX) * 256);
00301         if (type == TYPE_IP_NETWORK)
00302         {
00303             int e = (int)(((double)rand() / (double)RAND_MAX) * 33);
00304             return WvString("%s.%s.%s.%s/%s", a, b, c, d, e);
00305         }
00306         else
00307             return WvString("%s.%s.%s.%s", a, b, c, d);
00308     }
00309     else if (type == TYPE_INT)
00310         return rand();
00311     else if (type == TYPE_FLOAT)
00312         return rand();
00313     else 
00314         return "";
00315 }
00316 
00317 /* type_name(int type)
00318  * Returns a string representation of the type passed to it.
00319  */
00320 WvString UniConfGremlin::type_name(int type)
00321 {
00322     if (type == TYPE_STRING)
00323         return "String";
00324     else if (type == TYPE_IP)
00325         return "IP";
00326     else if (type == TYPE_IP_NETWORK)
00327         return "IP Network";
00328     else if (type == TYPE_HOSTNAME)
00329         return "Hostname";
00330     else if (type == TYPE_BOOL)
00331         return "Boolean";
00332     else if (type == TYPE_INT)
00333         return "Integer";
00334     else if (type == TYPE_FLOAT)
00335         return "Float";
00336     else 
00337         return "Unknown";
00338 }
00339 
00340 /* curr_runlevel()
00341  * Returns a string describing the current runlevel.
00342  */
00343 WvString UniConfGremlin::curr_runlevel()
00344 {
00345     if (runlevel == 1)
00346         return "1 - Change keys with valid data.";
00347     else if (runlevel == 2)
00348         return "2 - Change keys with invalid data.";
00349     else if (runlevel == 3)
00350         return "3 - Add new keys.";
00351     else if (runlevel == 4)
00352         return "4 - Remove keys.";
00353     else if (runlevel == 5)
00354         return "5 - Add/Remove keys, change keys with valid/invalid data.";
00355     else
00356         return WvString("Apparently I'm on runlevel %s", runlevel);
00357 }
00358 
00359 /* status()
00360  * Displays the most recent runlevel and action.
00361  */
00362 WvString UniConfGremlin::status()
00363 {
00364     return WvString("Last Runlevel was : %s\nLast Action was : %s", 
00365             curr_runlevel(), last_change);
00366 }
00367 
00368 /* test()
00369  * Code can be put here to test and tweak the functionality of the gremlin
00370  */
00371 void UniConfGremlin::test()
00372 {
00373     printf("Testing Random Generators\n");
00374     printf("STRING %s\n", UniConfGremlin::rand_str(TYPE_STRING).cstr());
00375     printf("IP %s\n", UniConfGremlin::rand_str(TYPE_IP).cstr());
00376     printf("IP_NETWORK %s\n", UniConfGremlin::rand_str(TYPE_IP_NETWORK).cstr());
00377     printf("HOSTNAME %s\n", UniConfGremlin::rand_str(TYPE_HOSTNAME).cstr());
00378     printf("BOOL %s\n", UniConfGremlin::rand_str(TYPE_BOOL).cstr());   
00379     printf("INT %s\n", UniConfGremlin::rand_str(TYPE_INT).cstr());
00380     printf("FLOAT %s\n", UniConfGremlin::rand_str(TYPE_FLOAT).cstr());
00381     printf("Testing adding things to the UniConf\n"); 
00382     int count = 0;
00383     for (int i = 0; i < 5; i ++)
00384     {
00385         printf("Adding 5 random strings of type %s\n", type_name(i).cstr());
00386         for (int j = 0; j < 5; j ++)
00387         {
00388             WvString rand = rand_str(i);
00389             printf("Adding %s\n", rand.cstr());
00390             cfg[count].set(rand);
00391             cfg[count].commit();
00392             count ++;
00393         }
00394     }
00395     printf("Adding 5 random ints\n");
00396     for (int i = 5; i < 10; i ++)
00397     {
00398         int randint = rand();
00399         printf("Adding %i\n", randint);
00400         cfg[count].setint(randint);
00401         cfg[count].commit();
00402         count ++;
00403     }
00404     printf("Testing Find Victims\n");
00405     find_victims(key);
00406     for (int i = 0; i < num_victims; i ++)
00407     {
00408        printf("%s:", victims[i]->name.cstr());
00409        printf("%s:", type_name(victims[i]->type).cstr());
00410        printf("%s\n", cfg[victims[i]->name].get().cstr());
00411     }
00412     
00413     printf("Testing Start Trouble\n");
00414     start_trouble(1);
00415 }

Generated on Sat Feb 21 21:05:21 2004 for WvStreams by doxygen 1.3.5