modules/pm/protocol_mirror.c

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

FUNCTIONS

This source file includes following functions.
  1. IS_Q_QUERY
  2. IS_G_QUERY
  3. IS_PERSISTENT
  4. parse_request
  5. PM_interact

   1 /***************************************
   2 
   3   Protocol mirror module (pw). 
   4 
   5   Status: NOT REVUED, NOT TESTED
   6 
   7   ******************/ /******************
   8   Filename            : protocol_mirror.c
   9   Author              : andrei
  10   OSs Tested          : Solaris
  11   ******************/ /******************
  12   Copyright (c) 2000                              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 #include <stdio.h>
  32 #include <glib.h>
  33 
  34 #include "protocol_mirror.h"
  35 #include "mysql_driver.h"
  36 #include "constants.h"
  37 
  38 //#include "access_control.h"
  39 #include "sk.h"
  40 #include "stubs.h"
  41 #include "ud.h"
  42 #include "ta.h"
  43 
  44 #include "ca_configFns.h"
  45 #include "ca_dictionary.h"
  46 #include "ca_macros.h"
  47 #include "ca_srcAttribs.h"
  48 
  49 #include "erroutines.h"
  50 
  51 #include "getopt.h"
  52 
  53 #define MIN_ARG_LENGTH  6
  54 #define NRTM_DELIM "-:"
  55 
  56 #define MAX_OPT_ARG_C 3
  57 
  58 #define Q_QUERY      0x01
  59 #define G_QUERY      0x02
  60 #define K_QUERY      0x04
  61 
  62 #define IS_Q_QUERY(a)          ((a)&Q_QUERY)
     /* [<][>][^][v][top][bottom][index][help] */
  63 #define IS_G_QUERY(a)          ((a)&G_QUERY)
     /* [<][>][^][v][top][bottom][index][help] */
  64 #define IS_PERSISTENT(a)       ((a)&K_QUERY)
     /* [<][>][^][v][top][bottom][index][help] */
  65 
  66 
  67 /*
  68 * parses input and fills nrtm_q_t structure
  69 *
  70 * Returns:
  71 *  -1 in case of garbage
  72 *  1  in case of -q sources
  73 *  2  in case of valid -g
  74 *  3  in case of -k
  75 */
  76 static int parse_request(char *input, nrtm_q_t *nrtm_q)
     /* [<][>][^][v][top][bottom][index][help] */
  77 {
  78  int res=0, err=0;
  79  int opt_argc;
  80  int c;
  81  gchar **opt_argv;
  82  getopt_state_t *gst = NULL;
  83 
  84   /* Create the arguments. */
  85   /* This allows only a maximum of MAX_OPT_ARG_C words in the query. */
  86   opt_argv = g_strsplit(input, " ", MAX_OPT_ARG_C);
  87 
  88   /* Determine the number of arguments. */
  89   for (opt_argc=0; opt_argv[opt_argc] != NULL; opt_argc++);
  90 
  91   dieif( (gst = mg_new(0)) == NULL );
  92   
  93   while ((c = mg_getopt(opt_argc, opt_argv, "kq:g:", gst)) != EOF) 
  94   {
  95     switch (c) {
  96             case 'k':
  97                res |= K_QUERY; /* persistent connection */ 
  98             break;
  99 
 100             case 'q':
 101                 if (gst->optarg != NULL) {
 102                 char *token, *cursor = gst->optarg;
 103                    
 104                    res |= Q_QUERY;
 105                    err=strncmp(cursor, "sources", 7);
 106                    if(err!=0) break;
 107                    cursor+=7;
 108                    g_strchug(cursor);
 109                    token=cursor;
 110                    /* if no sourses are specified - put NULL in nrtm_q->source and list them all */
 111                    if ((*token=='\0') || (*token=='\n') || ((int)*token==13))nrtm_q->source=NULL;
 112                    else {
 113                      cursor=index(token, ' ');
 114                      if (cursor) nrtm_q->source=g_strndup(token, (cursor-token));
 115                      else {
 116                        cursor=index(token, 13); /* search for ctrl-M - telnet loves this */
 117                        if (cursor) nrtm_q->source=g_strndup(token, (cursor-token));
 118                        else {
 119                          cursor=index(token, '\n');
 120                          if (cursor) nrtm_q->source=g_strndup(token, (cursor-token));
 121                          else nrtm_q->source=g_strdup(token);
 122                        }
 123                      }
 124                    }
 125                 /* if source was specified - convert it to an upper case */
 126                 if (nrtm_q->source) g_strup(nrtm_q->source);
 127                 } else err=1;
 128             break;
 129 
 130             case 'g':
 131                 if (gst->optarg != NULL) {
 132                 char *cursor = gst->optarg;
 133                 char **tokens;  
 134                 
 135                   res |= G_QUERY;
 136                   g_strdelimit(cursor, NRTM_DELIM, ':');
 137                   tokens=g_strsplit(cursor, ":", 4);
 138                   if(tokens==NULL) { err=1; break; }
 139  
 140                   if(tokens[0]) {
 141                   /* first token is source name */       
 142                      nrtm_q->source=g_strdup(tokens[0]);
 143                      /* convert it to an upper case */
 144                      g_strup(nrtm_q->source);
 145                      if(tokens[1]) {
 146                     /* second token is version number */
 147                         nrtm_q->version=atoi(tokens[1]);
 148                         if(tokens[2]) {
 149                         /* this is first serial */      
 150                            nrtm_q->first=atol(tokens[2]);
 151                            if (nrtm_q->first>0) {
 152                               if(tokens[3]) {
 153                              /* this is last serial */
 154                                  nrtm_q->last=atol(tokens[3]);
 155                                  if (nrtm_q->last==0) 
 156                                  if (strncasecmp(tokens[3], "LAST", 4)!=0) err=1;
 157                               } else err=1;
 158                            } else err=1;    
 159                         } else err=1; 
 160                      } else err=1;  
 161                   } else err=1;   
 162                   g_strfreev(tokens);
 163  
 164                 } else err=1;
 165                 
 166             break;
 167             default:
 168                 err=1;
 169             break;
 170     } /* switch */
 171   } /* while there are arguments */
 172 
 173  free(gst);
 174  g_strfreev(opt_argv);
 175 
 176  if (err) return(-1);
 177  else return(res); 
 178    
 179 }
 180 
 181 
 182 /* PM_interact() */
 183 /*++++++++++++++++++++++++++++++++++++++
 184   Interact with the client.
 185 
 186   int sock Socket that client is connected to.
 187 
 188   More:
 189   +html+ <PRE>
 190   Authors:
 191         ottrey
 192         andrei
 193 
 194   +html+ </PRE><DL COMPACT>
 195   +html+ <DT>Online References:
 196   +html+ <DD><UL>
 197   +html+ </UL></DL>
 198 
 199   ++++++++++++++++++++++++++++++++++++++*/
 200 void PM_interact(int sock) {
     /* [<][>][^][v][top][bottom][index][help] */
 201   char input[MAX_PM_INPUT_SIZE+1];
 202   char buff[STR_L];
 203   ca_dbSource_t *source_hdl;
 204   int read_result;
 205   int parse_result;
 206   ip_addr_t address;
 207 
 208   char *hostaddress=NULL;
 209   sk_conn_st condat;
 210   nrtm_q_t nrtm_q;
 211   long current_serial;
 212   long oldest_serial;
 213   
 214   char *object;
 215   int operation;
 216   
 217   
 218   char *db_host;
 219   int  db_port;
 220   char *db_name;
 221   char *db_user;
 222   char *db_pswd;
 223   int protocol_version;
 224 
 225   GString *gbuff;
 226   
 227   SQ_connection_t *sql_connection;     
 228   int persistent_connection;
 229 
 230   /* make a record for thread accounting */
 231   TA_add(sock, "nrtm_srv");
 232 
 233   
 234   /* Get the IP of the client */
 235   hostaddress = SK_getpeername(sock);
 236   
 237   /* initialise the connection structure */
 238   memset( &condat, 0, sizeof(sk_conn_st));
 239   /* initialise the nrtm structure */
 240   memset( &nrtm_q, 0, sizeof(nrtm_q_t));
 241   /* set the connection data: both rIP and eIP to real IP */
 242   condat.sock = sock;
 243   condat.ip = hostaddress;
 244   SK_getpeerip(sock, &(condat.rIP));
 245   memcpy( &(condat.eIP), &(condat.rIP), sizeof(ip_addr_t));
 246 
 247 
 248   /* Read input */
 249   read_result = SK_cd_gets(&(condat), input, MAX_PM_INPUT_SIZE);
 250     
 251   /* read_result < 0 is an error and connection should be closed */
 252   if (read_result < 0 ) {
 253       /* log the fact, rtc was set */
 254   }
 255 
 256     
 257   parse_result = parse_request(input, &nrtm_q);
 258 
 259   
 260   if (parse_result < 0 ) {
 261       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Garbage received: %s", hostaddress, input);
 262       /* log the fact and exit */
 263       /* Free the hostaddress */
 264       sprintf(buff, "\n%%ERROR:405: syntax error\n\n\n");
 265       SK_cd_puts(&condat, buff);
 266       SK_cd_close(&(condat));
 267       free(hostaddress);
 268       free(nrtm_q.source);
 269       return;
 270   }
 271   
 272   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] -- input: [%s]", hostaddress, input); 
 273   
 274   /* this is -q sources query  - answer and return */    
 275   if (IS_Q_QUERY(parse_result)) {
 276           
 277       gbuff=PM_get_nrtm_sources(&(condat.rIP), nrtm_q.source);
 278       SK_cd_puts(&condat, gbuff->str);
 279       /* end-of-result one extra line (2 in total) */
 280       SK_cd_puts(&condat, "\n");
 281       /* Free allocated memory  */
 282       g_string_free(gbuff, TRUE);
 283       free(hostaddress);
 284       free(nrtm_q.source);
 285       SK_cd_close(&(condat));
 286       return;
 287   }
 288   else if(IS_G_QUERY(parse_result)){
 289      if(IS_PERSISTENT(parse_result))persistent_connection=1; else persistent_connection=0;
 290   }
 291   else {
 292       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Syntax error: %s", hostaddress, input);
 293       /* log the fact and exit */
 294       /* Free the hostaddress */
 295       sprintf(buff, "\n%%ERROR:405: syntax error\n\n\n");
 296       SK_cd_puts(&condat, buff);
 297       SK_cd_close(&(condat));
 298       free(hostaddress);
 299       free(nrtm_q.source);
 300       return;
 301           
 302   }
 303   
 304   /* otherwise this is -g query */
 305   
 306   
 307   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] -- input parsed: %s:%d:%ld-%ld", hostaddress, nrtm_q.source, nrtm_q.version, nrtm_q.first, nrtm_q.last);
 308    
 309   source_hdl = ca_get_SourceHandleByName(nrtm_q.source); 
 310   if (source_hdl == NULL){
 311      ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Unknown source %s", hostaddress, nrtm_q.source);
 312      sprintf(buff, "\n%%ERROR:403: unknown source\n\n\n");
 313      SK_cd_puts(&condat, buff);
 314      free(hostaddress);
 315      free(nrtm_q.source);
 316      SK_cd_close(&(condat));
 317      return;
 318   }
 319          
 320   /* check if the client is authorized to mirror */
 321   SK_getpeerip(sock, &address);
 322   if(!AA_can_mirror(&address, nrtm_q.source)){
 323      ER_inf_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Not authorized to mirror the source %s", hostaddress, nrtm_q.source);
 324      sprintf(buff, "\n%%ERROR:402: not authorized to mirror the database\n\n\n");
 325      SK_cd_puts(&condat, buff);
 326      free(hostaddress);
 327      free(nrtm_q.source);
 328      SK_cd_close(&(condat));
 329      return;
 330   }
 331 
 332   /* get protocol version of the source */
 333   protocol_version = ca_get_srcnrtmprotocolvers(source_hdl);    
 334 
 335 /* XXX this is compatibility mode where we don't care about the protocol version */
 336 #if 0
 337   /* compare to the version requested */
 338   if(nrtm_q.version != protocol_version){
 339      ER_inf_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Source does not support requested protocol %d", hostaddress, nrtm_q.version);
 340      sprintf(buff, "\n%%ERROR:404: version %d of protocol is not supported\n\n\n", nrtm_q.version);
 341      SK_cd_puts(&condat, buff);
 342      free(hostaddress);
 343      free(nrtm_q.source);
 344      SK_cd_close(&(condat));
 345      return;
 346   }
 347 #endif
 348   
 349       
 350   /* get database */
 351   db_name = ca_get_srcdbname(source_hdl);
 352   /* get database host*/
 353   db_host = ca_get_srcdbmachine(source_hdl);      
 354   /* get database port*/
 355   db_port = ca_get_srcdbport(source_hdl);        
 356   /* get database user*/
 357   db_user = ca_get_srcdbuser(source_hdl);          
 358   /* get database password*/
 359   db_pswd = ca_get_srcdbpassword(source_hdl);
 360   
 361   sql_connection = SQ_get_connection(db_host, db_port,db_name, db_user, db_pswd);
 362   if(!sql_connection) {
 363       ER_perror(FAC_PM, PM_NOSQLC," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection));
 364       return;
 365   }
 366   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] --  Made SQL connection to %s@%s", hostaddress, db_name, db_host); 
 367 
 368   /* free copies of the variables */
 369   free(db_host);
 370   free(db_name);
 371   free(db_user);
 372   free(db_pswd);
 373   
 374   /* Not to consume the last serial which may cause crash */                                                                                                          
 375   current_serial=PM_get_current_serial(sql_connection) - SAFE_BACKLOG;
 376   oldest_serial=PM_get_oldest_serial(sql_connection);
 377     
 378   if((current_serial==-1) || (oldest_serial==-1)) {
 379       ER_perror(FAC_PM, PM_NOSERN," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection));
 380       /* Free the hostaddress */
 381       SK_cd_close(&(condat));
 382       /* close the connection to SQL server */
 383       SQ_close_connection(sql_connection); 
 384       free(hostaddress);
 385       free(nrtm_q.source);
 386       return;
 387   }
 388   
 389   /* zero indicates that LAST keyword has been used */    
 390   if(nrtm_q.last==0)nrtm_q.last=current_serial;
 391   /* for persistent connections end of range has no meaning */
 392   if(persistent_connection)nrtm_q.last=current_serial;
 393 
 394     
 395   if((nrtm_q.first>nrtm_q.last) || (nrtm_q.first<oldest_serial) || (nrtm_q.last>current_serial) ||
 396      (nrtm_q.first<=0) || (nrtm_q.last<=0) ) 
 397   {
 398       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Invalid range: %ld-%ld", hostaddress, nrtm_q.first, nrtm_q.last);
 399       /* write error message back to the client */
 400       sprintf(buff, "\n%%ERROR:401: invalid range: Not within %ld-%ld\n\n\n", oldest_serial, current_serial);
 401       SK_cd_puts(&condat, buff);
 402       SK_cd_close(&(condat));
 403       
 404       /* close the connection to SQL server */
 405       SQ_close_connection(sql_connection); 
 406 
 407       /* Free the hostaddress */
 408       free(hostaddress);
 409       free(nrtm_q.source);
 410       return;
 411   }
 412   
 413   current_serial=nrtm_q.first;
 414  
 415   /* print banner */
 416   {
 417   /* get the header string */
 418   char *resp_header = ca_get_pw_resp_header;
 419   SK_cd_puts(&condat, "\n");
 420   SK_cd_puts(&condat, resp_header);
 421   free(resp_header);
 422   SK_cd_puts(&condat, "\n");
 423   } 
 424    
 425   sprintf(buff, "%%START Version: %d %s %ld-%ld\n\n", nrtm_q.version, nrtm_q.source, nrtm_q.first, nrtm_q.last);
 426   SK_cd_puts(&condat, buff);
 427 
 428   /* make a record for thread accounting */
 429   TA_setactivity(buff);
 430 
 431 /*************************** MAIN LOOP ****************************/   
 432 /* now start feeding client with data */    
 433   do {    
 434 
 435     /************ ACTUAL PROCESSING IS HERE ***********/
 436     /* this call will block if queries are paused */
 437     object=PM_get_serial_object(sql_connection, current_serial, &operation);
 438     
 439     /* there is a probability that mirroring interferes with HS cleanup */
 440     /* in such case serial may be deleted before it is read by mirrir client */
 441     /* null object will be returned in this case and we need to break the loop */
 442     if(object==NULL) break;
 443     if (operation == OP_ADD) SK_cd_puts(&condat, "ADD\n\n");
 444     else SK_cd_puts(&condat, "DEL\n\n");
 445     
 446     SK_cd_puts(&condat, object);
 447 
 448     SK_cd_puts(&condat, "\n");
 449       
 450     free(object);
 451     current_serial++;
 452 
 453    /* for real-time mirroring we need some piece of code */
 454     if(persistent_connection && (condat.rtc == 0) )
 455     {
 456         while(((nrtm_q.last = PM_get_current_serial(sql_connection) - SAFE_BACKLOG)<current_serial) 
 457                 && (CO_get_do_server()==1))sleep(1);
 458     }
 459 
 460   } /* do while there are more serials, connection was not reset and XXX do_server is on*/
 461    while((current_serial<=nrtm_q.last) && (condat.rtc == 0) && (CO_get_do_server()==1));
 462 /*******************************************************************/
 463   
 464   sprintf(buff, "%%END %s\n\n\n", nrtm_q.source);
 465   SK_cd_puts(&condat, buff);
 466 
 467   ER_inf_va(FAC_PM, ASP_PM_INPUT,"[%s] -- <%s:%ld-%ld (%ld)> ", 
 468            hostaddress, nrtm_q.source, nrtm_q.first, nrtm_q.last, nrtm_q.last-nrtm_q.first+1); 
 469 
 470   /* make a record for thread accounting */
 471   TA_delete();
 472 
 473   /* close the connection to SQL server */
 474   SQ_close_connection(sql_connection); 
 475   /* Free the hostaddress */
 476   free(hostaddress);
 477   free(nrtm_q.source);
 478 
 479   
 480   
 481 } /* PM_interact() */

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