1 | /***************************************
2 | $Revision: 1.17 $
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 "ud.h"
40 | #include "ud_int.h"
41 | #include "constants.h"
42 |
43 | #include "server.h"
44 | #include "protocol_mirror.h"
45 | #include "ta.h"
46 |
47 | /* here we store sockets for update threads */
48 | /* they are from SV module */
49 | extern int SV_update_sock[];
50 |
51 | /* Response time to swtching updates on and off */
52 | #define TIMEOUT 60
53 | /* Maximum number of objects(serials) we can consume at a time */
54 | #define SBUNCH 1000
55 |
56 | /************************************************************
57 | * int get_NRTM_fd() *
58 | * *
59 | * Gets the NRTM stream *
60 | * *
61 | * First tries to request the serials from the NRTM server *
62 | * If the name of the server appears to be not a network name*
63 | * it tries to open the file with this name *
64 | * *
65 | * nrtm - pointer to _nrtm structure *
66 | * upto_last - if==1 then requests to download serials using *
67 | * LAST keyword *
68 | * *
69 | * Returns: *
70 | * A file descriptor for a data stream *
71 | * -1 - error *
72 | * *
73 | ************************************************************/
74 | int get_NRTM_fd(struct _nrtm *nrtm, int upto_last, char *source)
75 | {
76 | int sockfd;
77 | struct hostent *hptr;
78 | struct sockaddr_in serv_addr;
79 | struct in_addr *paddr;
80 | char line_buff[STR_XXL];
81 | int fd;
82 | int nwrite;
83 | struct hostent result;
84 | int error;
85 | int network;
86 |
87 |
88 | fprintf(stderr, "Making connection to NRTM server ...\n");
89 | if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1){
90 | perror("socket");
91 | return(-1);
92 | }
93 | /* hptr=gethostbyname(nrtm->server);*/
94 | hptr=gethostbyname_r(nrtm->server, &result, line_buff, sizeof(line_buff), &error);
95 |
96 | /* Check if it is a network stream or a file */
97 | if (hptr) { /* this is a network stream*/
98 | paddr=(struct in_addr *)hptr->h_addr;
99 | bzero(&serv_addr, sizeof(serv_addr));
100 | serv_addr.sin_family=AF_INET;
101 | serv_addr.sin_port=nrtm->port;
102 | memcpy(&serv_addr.sin_addr, paddr, sizeof(struct in_addr));
103 | fprintf(stderr,"Trying %s port %d\n", inet_ntoa(serv_addr.sin_addr), nrtm->port);
104 | if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))==-1) {
105 | perror("connect");
106 | return(-1);
107 | }
108 | fprintf(stderr, "Sending Invitation\n");
109 |
110 | /* Request all available serials (upto LAST), or SBUNCH of them */
111 | if(upto_last)
112 | sprintf(line_buff, "-g %s:%d:%ld-LAST\n", source, nrtm->version, nrtm->current_serial+1);
113 | else
114 | sprintf(line_buff, "-g %s:%d:%ld-%ld\n", source, nrtm->version, nrtm->current_serial+1, nrtm->current_serial+SBUNCH);
115 | nwrite=SK_write(sockfd, line_buff, strlen(line_buff) );
116 | if(nwrite != strlen(line_buff)) { perror("write"); return(-1); }
117 | fd=sockfd;
118 | network=1;
119 | fprintf(stderr, "Returning stream pointer\n");
120 | }
121 | else { /* this is a file stream*/
122 | network=0;
123 | close(sockfd);
124 | fprintf(stderr, "Trying file ...\n");
125 | if((fd=open(nrtm->server, O_RDONLY, 0666))==-1) {
126 | perror("open");
127 | return(-1);
128 | }
129 | }
130 | return(fd);
131 | }
132 |
133 | /************************************************************
134 | * void UD_do_nrtm() *
135 | * *
136 | * Processes NRTM stream *
137 | * *
138 | * It cycles requesting objects from the NRTM server, *
139 | * processing them and then sleeping a specified amount of *
140 | * time. *
141 | * *
142 | * It starts by requesting SBUNCH number of serials and does *
143 | * so untill no serials are received (actually a warning *
144 | * is received saying that the requested range is invalid) *
145 | * This approach avoids excessive load on the NRTM server *
146 | * *
147 | * After that it requests serials using LAST keyward keeping *
148 | * almost in sync with the server *
149 | * *
150 | ************************************************************/
151 |
152 | void UD_do_nrtm(void *arg)
153 | {
154 | int source = (int)arg;
155 | UD_stream_t ud_stream;
156 | struct _nrtm *nrtm;
157 | int delay;
158 | int do_update=1;
159 | int do_server;
160 | char *logfilename;
161 | FILE *file;
162 | int nrtm_fd;
163 | int num_ok;
164 | int upto_last;
165 | char ta_activity[STR_M];
166 | ca_dbSource_t *source_hdl = ca_get_SourceHandleByPosition(source);
167 | char *db_host, *db_name, *db_user, *db_passwd;
168 | int db_port;
169 | char *source_name;
170 |
171 |
172 |
173 | nrtm=calloc(1, sizeof(struct _nrtm));
174 | if(nrtm==NULL) {
175 | printf("Cannot allocate memory\n");
176 | die;
177 | }
178 | /* get mode of operation: protected/unprotected (dummy) */
179 | ud_stream.source_hdl=source_hdl;
180 | ud_stream.ud_mode=ca_get_srcmode(source_hdl);
181 |
182 | fprintf(stderr, "Mode of operation:\n");
183 | if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n");
184 | else fprintf(stderr, "* dummy not allowed\n");
185 | if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
186 | else fprintf(stderr, "* NRTM\n");
187 | if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
188 | else fprintf(stderr, "* running as a server\n");
189 |
190 | /* get mirror server */
191 | nrtm->server=ca_get_srcnrtmhost(source_hdl);
192 |
193 |
194 | /* get mirror port */
195 | nrtm->port = htons(ca_get_srcnrtmport(source_hdl));
196 | printf("XXX nrtm_port=%d\n", ntohs(nrtm->port));
197 |
198 | /*
199 | if(nrtm->port == -1) {
200 | printf("Invalid service/port: %d\n", nrtm->port);
201 | return;
202 | }
203 | */
204 |
205 | /* get mirror version */
206 | nrtm->version=ca_get_srcnrtmprotocolvers(source_hdl);
207 |
208 | /* get source we are going to mirror */
209 | source_name = ca_get_srcname(source_hdl);
210 |
211 |
212 | /* get error log facility */
213 | logfilename=ca_get_srcnrtmlog(source_hdl);
214 |
215 | db_host = ca_get_srcdbmachine(source_hdl);
216 | db_port = ca_get_srcdbport(source_hdl);
217 | db_name = ca_get_srcdbname(source_hdl);
218 | db_user = ca_get_srcdbuser(source_hdl);
219 | db_passwd = ca_get_srcdbpassword(source_hdl);
220 |
221 | /* Connect to the database */
222 | fprintf(stderr, "D: Making SQL connection to %s@%s ...", db_name, db_host);
223 | ud_stream.db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd);
224 |
225 |
226 | if(! ud_stream.db_connection) {
227 | fprintf(stderr, "D: ERROR: no SQL connection\n");
228 | return;
229 | }
230 |
231 | fprintf(stderr, "OK\n");
232 |
233 | ud_stream.num_skip=0;
234 | ud_stream.load_pass=0;
235 | ud_stream.nrtm=nrtm;
236 | ud_stream.log.logfile = fopen(logfilename, "a+");
237 | if(!ud_stream.log.logfile){
238 | fprintf(stderr, "D: ERROR: cannot open log file %s\n", logfilename);
239 | return;
240 | }
241 |
242 | free(db_host);
243 | free(db_name);
244 | free(db_user);
245 | free(db_passwd);
246 | free(logfilename);
247 |
248 |
249 | upto_last=0; /* let's start gradually if the backlog is > SBUNCH (1000) serials*/
250 |
251 | /*+++ main cycle +++*/
252 |
253 | do {
254 | do_update=CO_get_do_update();
255 | if(do_update) {
256 |
257 |
258 | /* get current serial */
259 | nrtm->current_serial=PM_get_current_serial(ud_stream.db_connection);
260 |
261 | if(nrtm->current_serial == -1) {
262 | fprintf(stderr, "D: ERROR: Error obtaining current serial: %ld\n", nrtm->current_serial);
263 | return;
264 | }
265 |
266 | fprintf(stderr, "current_serial:\t%ld\n", nrtm->current_serial);
267 | fprintf(stderr, "conecting to server...\n");
268 |
269 | /* Get file descriptor of the data stream (RPSL format, use mirror reflector to convert if needed)*/
270 | nrtm_fd=get_NRTM_fd(nrtm, upto_last, source_name);
271 |
272 | /* make a record for thread accounting */
273 | TA_add(nrtm_fd, "nrtm_clnt");
274 | sprintf(ta_activity,"[%s]%ld->", source_name, nrtm->current_serial);
275 | TA_setactivity(ta_activity);
276 | file=fdopen(nrtm_fd, "r+");
277 |
278 |
279 | fprintf(stderr, "OK\n");
280 | printf("OK\n");
281 |
282 |
283 | if (file==NULL) {
284 | fprintf(stderr, "Cannot open data stream. Trying...\n");
285 | sleep(100);
286 | continue;
287 | }
288 |
289 |
290 | ud_stream.stream=file;
291 | ud_stream.log.num_ok=0;
292 | ud_stream.log.num_failed=0;
293 |
294 |
295 | fprintf(stderr, "starting processing stream\n");
296 |
297 | num_ok=UD_process_stream(&ud_stream);
298 |
299 |
300 | /*Check for errors */
301 | if(num_ok<0) {
302 | fprintf(stderr, "processing stream failed\n");
303 | do_server=0;
304 | break;
305 | }
306 | else fprintf(stderr, "processing stream finished\n");
307 |
308 | /* Now we can process serials in normal way (upto LAST)*/
309 | if(num_ok==0) upto_last=1;
310 |
311 | fprintf(ud_stream.log.logfile, "forwarded to serial:\t%ld\n", (nrtm->current_serial+num_ok));
312 | fflush(ud_stream.log.logfile);
313 | fprintf(stderr, "forwarded to serial:\t%ld\n", (nrtm->current_serial+num_ok));
314 | printf("Objects received: %d\n-----------\n", num_ok);
315 |
316 | /* set activity for thread record */
317 | sprintf(ta_activity,"[%s]->%ld", source_name, (nrtm->current_serial+num_ok));
318 | TA_setactivity(ta_activity);
319 |
320 |
321 | /* get delay */
322 | delay=ca_get_srcnrtmdelay(source_hdl);
323 | SV_sleep(LOCK_SHTDOWN, delay);
324 | } /* if do_updates */
325 | else SV_sleep(LOCK_SHTDOWN, TIMEOUT);
326 |
327 | do_server=CO_get_do_server();
328 | TA_delete();
329 |
330 | } while(do_server); /* main cycle */
331 |
332 | fclose(ud_stream.log.logfile);
333 | free(source_name);
334 | /* free data associated with nrtm structure */
335 | if(nrtm) {
336 | free(nrtm->server);
337 | free(nrtm);
338 | }
339 |
340 | /* That's all. Close connection to the DB */
341 | SQ_close_connection(ud_stream.db_connection);
342 | fprintf(stderr, "NRTM stopped\n");
343 |
344 | } /* UD_do_nrtm() */
345 |
346 | /************************************************************
347 | * void UD_do_updates() *
348 | * *
349 | * Processes updates *
350 | * *
351 | * It cycles accepting connections and processing them *
352 | * (interactive server). This assures that there is only *
353 | * one write thread per database/source. *
354 | * *
355 | ************************************************************/
356 |
357 | void UD_do_updates(void *arg)
358 | {
359 | int source = (int)arg;
360 | int listening_socket = SV_update_sock[source];
361 | int connected_socket;
362 | UD_stream_t ud_stream;
363 | int do_update=1;
364 | int do_server;
365 | char *logfilename;
366 | FILE *file, *file_ack;
367 | int num_ok;
368 | ca_dbSource_t *source_hdl = ca_get_SourceHandleByPosition(source);
369 | char *db_host, *db_name, *db_user, *db_passwd;
370 | int db_port;
371 |
372 |
373 |
374 | /* get mode of operation: protected/unprotected (dummy) */
375 | /* ud_stream.ud_mode=CO_get_update_mode(); */
376 | ud_stream.source_hdl=source_hdl;
377 | ud_stream.ud_mode=ca_get_srcmode(source_hdl);
378 |
379 | fprintf(stderr, "Mode of operation:\n");
380 | if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n");
381 | else fprintf(stderr, "* dummy not allowed\n");
382 | if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
383 | else fprintf(stderr, "* NRTM\n");
384 | if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
385 | else fprintf(stderr, "* running as a server\n");
386 |
387 |
388 | /* get error log facility */
389 | logfilename=ca_get_srcnrtmlog(source_hdl);
390 | db_host = ca_get_srcdbmachine(source_hdl);
391 | db_port = ca_get_srcdbport(source_hdl);
392 | db_name = ca_get_srcdbname(source_hdl);
393 | db_user = ca_get_srcdbuser(source_hdl);
394 | db_passwd = ca_get_srcdbpassword(source_hdl);
395 |
396 | /* Connect to the database */
397 | fprintf(stderr, "D: Making SQL connection to %s@%s ...", db_name, db_host);
398 |
399 | /* ud_stream.db_connection=SQ_get_connection2(); */
400 | ud_stream.db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd);
401 |
402 | if(! ud_stream.db_connection) {
403 | fprintf(stderr, "D: ERROR: no SQL connection\n");
404 | return;
405 | }
406 |
407 | fprintf(stderr, "OK\n");
408 |
409 | ud_stream.num_skip=0;
410 | ud_stream.load_pass=0;
411 | ud_stream.nrtm=NULL;
412 | ud_stream.log.logfile = fopen(logfilename, "a+");
413 | if(!ud_stream.log.logfile){
414 | fprintf(stderr, "D: ERROR: cannot open log file %s\n", logfilename);
415 | return;
416 | }
417 |
418 | free(db_host);
419 | free(db_name);
420 | free(db_user);
421 | free(db_passwd);
422 | free(logfilename);
423 |
424 |
425 | /*+++ main cycle +++*/
426 |
427 | do { /* be alive while do_server is 1. do_server is turned off by SIGINT */
428 |
429 | /* make a record for thread accounting */
430 | TA_add(listening_socket, "update");
431 | TA_setactivity("waiting");
432 |
433 |
434 | /* accept connection */
435 | connected_socket = SK_accept_connection(listening_socket);
436 | if(connected_socket==-1) break;
437 |
438 |
439 | /* make a record for thread accounting */
440 | TA_delete(); /* Delete 'waiting' record */
441 | TA_add(connected_socket, "update");
442 |
443 | file=fdopen(connected_socket, "r");
444 | file_ack=fdopen(connected_socket, "w");
445 |
446 | do_update=CO_get_do_update();
447 | if(do_update) {
448 |
449 | TA_setactivity("suspended");
450 |
451 | fprintf(stderr, "Connection accepted...\n");
452 |
453 | if ((file==NULL) || (file_ack==NULL)) {
454 | fprintf(stderr, "Cannot open data stream. Closing connction\n");
455 | return;
456 | }
457 | fprintf(stderr, "Connection accepted...\n");
458 |
459 | ud_stream.stream=file;
460 | ud_stream.log.num_ok=0;
461 | ud_stream.log.num_failed=0;
462 |
463 |
464 | fprintf(stderr, "starting processing object\n");
465 |
466 | num_ok=UD_process_stream(&ud_stream);
467 |
468 | fprintf(stderr, "processing object finished\n");
469 |
470 | if(num_ok==1) {
471 | fprintf(file_ack, "%%ERROR 0\n");
472 | fprintf(stderr, "%%ERROR 0\n");
473 | }
474 | else {
475 | num_ok=(-1)*num_ok;
476 | fprintf(file_ack, "%%ERROR %d\n",num_ok);
477 | fprintf(stderr, "%%ERROR %d\n",num_ok);
478 | fprintf(file_ack, "Transaction had the following problems:\n");
479 | if(num_ok & ERROR_U_MEM) fprintf(file_ack, "Memory allocation error\n");
480 | /* if(num_ok & ERROR_U_DBS) fprintf(file_ack, "Database (SQL) error\n");*/
481 | /* if(num_ok & ERROR_U_OBJ) fprintf(file_ack, "Object (RF) error\n");*/
482 | /* if(num_ok & ERROR_U_AUT) fprintf(file_ack, "Object authentication error\n");*/
483 | if(num_ok & ERROR_U_BADOP) fprintf(file_ack, "Bad operation\n");
484 | if(num_ok & ERROR_U_COP) fprintf(file_ack, "Conflicting operation\n");
485 | if(num_ok & ERROR_U_NSUP) fprintf(file_ack, "Object of this type is not supported\n");
486 | if(num_ok & ERROR_U_BUG) fprintf(file_ack, "Software bug - report to <ripe-dbm@ripe.net>\n");
487 | }
488 | if(ud_stream.error_script)fprintf(file_ack, "%s\n", ud_stream.error_script);
489 |
490 | if(ud_stream.error_script) free(ud_stream.error_script);
491 |
492 | fflush(file_ack); fclose(file_ack);
493 | fclose(file);
494 | } /* if do_update*/
495 | else { /* Otherwise print a message*/
496 | /* To display with 'show threads' */
497 | TA_setactivity("suspended");
498 |
499 | fprintf(file_ack, "%%ERROR 1000\n%%Updates are suspended\n");
500 | fflush(file_ack); fclose(file_ack);
501 | fclose(file);
502 | }
503 | /* make a record for thread accounting */
504 | TA_delete();
505 |
506 | do_server=CO_get_do_server();
507 |
508 | } while (do_server); /* main cycle */
509 |
510 | fclose(ud_stream.log.logfile);
511 | /* That's all. Close connection to the DB */
512 | SQ_close_connection(ud_stream.db_connection);
513 |
514 |
515 | fprintf(stderr, "server stopped\n");
516 |
517 | } /* UD_do_update() */
518 |
519 |
520 |
521 |