## utils/history/recreate.c

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

## DEFINITIONS

This source file includes following functions.

1. process_arguments
2. put_delete_before
3. get_serial_interval
4. generate_load_data
5. serve_TCP
6. fake_sources
7. get_operation
8. get_minimum_RIPE_serial
9. wait_for_whoisd_completion
10. process_timestamp_add
11. process_timestamp_delete
12. correct_timestamps
13. main

       1 /***************************************
       2   $Revision: 1.1 $
       3
       4   Recreate.  recreate.c - whois DB recreation via pseudo mirror server.
       5
       6   Status: NOT REVIEWED, TESTED, COMPLETE
       7
       8   Implementation by: Tiago Antao
       9
      10   ******************/ /******************
      11   Copyright (c) 2002               RIPE NCC
      12
      13   All Rights Reserved
      14
      15   Permission to use, copy, modify, and distribute this software and its
      16   documentation for any purpose and without fee is hereby granted,
      17   provided that the above copyright notice appear in all copies and that
      18   both that copyright notice and this permission notice appear in
      19   supporting documentation, and that the name of the author not be
      20   used in advertising or publicity pertaining to distribution of the
      21   software without specific, written prior permission.
      22
      23   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
      24   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
      25   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
      26   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
      27   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      28   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      29   ***************************************/
      30
      31
      32 #include <stdio.h>
      33 #include <stdlib.h>
      34 #include <time.h>
      35 #include <regex.h>
      36 #include <rip.h>
      37 #include "miniconf.h"
      38 #include "dbsupport.h"
      39 #include "aconf.h"
      40
      41 int  response_sock;
      42 long end_serial = 0;
      43 long serial_max;
      44 long serial_min;
      45 long requested_max;
      46 long requested_min;
      47 char source_name[100];
      48 long RIPE_serial_min;
      49 long updates_sent;
      50
      51 /*
      52   process_arguments: proccesses command-line arguments.
      53 */
      54 void process_arguments(int argv, char** argc) {
         /* [<][>][^][v][top][bottom][index][help] */
      55   if (argv != 2 ) {
      56     printf("Usage: %s <serial>\n", argc[0]);
      57     exit(1);
      58   }
      59
      60   end_serial = atol(argc[1]); //assume is number
      61
      62   if (end_serial == 0) {
      63     printf("Usage: %s <serial>\n", argc[0]);
      64     exit(1);
      65   }
      66
      67 }
      68
      69 void put_delete_before(char* pkey, long timestamp, char* buffer) {
         /* [<][>][^][v][top][bottom][index][help] */
      70   SQ_result_set_t* rs;
      71   SQ_row_t*        row;
      72   char             query[200];
      73   char             ppkey[200];
      74   long             ts;
      75   char*            object;
      76
      77   prepare_string_attribute(pkey,
      78                            ppkey);
      79   sprintf(query,
      80           "SELECT max(timestamp) FROM archive "
      81           " WHERE timestamp<%ld AND pkey='%s'",
      82           timestamp, ppkey);
      83   SQ_execute_query(archive_conn, query, &rs);
      84   row = SQ_row_next(rs);
      85   if (row==NULL) { //very bad
      86     printf ("Severe error with executing: %s\n", query);
      87     exit(1);
      88   }
      89   SQ_get_column_int(rs, row, 0, &ts);
      90   SQ_free_result(rs);
      91
      92   sprintf(query,
      93           "SELECT object FROM archive WHERE timestamp=%ld "
      94           "   AND pkey='%s' AND operation=1",
      95           timestamp, pkey);
      96   SQ_execute_query(archive_conn, query, &rs);
      97   row = SQ_row_next(rs);
      98   if (row==NULL) { //bummer, it has to be on last of RIP DB
      99     SQ_free_result(rs);
     100     sprintf(query,
     101             "SELECT object FROM last WHERE pkey='%s'",
     102             pkey);
     103     SQ_execute_query(RIPE_conn, query, &rs);
     104     row = SQ_row_next(rs);
     105     if (row==NULL) { //very bad
     106       printf ("Severe error with executing: %s\n", query);
     107       exit(1);
     108     }
     109   }
     110   object = SQ_get_column_string_nocopy(rs, row, 0);
     111   sprintf(buffer, "DEL\n\n%s\n\n", object);
     112   SK_write(response_sock, buffer, strlen(buffer), NULL, NULL);
     113   SQ_free_result(rs);
     114 }
     115
     116 /*
     117   get_serial_interval: gets the upper and lower bounds of available serials.
     118 */
     119 void get_serial_interval() {
         /* [<][>][^][v][top][bottom][index][help] */
     120   SQ_result_set_t* rs;
     121   SQ_row_t*        row;
     122   char             query[200];
     123
     124   sprintf(query,
     125           "SELECT min(serial_id), max(serial_id) "
     126           "  FROM archive "
     127           " WHERE serial_id<=%ld ",
     128           end_serial);
     129   SQ_execute_query(archive_conn, query, &rs);
     130   row = SQ_row_next(rs);
     131   SQ_get_column_int(rs, row, 0, &serial_min);
     132   SQ_get_column_int(rs, row, 1, &serial_max);
     133   SQ_free_result(rs);
     134 }
     135
     136 /*
     137   generate_load_data: generates and send data to be recreated.
     138 */
     139 void generate_load_data() {
         /* [<][>][^][v][top][bottom][index][help] */
     140   SQ_result_set_t* rs;
     141   SQ_row_t*        row;
     142   char             query[200];
     143   char*            object;
     144   long             operation;
     145   char*            pkey;
     146   long             timestamp;
     147   char             sbuffer[1000000]; // ridiculous but more bug proof?
     148
     149
     150   updates_sent = 0;
     151   sprintf(sbuffer,
     152           "%%START Version: 2 %s %ld-%ld\n\n",
     153           source_name, requested_min, serial_max);
     154   SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
     155   sprintf(query,
     156           "  SELECT object, operation, pkey, timestamp "
     157           "    FROM archive "
     158           "   WHERE serial_id<=%ld AND serial_id>=%ld "
     159           "     AND serial_id IS NOT NULL "
     160           "ORDER BY timestamp ASC, serial_id ASC ",
     161           // order because of special deletes (op=3)
     162           end_serial, requested_min);
     163   SQ_execute_query(archive_conn, query, &rs);
     164   while ((row = SQ_row_next(rs)) != NULL) {
     165     updates_sent++;
     166     object = SQ_get_column_string_nocopy(rs, row, 0);
     167     SQ_get_column_int(rs, row, 1, &operation);
     168     switch (operation) {
     169     case 1:
     170       sprintf(sbuffer, "ADD\n\n%s\n\n", object);
     171       SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
     172       break;
     173     case 2:
     174       sprintf(sbuffer, "DEL\n\n%s\n\n", object);
     175       SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
     176       break;
     177     case 3:
     178       pkey = SQ_get_column_string_nocopy(rs, row, 2);
     179       SQ_get_column_int(rs, row, 3, &timestamp);
     180       put_delete_before(pkey, timestamp, sbuffer);
     181       sprintf(sbuffer, "ADD\n\n%s\n\n", object);
     182       SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
     183       break;
     184     }
     185
     186   }
     187   SQ_free_result(rs);
     188
     189   sprintf(sbuffer,"%%END %s\n\n", source_name);
     190   SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
     191 }
     192
     193 /*
     194   serve_TCP: creates a socket and accepts a connection.
     195 */
     196 void serve_TCP() {
         /* [<][>][^][v][top][bottom][index][help] */
     197   int sock;
     198
     199   sock = SK_getsock(SOCK_STREAM, 24444, 128, INADDR_ANY);
     200   //printf("Accepting connections\n");
     201   response_sock = SK_accept_connection(sock);
     202 }
     203
     204 /*
     205   fake_sources: fakes a source
     206
     207   Irrelevant?
     208 */
     209 void fake_sources() {
         /* [<][>][^][v][top][bottom][index][help] */
     210   char line[100];
     211   sprintf(line, "RIPE:2:Y:%ld-%ld\n\n", serial_min, serial_max);
     212   SK_write(response_sock, line, strlen(line), NULL, NULL);
     213 }
     214
     215 /*
     216   get_operation: gets the operation required by the client.
     217
     218   Also puts the source name in a global variable if -g requested.
     219 */
     220 int get_operation() {
         /* [<][>][^][v][top][bottom][index][help] */
     221   char       line[201];
     222   regex_t    reg;
     223   regmatch_t match[3];
     224
     225   bzero(line, 201);
     226   if (SK_gets(response_sock, line, 200)>=0) {
     227     if (strncmp(line,"-q sources", 10)==0) {
     228       return 0;
     229     }
     230     else {
     231       regcomp(&reg,"-g (.+):2:([0-9]+)-", REG_EXTENDED);
     232       if (regexec(&reg, line, 3, match, 0) != 0) {
     233         printf("match failed: %s\n", line);
     234         exit(1);
     235       }
     236       //printf("match OK: %s\n", line);
     237       bzero(source_name, 99);
     238       strncpy(source_name,
     239               line + match[1].rm_so,
     240               match[1].rm_eo - match[1].rm_so);
     241       requested_min = atol(line+match[2].rm_so);
     242       //printf("%sXX\n", line+match[2].rm_so);
     243       return 1;
     244     }
     245   }
     246   else {
     247     //printf("Input from client ended\n");
     248     exit(1);
     249   }
     250 }
     251
     252 /*
     253   get_minimum_RIPE_serial: returns the minimum serial of recreate
     254
     255   The minimum serial of recreate is the initial max + 1.
     256   Returns in a global variable.
     257 */
     258 void get_minimum_RIPE_serial() {
         /* [<][>][^][v][top][bottom][index][help] */
     259   SQ_result_set_t* rs;
     260   SQ_row_t*        row;
     261   long             result;
     262
     263   SQ_execute_query(RIPE_conn,
     264                    "SELECT max(serial_id) FROM serials",
     265                    &rs);
     266
     267   row = SQ_row_next(rs);
     268
     269   if (SQ_get_column_int(rs, row, 0, &result) == 0) {
     270     RIPE_serial_min = result + 1;
     271   }
     272   else {
     273     RIPE_serial_min = 1;
     274   }
     275
     276   SQ_free_result(rs);
     277 }
     278
     279 /*
     280   wait_for_whoisd_completion: as named
     281
     282   As we know the serial when we started and the number of updates sent
     283   we know which number should be the max on serials.
     284   We wait for it.
     285 */
     286 void wait_for_whoisd_completion() {
         /* [<][>][^][v][top][bottom][index][help] */
     287   SQ_result_set_t* rs;
     288   SQ_row_t*        row;
     289   long             current_serial=0;
     290
     291   while (current_serial<RIPE_serial_min+updates_sent) {
     292     SQ_execute_query(RIPE_conn,
     293                      "SELECT max(serial_id) FROM serials",
     294                      &rs);
     295
     296     row = SQ_row_next(rs);
     297
     298     if (SQ_get_column_int(rs, row, 0, &current_serial) != 0) {
     299       current_serial = 0;
     300     }
     301
     302     SQ_free_result(rs);
     303     sleep(1); //ALWAYS wait
     304   }
     305 }
     306
     307 /*
     308   process_timestamp_add: patches the timestmap of an add
     309 */
     310 void process_timestamp_add(SQ_result_set_t* rs,
         /* [<][>][^][v][top][bottom][index][help] */
     311                            SQ_row_t*        row,
     312                            long             timestamp) {
     313   long atlast;
     314   long seq_id;
     315   long object_id;
     316   char upd[200];
     317
     318   SQ_get_column_int(rs, row, 0, &object_id);
     319   SQ_get_column_int(rs, row, 1, &seq_id);
     320   SQ_get_column_int(rs, row, 2, &atlast);
     321
     322   if (atlast) {
     323     sprintf(upd,
     324             "UPDATE last SET timestamp=%ld WHERE object_id=%ld",
     325             timestamp, object_id);
     326   }
     327   else {
     328     sprintf(upd,
     329             "UPDATE history "
     330             "   SET timestamp=%ld "
     331             " WHERE object_id=%ld AND sequence_id=%ld",
     332             timestamp, object_id, seq_id);
     333   }
     334   SQ_execute_query(RIPE_conn, upd, NULL);
     335 }
     336
     337 /*
     338   process_timestamp_delete: patches the timestamp of a delete
     339 */
     340 void process_timestamp_delete(SQ_result_set_t* rs,
         /* [<][>][^][v][top][bottom][index][help] */
     341                               SQ_row_t*        row,
     342                               long             timestamp) {
     343   long object_id;
     344   char upd[200];
     345
     346   SQ_get_column_int(rs, row, 0, &object_id);
     347
     348   sprintf(upd,
     349           "UPDATE last "
     350           "   SET timestamp=%ld "
     351           " WHERE object_id=%ld AND object=''",
     352           timestamp, object_id);
     353   SQ_execute_query(RIPE_conn, upd, NULL);
     354 }
     355
     356 /*
     357   correct_timestamps:
     358 */
     359 void correct_timestamps() {
         /* [<][>][^][v][top][bottom][index][help] */
     360   SQ_result_set_t* RIPE_rs;
     361   SQ_row_t*        RIPE_row;
     362   char             RIPE_query[200];
     363   SQ_result_set_t* archive_rs;
     364   SQ_row_t*        archive_row;
     365   char             archive_query[200];
     366   long             operation;
     367   long             timestamp;
     368
     369   sprintf(RIPE_query,
     370           "  SELECT object_id, sequence_id, atlast "
     371           "    FROM serials "
     372           "   WHERE serial_id>=%ld "
     373           "ORDER BY serial_id ",
     374           RIPE_serial_min);
     375   SQ_execute_query(RIPE_conn, RIPE_query, &RIPE_rs);
     376
     377   sprintf(archive_query,
     378           "  SELECT timestamp, operation "
     379           "    FROM archive "
     380           "   WHERE serial_id<=%ld AND serial_id>=%ld "
     381           "     AND serial_id IS NOT NULL "
     382           "ORDER BY timestamp ASC, serial_id ASC ",
     383           end_serial, requested_min);
     384   SQ_execute_query(archive_conn, archive_query, &archive_rs);
     385
     386   while((archive_row = SQ_row_next(archive_rs)) != NULL) {
     387     SQ_get_column_int(archive_rs, archive_row, 0, &timestamp);
     388     SQ_get_column_int(archive_rs, archive_row, 1, &operation);
     389     if (operation==3) {
     390       RIPE_row = SQ_row_next(RIPE_rs);
     391       if (RIPE_row == NULL) {
     392         printf("serials are incomplete irt archive\n");
     393         exit(1);
     394       }
     395       process_timestamp_delete(RIPE_rs, RIPE_row, timestamp);
     396       operation = 1;
     397     }
     398     RIPE_row = SQ_row_next(RIPE_rs);
     399     if (RIPE_row == NULL) {
     400       printf("serials are incomplete irt archive\n");
     401       exit(1);
     402     }
     403     if (operation==1) {
     404       process_timestamp_add(RIPE_rs, RIPE_row, timestamp);
     405     }
     406     else {
     407       process_timestamp_delete(RIPE_rs, RIPE_row, timestamp);
     408     }
     409
     410   }
     411   RIPE_row = SQ_row_next(RIPE_rs);
     412   if (RIPE_row != NULL) {
     413     printf("archive is incomplete irt serials\n");
     414     exit(1);
     415   }
     416
     417   SQ_free_result(RIPE_rs);
     418   SQ_free_result(archive_rs);
     419 }
     420
     421 /*
     422   main: recreate entry point.
     423
     424   Determine biggest serial on process start (on RIPE DB)
     425   Get serials available (on archive DB)
     426   Wait for TCP connection
     427   while(1)
     428    switch (whoisd request)
     429      0: fake_sources (-g & -q)
     430      1: generate_load_data
     431         wait for whois completion
     432         patch timestamps
     433 */
     434 int main (int argv, char** argc) {
         /* [<][>][^][v][top][bottom][index][help] */
     435   process_arguments(argv, argc);
     436   read_configuration();
     437   get_db_connections();
     438
     439   get_minimum_RIPE_serial();
     440   get_serial_interval();
     441   serve_TCP();
     442   while (1) {
     443     switch (get_operation()) {
     444     case 0:
     445       fake_sources();
     446       break;
     447     case 1:
     448       generate_load_data();
     449       wait_for_whoisd_completion();
     450       correct_timestamps();
     451       return 0;
     452     }
     453   }
     454
     455   //close_dbs();
     456
     457   return 0;
     458 }

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