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 |