modules/ac/ac_persistence.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following functions.
  1. acc_ip
  2. AC_acc_copy_l
  3. AC_persistence_get_leaves_hook_l
  4. ac_persistence_get_leaves
  5. AC_delete_timestamp_l
  6. AC_persistence_load
  7. AC_persistence_walk_l
  8. AC_persistence_save_l
  9. AC_persistence_save
  10. AC_persistence_init
  11. AC_persistence_daemon

   1 /***************************************
   2   $Revision: 1.7 $
   3 
   4   Access control (AC).  ac_persistence.c - functions to make the access
   5   control tree persistent.
   6 
   7   Status: NOT REVIEWED, NOT TESTED, COMPLETE
   8 
   9   Implementation by: Tiago Antao
  10 
  11   ******************/ /******************
  12   Copyright (c) 2002               RIPE NCC
  13  
  14   All Rights Reserved
  15   
  16   Permission to use, copy, modify, and distribute this software and its
  17   documentation for any purpose and without fee is hereby granted,
  18   provided that the above copyright notice appear in all copies and that
  19   both that copyright notice and this permission notice appear in
  20   supporting documentation, and that the name of the author not be
  21   used in advertising or publicity pertaining to distribution of the
  22   software without specific, written prior permission.
  23   
  24   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  25   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  26   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  27   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  28   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  29   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  30   ***************************************/
  31 
  32 /*
  33   ac_persistence - Does access control persistence
  34 
  35   External entry points are:
  36 
  37   AC_persistence_daemon - Persistence thread
  38   AC_persistence_save   - Saves the current access control tree
  39 */
  40 
  41 #include "rip.h"
  42 #include <math.h>
  43 
  44 /*
  45   Global variables.
  46 */
  47 
  48 int ac_auto_save; /* This should be encapsulated. */
  49 
  50 /*
  51   Local typedefs.
  52 */
  53 
  54 
  55 /* acc_ip
  56    It is needed because we need to know the IP of a acc on a GList context.
  57 */
  58 typedef struct {
  59   acc_st acc;
  60   ip_prefix_internal_t ip; /* internal?????*/
  61 } acc_ip;
     /* [<][>][^][v][top][bottom][index][help] */
  62 
  63 
  64 /*
  65   Local variables.
  66 */
  67 
  68 static rw_lock_t save_lock; /* lock for save operation*/
  69 static char on_save = 0;    /* lack of trylock in TH requires this */
  70 
  71 /*
  72   From access_control.c
  73 */
  74 void AC_decay_leaf_l(acc_st *leaf);
  75 char AC_prunable(acc_st *leaf);
  76 extern ut_timer_t oldest_timestamp;
  77 
  78 /*
  79   AC_acc_copy_l:
  80 
  81   Copies the contents of a acc_st variable.
  82 
  83   maybe it should go to access_control.c
  84 */
  85 void AC_acc_copy_l(acc_st *dest, acc_st *src) {
     /* [<][>][^][v][top][bottom][index][help] */
  86   memcpy(dest, src, sizeof(acc_st)); /* This is OK for the current struct data */
  87 }
  88 
  89 /*
  90   AC_persistence_get_leaves_hook_l:
  91 
  92   Hook for the return of a Glist of leaves. Constructs an acc_ip.
  93 */
  94 /* static */
  95 er_ret_t AC_persistence_get_leaves_hook_l(rx_node_t* node, int level,
     /* [<][>][^][v][top][bottom][index][help] */
  96     int node_counter, void *con) {
  97   GList** list;
  98   acc_ip* ip_acc;
  99   acc_st* acc;
 100 
 101 
 102 
 103   list = (GList**) con;
 104   acc = node->leaves_ptr->data;
 105   if (acc->changed != AC_ACC_NOT_CHANGED) {
 106     ip_acc = UT_malloc(sizeof(acc_ip));
 107     AC_acc_copy_l(&(ip_acc->acc), acc);
 108 
 109     acc->changed = AC_ACC_NOT_CHANGED;
 110 
 111     ip_acc->ip = node->prefix;
 112 
 113     *list = g_list_prepend(*list, ip_acc);
 114   }
 115   return RX_OK;
 116 } /* AC_persistence_get_leaves_hook_l */
 117 
 118 /*
 119   ac_persistence_get_leaves:
 120 
 121   Returns a GList of leaves (clone, ie don't forget to deallocate elements)
 122 
 123   Write locks act_runtime.
 124 */
 125 GList* ac_persistence_get_leaves() {
     /* [<][>][^][v][top][bottom][index][help] */
 126   GList *list;
 127   er_ret_t ret_err;
 128 
 129   list = NULL;
 130 
 131   TH_acquire_write_lock(&(act_runtime->rwlock));
 132   rx_walk_tree(act_runtime->top_ptr, AC_persistence_get_leaves_hook_l,
 133     RX_WALK_SKPGLU, 255, 0, 0, &list, &ret_err);
 134   /* dieif(ret_err != RX_OK); -- can be safely ignored as result is always OK*/
 135   TH_release_write_lock(&(act_runtime->rwlock));
 136 
 137   return list;
 138 } /* ac_persistence_get_leaves */
 139 
 140 
 141 /*
 142   AC_delete_timestamp_l
 143 */
 144 void AC_delete_timestamp_l(SQ_connection_t* conn) {
     /* [<][>][^][v][top][bottom][index][help] */
 145   char delete_query[120];
 146 
 147   sprintf(delete_query,
 148           "DELETE FROM access "
 149           "      WHERE timestamp<%11.0f AND addr_passes=0 AND denials=0 ",
 150           ceil(UT_time_getvalue(&oldest_timestamp)) - 10);
 151   //printf("%s\n", delete_query);
 152   SQ_execute_query(conn, delete_query, NULL); //ignoring ?!?
 153 }
 154 
 155 /*
 156   AC_persistence_load:
 157 
 158   Loads the SQL access control data into the act_runtime tree.
 159   Also, clears old leaves from SQL.
 160 
 161   This is different from discussed design!!! Explain:
 162 
 163   This operation blocks the server. It is all in-memory so it should not take too
 164   long.
 165 
 166   Pseudo-code:
 167 
 168   connect to admin DB
 169   SELECT * FROM access
 170   write lock act_runtime
 171   for each row
 172     creates acc_st and ip_prefix
 173     calculates new decay
 174     if node still active then
 175       adds acc_st/ip_prefix to act_runtime
 176       if timestamp<oldest_timestamp
 177         oldest_timestamp = timestamp;
 178   release lock
 179   DELETE FROM access WHERE timestamp<oldest_timestamp
 180   close DB
 181 
 182 */
 183 void AC_persistence_load(void) {
     /* [<][>][^][v][top][bottom][index][help] */
 184   SQ_connection_t* sql_conn;
 185   SQ_result_set_t* rs;
 186   SQ_row_t *row;
 187   er_ret_t ret_err = RX_OK;
 188   unsigned long timestamp;
 189 
 190 
 191   sql_conn = AC_dbopen_admin();
 192 
 193   if (SQ_execute_query(sql_conn, "SELECT prefix, prefix_length, connections, "
 194                                          "addr_passes, denials, queries, "
 195                                          "referrals, public_objects, "
 196                                          "private_objects, public_bonus, "
 197                                          "private_bonus, timestamp "
 198                                  "FROM   access", &rs) == 0) {
 199     TH_acquire_write_lock(&(act_runtime->rwlock));
 200 
 201     while ((row = SQ_row_next(rs)) != NULL && ret_err == RX_OK) {
 202       acc_st *acc;
 203       ip_prefix_t ip;
 204 
 205       /* Repeat with me: With pre-historic languages you have to do basic
 206                          memory management. 8-) */
 207       acc = UT_malloc(sizeof(acc_st));
 208 
 209       IP_pref_f2b_v4(&ip, 
 210                      SQ_get_column_string_nocopy(rs, row, 0),
 211                      SQ_get_column_string_nocopy(rs, row, 1));
 212 
 213       /*
 214         SQ_get_column_int errors are not detected.
 215         In theory it should not be a problem
 216       */
 217       SQ_get_column_int(rs, row,  2, (long *)&acc->connections);
 218       SQ_get_column_int(rs, row,  3, (long *)&acc->addrpasses);
 219       SQ_get_column_int(rs, row,  4, (long *)&acc->denials);
 220       SQ_get_column_int(rs, row,  5, (long *)&acc->queries);
 221       SQ_get_column_int(rs, row,  6, (long *)&acc->referrals);
 222       SQ_get_column_int(rs, row,  7, (long *)&acc->public_objects);
 223       SQ_get_column_int(rs, row,  8, (long *)&acc->private_objects);
 224       acc->public_bonus = atof(SQ_get_column_string_nocopy(rs, row, 9));
 225       acc->private_bonus = atof(SQ_get_column_string_nocopy(rs, row, 10));
 226       SQ_get_column_int(rs, row, 11, (long*)&timestamp);
 227 
 228       UT_time_set(&acc->timestamp, timestamp, 0);
 229 
 230       /* Mark it as not changed */
 231       acc->changed = AC_ACC_NOT_CHANGED;
 232 
 233       AC_decay_leaf_l(acc);
 234       /* lets be memory efficient and not create if it is decayed */
 235       if (AC_prunable(acc)) {
 236         UT_free(acc);
 237       }
 238       else {
 239         if (UT_timediff(&acc->timestamp, &oldest_timestamp) > 0 &&
 240             acc->denials == 0 && acc->addrpasses == 0) {
 241           oldest_timestamp = acc->timestamp;
 242         }
 243         // what about if it already exists?
 244         ret_err = rx_bin_node(RX_OPER_CRE, &ip, act_runtime, (rx_dataleaf_t*) acc);
 245       }
 246     }
 247     // if ret_err...
 248 
 249     SQ_free_result(rs);
 250     TH_release_write_lock(&(act_runtime->rwlock));
 251   }
 252   else {
 253     ER_perror(FAC_AC, AC_INTR_ERR, "Couldn't load access table");
 254   }
 255   AC_delete_timestamp_l(sql_conn);
 256   SQ_close_connection(sql_conn);
 257 
 258 } /* AC_persistence_load */
 259 
 260 /*
 261   AC_persistence_walk_l:
 262 
 263   Writes each leaf (or not, depending on timestamp & changed).
 264 
 265   Pseudo-code:
 266 
 267   for each element
 268     if acc.changed = not changed
 269       continue
 270     if acc.changed == new
 271       insert
 272       if insert ok
 273         return
 274     update (also on special acc.changed cases)
 275     if fail log
 276 
 277 */
 278 static void AC_persistence_walk_l(GList *list) {
     /* [<][>][^][v][top][bottom][index][help] */
 279   acc_ip *element;
 280   SQ_connection_t *sql_conn;
 281   char sql[500];
 282   GList *curr_list;
 283 
 284   sql_conn  = AC_dbopen_admin();
 285   curr_list = list;
 286 
 287   while (curr_list) {
 288     element   = curr_list->data;
 289     curr_list = g_list_next(curr_list);
 290     if (element->acc.changed == AC_ACC_NOT_CHANGED ||
 291         IP_pref_b2v4_addr(&element->ip) == 0) continue;
 292     if (element->acc.changed == AC_ACC_NEW) {
 293       sprintf(sql, "INSERT INTO access(prefix, prefix_length, connections, addr_passes, denials, queries, referrals, public_objects, private_objects, public_bonus, private_bonus, timestamp) VALUES(%u, %d, %d, %d, %d, %d, %d, %d, %d, %f, %f, %10.0f)",
 294         IP_pref_b2v4_addr(&element->ip),
 295         IP_pref_b2_len(&element->ip),
 296         element->acc.connections,
 297         element->acc.addrpasses,
 298         element->acc.denials,
 299         element->acc.queries,
 300         element->acc.referrals,
 301         element->acc.public_objects,
 302         element->acc.private_objects,
 303         element->acc.public_bonus,
 304         element->acc.private_bonus,
 305         ceil(UT_time_getvalue(&element->acc.timestamp))
 306       );
 307       //printf("%s\n", sql);
 308       if (SQ_execute_query(sql_conn, sql, NULL) == 0 ) {
 309         continue;
 310       }
 311       /* It is OK to fail, let's wait for UPDATE */
 312     }
 313     sprintf(sql, "UPDATE access "
 314                     "SET connections = %d, addr_passes = %d, denials = %d, "
 315                          "queries = %d, referrals = %d, public_objects = %d, "
 316                          "private_objects = %d , public_bonus = %f, "
 317                          "private_bonus = %f, timestamp = %10.0f "
 318                   "WHERE prefix = %u AND prefix_length = %d",
 319       element->acc.connections,
 320       element->acc.addrpasses,
 321       element->acc.denials,
 322       element->acc.queries,
 323       element->acc.referrals,
 324       element->acc.public_objects,
 325       element->acc.private_objects,
 326       element->acc.public_bonus,
 327       element->acc.private_bonus,
 328       ceil(UT_time_getvalue(&element->acc.timestamp)),
 329       IP_pref_b2v4_addr(&element->ip),
 330       IP_pref_b2_len(&element->ip)
 331     );
 332     //printf("%s\n", sql);
 333     if (SQ_execute_query(sql_conn, sql, NULL) != 0 ) {
 334       ER_perror(FAC_AC, AC_INTR_ERR, "Problems with database updates on "
 335                                      "admin database");
 336       SQ_close_connection(sql_conn);
 337       return;
 338     }
 339   }
 340   AC_delete_timestamp_l(sql_conn);
 341   SQ_close_connection(sql_conn);
 342 }
 343 
 344 
 345 
 346 /*
 347   AC_persistence_save_l:
 348 
 349   Saves the act_runtime tree into the SQL access control data table:
 350     This is the function that realy does it (not the public
 351     AC_persistence_save).
 352 
 353   Pseudo-code:
 354 
 355   gets a list of relevant leaves
 356   writes the leaves to SQL
 357   destroy the list of leaves
 358 
 359 */
 360 static void AC_persistence_save_l(void) {
     /* [<][>][^][v][top][bottom][index][help] */
 361   GList *leaves;
 362   int length;
 363   int cont;
 364 
 365   leaves = ac_persistence_get_leaves();
 366 
 367   if (leaves) {
 368     AC_persistence_walk_l(leaves);
 369 
 370     /* free GList (&contents!!) */
 371     length = g_list_length(leaves);
 372     for(cont=0; cont<length; cont++) {
 373       UT_free(g_list_nth_data(leaves, cont));
 374     }
 375     g_list_free(leaves);
 376   }
 377 } /* AC_persistence_save_l */
 378 
 379 
 380 /*
 381   AC_persistence_save:
 382 
 383   Saves the act_runtime tree into the SQL access control data table:
 384    Checks if another thread is saving, if so doesn't save.
 385    Saving is done by a call to ac_persistence_save which does the real job.
 386 
 387    Returns AC_OK if done or AC_SAVING in the case of another save process
 388    is running.
 389 
 390    Pseudo-code:
 391 
 392    if !(acquire saving rights)
 393      return AC_SAVING
 394    save
 395    release saving rights
 396    return AC_OK
 397 */
 398 er_ret_t AC_persistence_save(void) {
     /* [<][>][^][v][top][bottom][index][help] */
 399 
 400 
 401   /* Let's see if a save is already running... */
 402   TH_acquire_write_lock(&save_lock);
 403   /* Save already running*/
 404   if (on_save) {
 405     TH_release_write_lock(&save_lock);
 406     return AC_SAVING;
 407   }
 408   /* else ...
 409      We have a go! 
 410   */
 411   on_save = 1;
 412   TH_release_write_lock(&save_lock);
 413 
 414   AC_persistence_save_l();
 415 
 416   /* Signal end of save operation */
 417   TH_acquire_write_lock(&save_lock);
 418   on_save = 0;
 419   TH_release_write_lock(&save_lock);
 420 
 421   return AC_OK;
 422 
 423 } /* AC_persistence save */
 424 
 425 
 426 /*
 427   AC_persistence_init:
 428 
 429   Initializes the persistence system:
 430 
 431    1. Initializes the dump control mutex.
 432    2. Initializes the oldest_timestamp.
 433    3. If required by user loads SQL accounting data.
 434    4. Sets auto save.
 435 */
 436 er_ret_t AC_persistence_init(void) {
     /* [<][>][^][v][top][bottom][index][help] */
 437   TH_init_read_write_lock(&save_lock);
 438 
 439   UT_timeget(&oldest_timestamp);
 440 
 441   if (ca_get_ac_load) {
 442     AC_persistence_load();
 443   }
 444 
 445   ac_auto_save = ca_get_ac_auto_save;
 446   return AC_OK;
 447 } /* AC_persistence_init */
 448 
 449 
 450 /*
 451   AC_persistence_daemon:
 452 
 453   Implements the top-level thread code:
 454 
 455   Pseudo-code:
 456 
 457   while (CO_get_do_server)
 458     Sleep.
 459     If auto save
 460       Dumps the current version of the act_runtime tree to the persistence
 461         store.
 462 */
 463 er_ret_t AC_persistence_daemon(void) {
     /* [<][>][^][v][top][bottom][index][help] */
 464 
 465   TA_add(0, "persistence");
 466 
 467   while(CO_get_do_server()) {
 468     SV_sleep(ca_get_ac_save_interval);
 469     if (ac_auto_save) {
 470       AC_persistence_save();
 471     }
 472   }
 473 
 474   TA_delete();
 475 
 476   return AC_OK;
 477 } /* AC_persistence_daemon */

/* [<][>][^][v][top][bottom][index][help] */