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