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