modules/ud/ud_main.c

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

FUNCTIONS

This source file includes following functions.
  1. get_NRTM_fd
  2. UD_do_nrtm
  3. UD_do_updates

   1 /***************************************
   2   $Revision: 1.36 $
   3 
   4   Wrapper for NRTM client
   5 
   6   Status: NOT REVUED, NOT TESTED
   7 
   8  Author(s):       Andrei Robachevsky
   9 
  10   ******************/ /******************
  11   Modification History:
  12         andrei (17/01/2000) Created.
  13   ******************/ /******************
  14   Copyright (c) 2000,2001                         RIPE NCC
  15  
  16   All Rights Reserved
  17   
  18   Permission to use, copy, modify, and distribute this software and its
  19   documentation for any purpose and without fee is hereby granted,
  20   provided that the above copyright notice appear in all copies and that
  21   both that copyright notice and this permission notice appear in
  22   supporting documentation, and that the name of the author not be
  23   used in advertising or publicity pertaining to distribution of the
  24   software without specific, written prior permission.
  25   
  26   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  27   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  28   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  29   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  30   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  31   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  32  ***************************************/
  33 
  34 #include "rip.h"
  35 
  36 #include <sys/types.h>
  37 #include <sys/socket.h>
  38 #include <netinet/in.h>
  39 #include <arpa/inet.h>
  40 #include <fcntl.h>
  41 #include <signal.h>
  42 
  43 
  44 /* here we store sockets for update threads */
  45 /* they are from SV module */
  46 extern int SV_update_sock[];
  47 
  48 /* Response time to swtching updates on and off */
  49 #define TIMEOUT 60 
  50 
  51 /* timeout between successive attempts to establish connection with server */
  52 #define PM_CONNECTION_TIMEOUT 10 
  53 
  54 /* Maximum number of objects(serials) we can consume at a time */
  55 #define SBUNCH 1000
  56 
  57 /* Timeout in seconds when reading (writing) from DBupdate */
  58 #define STREAM_TIMEOUT 120
  59 
  60 /************************************************************
  61 * int get_NRTM_fd()                                         *
  62 *                                                           *
  63 * Gets the NRTM stream                                      *
  64 *                                                           *
  65 * First tries to request the serials from the NRTM server   *
  66 * If the name of the server appears to be not a network name*
  67 * it tries to open the file with this name                  *
  68 *                                                           *
  69 * nrtm - pointer to _nrtm structure                         *
  70 * upto_last - if==1 then requests to download serials using *
  71 * LAST keyword                                              *
  72 *                                                           *
  73 * Returns:                                                  *
  74 * A file descriptor for a data stream                       *
  75 * -1 - error                                                *
  76 *                                                           *
  77 ************************************************************/
  78 int get_NRTM_fd(struct _nrtm *nrtm, int upto_last, char *source)
     /* [<][>][^][v][top][bottom][index][help] */
  79 {
  80 int sockfd;
  81 struct hostent *hptr;
  82 struct sockaddr_in serv_addr;
  83 struct in_addr *paddr;
  84 char host_info[STR_XXL];
  85 GString *line_buff;
  86 int fd;
  87 int ret;
  88 struct hostent result;
  89 int error;
  90 int network;
  91 
  92 /* fprintf(stderr, "Making connection to NRTM server ...\n");*/
  93  if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1){
  94    ER_perror(FAC_UD, UD_FS, "cannot create socket");
  95    return(-1);
  96  }  
  97 #ifdef __linux__
  98  if(gethostbyname_r(nrtm->server,  &result, host_info, sizeof(host_info), &hptr, &error)<0)  hptr=NULL;
  99 #else/* default is Solaris implementation */
 100  hptr=gethostbyname_r(nrtm->server,  &result, host_info, sizeof(host_info), &error);
 101 #endif
 102 
 103  /* Check if it is a network stream or a file */
 104  if (hptr) { /* this is a network stream*/
 105    paddr=(struct in_addr *)hptr->h_addr;
 106    bzero(&serv_addr, sizeof(serv_addr));
 107    serv_addr.sin_family=AF_INET;
 108    serv_addr.sin_port=nrtm->port;
 109    memcpy(&serv_addr.sin_addr, paddr, sizeof(struct in_addr));
 110 /*   fprintf(stderr,"Trying %s port %d\n", inet_ntoa(serv_addr.sin_addr), nrtm->port);*/
 111    if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))==-1) {
 112      ER_perror(FAC_UD, UD_FS, "cannot connect"); 
 113      close(sockfd);
 114      return(-1);
 115    }  
 116 /*   fprintf(stderr, "Sending Invitation\n"); */
 117    
 118    /* Request all available serials (upto LAST), or SBUNCH of them */
 119    line_buff = g_string_sized_new(STR_L);
 120    if(upto_last==1)
 121       g_string_sprintf(line_buff, "-g %s:%d:%ld-LAST\n", source, nrtm->version, nrtm->current_serial+1);
 122    else if(upto_last==-1) /* persistent mirroring */
 123       g_string_sprintf(line_buff, "-k -g %s:%d:%ld-LAST\n", source, nrtm->version, nrtm->current_serial+1);
 124    else
 125       g_string_sprintf(line_buff, "-g %s:%d:%ld-%ld\n", source, nrtm->version, nrtm->current_serial+1, nrtm->current_serial+SBUNCH);   
 126    ret=SK_write(sockfd, line_buff->str, line_buff->len, NULL, NULL );
 127    g_string_free(line_buff, TRUE);
 128    if(ret != 1) { 
 129            ER_perror(FAC_UD, UD_FS, "cannot write");
 130            close(sockfd);
 131            return(-1); 
 132    }
 133    fd=sockfd;
 134    network=1;
 135 /*   fprintf(stderr, "Returning stream pointer\n"); */
 136  }
 137  else { /* this is a file stream*/
 138    network=0;
 139    close(sockfd);
 140 /*   fprintf(stderr, "Trying file ...\n");*/
 141    if((fd=open(nrtm->server, O_RDONLY, 0666))==-1) {
 142       ER_perror(FAC_UD, UD_FS, "cannot open");     
 143       return(-1);
 144    }  
 145  }  
 146  return(fd);
 147 } 
 148 
 149 
 150 
 151 /************************************************************
 152 *  void UD_do_nrtm()                                        *
 153 *                                                           *
 154 * Processes NRTM stream                                     *
 155 *                                                           *
 156 * It cycles requesting objects from the NRTM server,        * 
 157 * processing them and then sleeping a specified amount of   *
 158 * time.                                                     *
 159 *                                                           *
 160 * It starts by requesting SBUNCH number of serials and does *
 161 * so untill no serials are received (actually a warning     *
 162 * is received saying that the requested range is invalid)   *
 163 * This approach avoids excessive load on the NRTM server    *
 164 *                                                           *
 165 * After that it requests serials using LAST keyward keeping *
 166 * almost in sync with the server                            *
 167 *                                                           *
 168 ************************************************************/
 169  
 170 void UD_do_nrtm(void *arg)
     /* [<][>][^][v][top][bottom][index][help] */
 171 {
 172 int source = (int)arg;
 173 UD_stream_t ud_stream;
 174 struct _nrtm *nrtm;
 175 int nrtm_delay;
 176 int do_update=1;
 177 int do_server;
 178 int nrtm_fd;
 179 int num_ok;
 180 int upto_last;
 181 GString *ta_activity;
 182 ca_dbSource_t *source_hdl = ca_get_SourceHandleByPosition(source);
 183 char *db_host, *db_name, *db_user, *db_passwd;
 184 int db_port;
 185 /* get source we are going to mirror */
 186 char *source_name = ca_get_srcname(source_hdl);  
 187 
 188   { /* set up the lohgging path */
 189    int res;
 190    char *er_ud_def = ca_get_er_ud_def; /* something like 'RIPUPDLOG basename' */
 191    GString *er_def;
 192    char *erret = NULL;
 193 
 194    er_def = g_string_sized_new(256);
 195    g_string_sprintf(er_def, "%s %s", er_ud_def, source_name);
 196    fprintf(stderr, "[%s]\n", er_def->str);
 197    if( (res = ER_macro_spec(er_def->str, &erret)) != 0 ) {
 198         fputs(erret, stderr);
 199         die;
 200         /* or some other error handling */
 201    }     
 202    UT_free(erret); /* the response is allocated and must be freed */
 203    g_string_free(er_def, TRUE);
 204    UT_free(er_ud_def);
 205   }  
 206          
 207   nrtm=UT_calloc(1, sizeof(struct _nrtm));
 208   if(nrtm==NULL) {
 209           ER_perror(FAC_UD, UD_MEM, "cannot allocate memory");
 210           die;
 211   }       
 212 /* get mode of operation: protected/unprotected (dummy) */
 213   memset(&ud_stream, 0, sizeof(ud_stream));
 214   ud_stream.source_hdl=source_hdl;
 215   ud_stream.ud_mode=ca_get_srcmode(source_hdl);
 216   nrtm_delay=ca_get_srcnrtmdelay(source_hdl);
 217   /* Zero delay means persistent connection */
 218   if (nrtm_delay==0) ud_stream.ud_mode |= B_PERSIST_MIRR;
 219 
 220   fprintf(stderr, "Mode of operation:\n");
 221   if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n"); 
 222   else fprintf(stderr, "* dummy not allowed\n");
 223   
 224   if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
 225   else if(IS_NRTM_CLNT(ud_stream.ud_mode)) {
 226            
 227            if(IS_PERSIST_MIRR(ud_stream.ud_mode))fprintf(stderr, "* NRTM: persistent conection\n");
 228            else fprintf(stderr, "* NRTM\n");
 229         }   
 230         else fprintf(stderr, "* STATIC\n");
 231   
 232   if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
 233    else fprintf(stderr, "* running as a server\n");
 234   
 235   if(IS_NO_NHR(ud_stream.ud_mode))fprintf(stderr, "* NHR is not maintained\n");
 236    else fprintf(stderr, "* NHR is maintained\n");
 237 
 238 
 239 /* get mirror server */
 240   nrtm->server=ca_get_srcnrtmhost(source_hdl);
 241 
 242   
 243 /* get mirror port */
 244   nrtm->port = htons(ca_get_srcnrtmport(source_hdl));
 245   printf("XXX nrtm_port=%d\n", ntohs(nrtm->port));
 246 
 247 /* get mirror version */
 248   nrtm->version=ca_get_srcnrtmprotocolvers(source_hdl);
 249  
 250 
 251 /* get error log facility */
 252 /*   logfilename=ca_get_srcnrtmlog(source_hdl); */
 253 
 254    db_host = ca_get_srcdbmachine(source_hdl);
 255    db_port = ca_get_srcdbport(source_hdl);
 256    db_name = ca_get_srcdbname(source_hdl);
 257    db_user = ca_get_srcdbuser(source_hdl);
 258    db_passwd = ca_get_srcdbpassword(source_hdl);
 259   
 260 /* Connect to the database */
 261   ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s making SQL connection to %s@%s ...", UD_TAG, db_name, db_host);
 262   ud_stream.db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd);
 263      
 264  
 265   if(! ud_stream.db_connection) {
 266     ER_perror(FAC_UD, UD_SQL, "no connection to SQL server");
 267     die;
 268   }
 269         
 270   ud_stream.num_skip=0;
 271   ud_stream.load_pass=0;
 272   ud_stream.nrtm=nrtm;
 273   
 274   if(IS_PERSIST_MIRR(ud_stream.ud_mode))upto_last=-1; /* the faster the better */
 275   else upto_last=0; /* let's start gradually if the backlog is > SBUNCH (1000) serials*/
 276 
 277 /*+++ main cycle +++*/
 278 
 279  do {
 280   do_update=CO_get_do_update();
 281   if(do_update) {
 282  
 283    /* Check connection to the database and try to reconnect */
 284    if(SQ_ping(ud_stream.db_connection)) {
 285     ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "%s connection to SQL server timed out - reistablishing", UD_TAG);
 286    }
 287 
 288   /* get current serial */
 289    nrtm->current_serial=PM_get_current_serial(ud_stream.db_connection);
 290    
 291    if(nrtm->current_serial == -1) {
 292       ER_perror(FAC_UD, UD_SQL, "cannot obtain current serial: %ld", nrtm->current_serial);
 293      die;
 294    }
 295 
 296    ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "%s connecting to NRTM server [%s:%d] (current serial=%ld)", 
 297                                     UD_TAG, nrtm->server, ntohs(nrtm->port), nrtm->current_serial);
 298 
 299   /* Get file descriptor of the data stream (RPSL format, use mirror reflector to convert if needed)*/
 300     nrtm_fd=get_NRTM_fd(nrtm, upto_last, source_name);
 301     if (nrtm_fd==-1) { 
 302      ER_inf_va(FAC_UD, ASP_UD_OBJ, "%s Cannot open data stream. Trying...", UD_TAG);
 303      SV_sleep(PM_CONNECTION_TIMEOUT);
 304      continue;
 305     }  
 306 
 307    
 308     /* make a record for thread accounting */
 309     TA_add(nrtm_fd, "nrtm_clnt");
 310     ta_activity = g_string_sized_new(STR_M);
 311     g_string_sprintf(ta_activity, "[%s]%ld->", 
 312                      source_name, nrtm->current_serial);
 313     TA_setactivity(ta_activity->str);
 314    
 315    
 316    ud_stream.condat.sock = nrtm_fd;
 317    ud_stream.log.num_ok=0; 
 318    ud_stream.log.num_failed=0;
 319   
 320 
 321    ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s starting processing stream", UD_TAG);
 322 
 323 /***************** process stream ****************/
 324 
 325       num_ok=UD_process_stream(&ud_stream);
 326   
 327 /***************** process stream ****************/
 328   
 329    ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s processing stream finished", UD_TAG);
 330    
 331   /* close the socket of the NRTM stream */
 332    close(ud_stream.condat.sock);
 333    
 334   
 335 
 336    ER_inf_va(FAC_UD, ASP_UD_OBJ, "%s forwarded to serial:%ld", UD_TAG, (nrtm->current_serial+num_ok));
 337    
 338    /* set activity for thread record */
 339    g_string_sprintf(ta_activity, "[%s]->%ld", 
 340                     source_name, (nrtm->current_serial+num_ok));
 341    TA_setactivity(ta_activity->str);
 342    g_string_free(ta_activity, TRUE);
 343 
 344 /* if we are NOT in persistent mode */
 345    if(!IS_PERSIST_MIRR(ud_stream.ud_mode)) {
 346      /* Now we can process serials in normal way (upto LAST)*/ 
 347      if(num_ok==0) upto_last=1;
 348      /* get delay */
 349      nrtm_delay=ca_get_srcnrtmdelay(source_hdl);
 350    } else 
 351      /* we need to delay the next attempt not to have a birst of requests */
 352      nrtm_delay=TIMEOUT; 
 353      /* sleep the delay seconds or untill the shutdown requested */
 354      SV_sleep(nrtm_delay);
 355    
 356   } /* if do_updates */
 357   else SV_sleep(TIMEOUT); 
 358   
 359 
 360   TA_delete();
 361   
 362  } while((do_server=CO_get_do_server()));  /* main cycle */
 363 
 364 /*   fclose(ud_stream.log.logfile);*/
 365    UT_free(source_name);
 366 /* free data associated with nrtm structure */         
 367  if(nrtm) {
 368    UT_free(nrtm->server);
 369    UT_free(nrtm);
 370  }
 371  
 372  /* That's all. Close connection to the DB */ 
 373  SQ_close_connection(ud_stream.db_connection);
 374  UT_free(db_host);
 375  UT_free(db_name);
 376  UT_free(db_user);
 377  UT_free(db_passwd);
 378 
 379  ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s NRTM client stopped", UD_TAG); 
 380 } /* UD_do_nrtm() */
 381 
 382 /************************************************************
 383 *  void UD_do_updates()                                     *
 384 *                                                           *
 385 * Processes updates                                         *
 386 *                                                           *
 387 * It cycles accepting connections and processing them       * 
 388 * (interactive server). This assures that there is only     *
 389 * one write thread per database/source.                     *
 390 *                                                           *
 391 ************************************************************/
 392    
 393 void UD_do_updates(void *arg)
     /* [<][>][^][v][top][bottom][index][help] */
 394 {
 395 int source = (int)arg;
 396 int listening_socket = SV_update_sock[source];
 397 int connected_socket;
 398 UD_stream_t ud_stream;
 399 int do_update=1;
 400 int num_ok;
 401 ca_dbSource_t *source_hdl = ca_get_SourceHandleByPosition(source);
 402 char *db_host, *db_name, *db_user, *db_passwd;
 403 int db_port;
 404 ip_addr_t address;
 405 char *source_name;
 406 
 407    source_name = ca_get_srcname(source_hdl);  
 408 
 409   { /* set up the lohgging path */
 410    /* get source we are going to update */
 411    int res;
 412    char *er_ud_def = ca_get_er_ud_def; /* something like 'RIPUPDLOG basename' */
 413    GString *er_def;
 414    char *erret = NULL;
 415    
 416 
 417    er_def = g_string_sized_new(256);
 418    g_string_sprintf(er_def, "%s %s", er_ud_def, source_name);
 419    if( (res = ER_macro_spec(er_def->str, &erret)) != 0 ) {
 420         fputs(erret, stderr);
 421         die;
 422         /* or some other error handling */
 423    }     
 424    UT_free(erret); /* the response is allocated and must be freed */
 425    g_string_free(er_def, TRUE);
 426    UT_free(er_ud_def);
 427   } 
 428 
 429 /* get mode of operation: protected/unprotected (dummy) */
 430   memset(&ud_stream, 0, sizeof(ud_stream));
 431   ud_stream.source_hdl=source_hdl;
 432   ud_stream.ud_mode=ca_get_srcmode(source_hdl);
 433 
 434   fprintf(stderr, "Mode of operation:\n");
 435   if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n"); 
 436    else fprintf(stderr, "* dummy not allowed\n");
 437   if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
 438    else fprintf(stderr, "* NRTM\n");
 439   if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
 440    else fprintf(stderr, "* running as a server\n");
 441 
 442 
 443 /* get the database */
 444   db_host = ca_get_srcdbmachine(source_hdl);
 445   db_port = ca_get_srcdbport(source_hdl);
 446   db_name = ca_get_srcdbname(source_hdl);
 447   db_user = ca_get_srcdbuser(source_hdl);
 448   db_passwd = ca_get_srcdbpassword(source_hdl);
 449   
 450 /* Connect to the database */
 451   ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s making SQL connection to %s@%s ...", UD_TAG, db_name, db_host);
 452   ud_stream.db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd);
 453    
 454   if(! ud_stream.db_connection) {
 455    ER_perror(FAC_UD, UD_SQL, "no connection to SQL server\n");
 456    die;
 457   }
 458         
 459 
 460   ud_stream.condat.rd_timeout.tv_sec=STREAM_TIMEOUT;
 461   ud_stream.num_skip=0;
 462   ud_stream.load_pass=0;
 463   ud_stream.nrtm=NULL;
 464  
 465 /*+++ main cycle +++*/
 466 
 467 do { /* be alive while do_server is 1. do_server is turned off by SIGINT */
 468  
 469    /* make a record for thread accounting */
 470    TA_add(listening_socket, "update");
 471    TA_setactivity("waiting");
 472  
 473    /* accept connection */
 474    connected_socket = SK_accept_connection(listening_socket);
 475    if(connected_socket==-1) continue;
 476    
 477    /* check if the client is authorised to update */
 478    SK_getpeerip(connected_socket, &address);
 479    if(!AA_can_ripupdate(&address, source_name)){
 480    char *hostaddress;
 481    sk_conn_st condat;
 482    char buff[STR_L];
 483     
 484     memset( &condat, 0, sizeof(sk_conn_st));
 485     condat.wr_timeout.tv_sec=STREAM_TIMEOUT;
 486     condat.sock = connected_socket;
 487     SK_getpeerip(connected_socket, &(condat.rIP));
 488     memcpy( &(condat.eIP), &(condat.rIP), sizeof(ip_addr_t));
 489     hostaddress = SK_getpeername(connected_socket);
 490     condat.ip = hostaddress;
 491     
 492     ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%s] --  Not authorized to update the source %s", hostaddress, source_name);
 493     sprintf(buff, "\n%%ERROR:406: not authorized to update the database\n\n\n");
 494     SK_cd_puts(&condat, buff);
 495     SK_cd_close(&(condat));
 496     
 497     UT_free(hostaddress);
 498     /* start the next loop */
 499     continue;
 500    }
 501     
 502    /* make a record for thread accounting */
 503    TA_delete(); /* Delete 'waiting' record */
 504    TA_add(connected_socket, "update");
 505 
 506 
 507    ud_stream.condat.sock = connected_socket;
 508    ud_stream.condat.rtc = 0;
 509 
 510    
 511    ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s Connection accepted...", UD_TAG);
 512   
 513    ud_stream.log.num_ok=0; 
 514    ud_stream.log.num_failed=0;
 515  
 516   
 517    while((do_update=CO_get_do_update())!=1) {
 518            TA_setactivity("suspended");
 519            /* if shutdown was requested - break */
 520            if(SV_sleep(3*TIME_SLICE)) break;
 521    }
 522 
 523    if(do_update) {
 524      /* Check connection to the database and try to reconnect*/
 525      if(SQ_ping(ud_stream.db_connection)) {
 526        ER_perror(FAC_UD, UD_SQL, "%s", SQ_error(ud_stream.db_connection));
 527        die;
 528      }
 529 
 530      ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s starting processing object", UD_TAG);
 531   
 532      /***************** process stream ****************/
 533 
 534       num_ok=UD_process_stream(&ud_stream);
 535 
 536      /***************** process stream ****************/    
 537   
 538       ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s processing object finished", UD_TAG);
 539   
 540      /* close the socket of the NRTM stream */
 541      close(ud_stream.condat.sock);
 542     
 543    }  /* if do_update*/
 544 
 545    
 546    TA_delete();
 547 
 548 } while (CO_get_do_server());  /* main cycle */
 549   
 550 /*   fclose(ud_stream.log.logfile); */
 551  /* That's all. Close connection to the DB */ 
 552  SQ_close_connection(ud_stream.db_connection);
 553  UT_free(db_host);
 554  UT_free(db_name);
 555  UT_free(db_user);
 556  UT_free(db_passwd);
 557  UT_free(source_name);
 558 
 559 
 560  ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "%s update server stopped", UD_TAG);
 561 } /* UD_do_update() */
 562 
 563 
 564 
 565 

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