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