-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathniml_stream.c
3195 lines (2482 loc) · 111 KB
/
niml_stream.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "niml_private.h"
/*************************************************************************/
/********************* Functions for NIML I/O ****************************/
/*************************************************************************/
/*! To print a system error message. */
#undef PERROR
#ifdef NIML_DEBUG
# define PERROR(x) perror(x)
#else
# define PERROR(x)
#endif
#include <signal.h> /* signal handler stuff */
#include <fcntl.h> /* file control stuff */
/*! For tcp - indicates that SIGPIPE is ignored;
will be set the first time tcp_send is called. */
static int nosigpipe = 0 ;
/*! For tcp - indicates that the SIGURG handler is installed;
will be set the first time a TCP socket is created. */
static int sigurg = 0 ; /* 02 Jan 2004 */
/*! How to close a socket, given the descriptor ss. */
#define CLOSEDOWN(ss) ( shutdown((ss),2) , close((ss)) )
/*! This is used to set the send/receive buffer size for sockets. */
#define SOCKET_BUFSIZE (63*1024)
/*! This macro is used so I can replace recv() with something else if I want. */
#define tcp_recv recv
/*! This macro is used so I can replace send() with something else if I want. */
#define tcp_send send
#ifndef MIN
/*! Duh. */
# define MIN(a,b) (((a)>(b)) ? (b) : (a))
#endif
/*! Next delay in milliseconds, given current delay. */
#undef NEXTDMS
#define NEXTDMS(dm) MIN(1.1*(dm)+1.01,66.0)
/*-------------------------------------------------------------------*/
/*! Number of entries on the list of currently open streams.
This list is needed so we can deal with the SIGURG signal,
which we use as a message to shut a socket down. The signal
call itself doesn't tell us which socket was the trigger,
so we have to search all the open sockets for a match:
hence, this list of open streams.
---------------------------------------------------------------------*/
static int num_open_streams = 0 ;
/*! The actual array of open NIML streams. */
static NI_stream_type ** open_streams = NULL ;
/*! Signal that we are doing atexit() stuff. */
static volatile int doing_atexit = 0 ; /* 05 May 2005 */
/*-------------------------------------------------------------------*/
/*! Add a stream to the open list. */
static void add_open_stream( NI_stream_type *ns )
{
int nn = num_open_streams ;
if( ns == NULL ) return ; /* bad input */
open_streams = (NI_stream_type **)realloc( (void *)open_streams ,
sizeof(NI_stream_type *)*(nn+1) );
open_streams[nn] = ns ; num_open_streams++ ; return ;
}
/*-------------------------------------------------------------------*/
/*! Remove a stream from the open list. */
static void remove_open_stream( NI_stream_type *ns )
{
int nn = num_open_streams , ii,jj ;
if( doing_atexit || nn <= 0 || ns == NULL ) return ; /* bad input */
for( ii=0 ; ii < nn ; ii++ ) /* find input */
if( open_streams[ii] == ns ) break ;
if( ii == nn ) return ; /* not found!? */
for( jj=ii+1 ; jj < nn ; jj++ ) /* move those above down */
open_streams[jj-1] = open_streams[jj] ;
open_streams[nn-1] = NULL ; num_open_streams-- ; return ;
}
/*------------------------------------------------------------------*/
/*! At program exit, close all open streams. */
static void atexit_open_streams(void) /* 22 Apr 2005 */
{
int ii ;
if( doing_atexit ) return ;
doing_atexit = 1 ;
for( ii=0 ; ii < num_open_streams ; ii++ ){
NI_sleep(2) ;
NI_stream_close_keep( open_streams[ii] , 5 ) ;
}
return ;
}
/*! Variable to indicate that the atexit() call has/hasn't been made */
static int atexit_is_setup = 0 ;
/*------------------------------------------------------------------*/
/*! Signal handler for SIGURG -- for incoming OOB data on a socket.
We just close the NI_stream that the socket is attached to.
But first we have to find it!
--------------------------------------------------------------------*/
static void tcp_sigurg_handler( int sig )
{
int nn = num_open_streams , ii , sd,sdtop ;
NI_stream_type *ns ;
fd_set efds ;
struct timeval tv ;
static volatile int busy=0 ;
if( sig != SIGURG ||
busy ||
num_open_streams <= 0 || open_streams == NULL ) return ; /* bad */
busy = 1 ; /* prevent recursion! */
/* find largest socket descriptor in list of streams,
and make list of all open socket descriptors in streams */
FD_ZERO(&efds) ; sdtop = -1 ;
for( ii=0 ; ii < nn ; ii++ ){
if( open_streams[ii] != NULL &&
open_streams[ii]->bad != MARKED_FOR_DEATH &&
open_streams[ii]->type == NI_TCP_TYPE &&
open_streams[ii]->sd >= 0 ){
FD_SET( open_streams[ii]->sd , &efds ) ;
if( open_streams[ii]->sd > sdtop ) sdtop = open_streams[ii]->sd;
}
}
if( sdtop < 0 ){ busy=0 ; return; } /* no sockets found? */
/* do a select to find which socket has an exceptional condition */
tv.tv_sec = 0 ;
tv.tv_usec = 666 ;
ii = select(sdtop+1, NULL, NULL, &efds, &tv) ; /* check it */
if( ii <= 0 ){ busy=0 ; return; } /* no sockets found? */
/* loop over found sockets and close their streams */
for( ii=0 ; ii < nn ; ii++ ){
if( open_streams[ii] != NULL && open_streams[ii]->type == NI_TCP_TYPE ){
if( FD_ISSET( open_streams[ii]->sd , &efds ) ){
CLOSEDOWN( open_streams[ii]->sd ) ;
open_streams[ii]->bad = MARKED_FOR_DEATH ;
}
}
}
busy=0 ; return ;
}
/********************************************************************
Routines to manipulate TCP/IP stream sockets.
See http://www.manualy.sk/sock-faq/unix-socket-faq.html for info.
*********************************************************************/
/*-------------------------------------------------------------------*/
/*! See if the given socket (file descriptor sd) is ready to read.
msec is the number of milliseconds to wait:
- zero ==> no waiting
- < 0 ==> wait until something happens (not recommended)
Return values are:
- -1 = some error occured (socket closed at other end?)
- 0 = socket is not ready to read
- 1 = socket has data
---------------------------------------------------------------------*/
static int tcp_readcheck( int sd , int msec )
{
int ii ;
fd_set rfds ;
struct timeval tv , *tvp ;
if( sd < 0 ) return -1 ; /* bad socket id */
FD_ZERO(&rfds) ; FD_SET(sd, &rfds) ; /* check only sd */
if( msec >= 0 ){ /* set timer */
tv.tv_sec = msec/1000 ;
tv.tv_usec = (msec%1000)*1000 ;
tvp = &tv ;
} else {
tvp = NULL ; /* forever */
}
ii = select(sd+1, &rfds, NULL, NULL, tvp) ; /* check it */
if( ii == -1 ) PERROR( "tcp_readcheck(select)" ) ;
return ii ;
}
/*-------------------------------------------------------------------*/
/*! See if the given socket (file descriptor sd) is ready to write.
msec = max amount of time to wait, in milliseconds.
- zero ==> no waiting
- < 0 ==> wait until something happens (not recommended)
Return values are
- -1 = some error occured (socket closed at other end?)
- 0 = socket is not ready to write
- 1 = OK to write to socket
---------------------------------------------------------------------*/
static int tcp_writecheck( int sd , int msec )
{
int ii ;
fd_set wfds ;
struct timeval tv , *tvp ;
if( sd < 0 ) return -1 ; /* bad socket id */
FD_ZERO(&wfds) ; FD_SET(sd, &wfds) ; /* check only sd */
if( msec >= 0 ){ /* set timer */
tv.tv_sec = msec/1000 ;
tv.tv_usec = (msec%1000)*1000 ;
tvp = &tv ;
} else {
tvp = NULL ; /* forever */
}
ii = select(sd+1, NULL , &wfds, NULL, tvp); /* check it */
if( ii == -1 ) PERROR( "tcp_writecheck(select)" ) ;
return ii ;
}
/*------------------------------------------------------------------------*/
/*! Set a socket so that it will cutoff quickly when it is closed.
See http://www.manualy.sk/sock-faq/unix-socket-faq.html for more
information about this stuff.
--------------------------------------------------------------------------*/
static void tcp_set_cutoff( int sd )
{
if( sd < 0 ) return ; /* bad input */
#ifdef SO_LINGER
/* Turn off "lingering". */
{ struct linger lg ;
lg.l_onoff = 1 ;
lg.l_linger = 0 ;
setsockopt(sd, SOL_SOCKET, SO_LINGER, (void *)&lg, sizeof(struct linger)) ;
}
#endif
#ifdef SO_REUSEADDR
/* Let the address be reused quickly,
in case of another connection from the same host on the same port. */
{ int optval = 1;
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) ;
}
#endif
return ;
}
/*-------------------------------------------------------------------*/
/*! Check if an already active socket is still alive.
If it is dead, then readcheck will say we can read, but we
won't actually get any bytes when we try (using peek mode).
Returns 1 if things are OK, 0 if not.
---------------------------------------------------------------------*/
static int tcp_alivecheck( int sd )
{
int ii ;
char bbb[4] ;
ii = tcp_readcheck(sd,0) ; /* can I read? */
if( ii == 0 ) return 1 ; /* can't read is OK */
if( ii < 0 ) return 0 ; /* some error is bad */
errno = 0 ;
ii = tcp_recv( sd , bbb , 1 , MSG_PEEK ) ; /* try to read one byte */
if( ii == 1 ) return 1 ; /* if we get it, good */
if( errno ) PERROR("tcp_alivecheck") ;
return 0 ; /* no data ==> death! */
}
/*------------------------------------------------------------------------*/
/*! Open a socket to the given host, to the given TCP port.
This function is used to "reach out" to a server that is supposed
to be listening on the same port.
Returns socket id; if -1, some error occured (e.g., nobody listening).
--------------------------------------------------------------------------*/
static int tcp_connect( char *host , int port )
{
int sd , l , q,qq ;
struct sockaddr_in sin ;
struct hostent *hostp ;
if( host == NULL || port < 1 ) return -1 ; /* bad inputs */
#ifdef NIML_DEBUG
NI_dpr("Enter tcp_connect: host=%s port=%d\n",host,port) ;
#endif
/** open a socket **/
sd = socket( AF_INET , SOCK_STREAM , 0 ) ;
if( sd == -1 ){ PERROR("tcp_connect(socket)"); return -1; }
/** set socket options (no delays, large buffers) **/
#if 0
{ char *eee=getenv( "NIML_TCP_NAGLE" ) ;
if( eee == NULL || toupper(*eee) != 'Y' ){
/** disable the Nagle algorithm **/
l = 1;
setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (void *)&l, sizeof(int)) ;
}
}
#endif
/* but large I/O buffers are good */
#ifdef SOCKET_BUFSIZE
q = 0 ; qq = sizeof(int) ; /* 03 Dec 2002: */
getsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *)&q, &qq ) ; /* only modify */
if( q < SOCKET_BUFSIZE ){ /* if current buffer */
l = SOCKET_BUFSIZE ; /* is too small */
setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *)&l, sizeof(int)) ;
}
q = 0 ; qq = sizeof(int) ;
getsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *)&q, &qq ) ;
if( q < SOCKET_BUFSIZE ){
l = SOCKET_BUFSIZE ;
setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *)&l, sizeof(int)) ;
}
#endif
/** set port on remote computer **/
memset( &sin , 0 , sizeof(sin) ) ;
sin.sin_family = AF_INET ;
sin.sin_port = htons(port) ;
/** set remote computer IP address from its name **/
hostp = gethostbyname(host) ;
if( hostp == NULL ){
PERROR("tcp_connect(gethostbyname)");
#ifdef NIML_DEBUG
NI_dpr(" tcp_connect: can't gethostbyname(); errno=%d\n",errno);
#endif
CLOSEDOWN(sd); return -1;
}
sin.sin_addr.s_addr = ((struct in_addr *)(hostp->h_addr))->s_addr ;
errno = 0 ;
if( connect(sd,(struct sockaddr *)&sin,sizeof(sin)) != 0 ){
if( errno != ECONNREFUSED ) PERROR("tcp_connect(connect)") ;
#ifdef NIML_DEBUG
NI_dpr(" tcp_connect: can't connect(); errno=%d\n",errno);
#endif
CLOSEDOWN(sd); return -1;
}
#ifdef NIML_DEBUG
NI_dpr(" tcp_connect: connected!\n");
#endif
tcp_set_cutoff( sd ) ;
return sd ;
}
/*--------------------------------------------------------------------------*/
/*! Set up to listen for a connection on a given port.
This is intended for use by a server, which will wait for some other
program to actively connect to this port. There is no security here -
connections will be taken from any IP address.
This function does not actually form the connection. That must be done
separately. Whether someone is trying to connect can be checked for
with the routine "tcp_readcheck" and then accepted with "tcp_accept".
The return value is the descriptor for the listening socket.
----------------------------------------------------------------------------*/
static int tcp_listen( int port )
{
int sd , l , q,qq ;
struct sockaddr_in sin ;
char serr[128]={""};
if( port < 1 ) return -1 ; /* bad input */
/** open a socket **/
sd = socket( AF_INET , SOCK_STREAM , 0 ) ;
if( sd == -1 ){
sprintf(serr,"tcp_listen(socket): (Name %s, Port %d)",
get_port_numbered(port), port);
PERROR(serr); return -1;
}
/** set socket options (no delays, large buffers) **/
#if 0
{ char *eee=getenv( "NIML_TCP_NAGLE" ) ;
if( eee == NULL || toupper(*eee) != 'Y' ){
/** disable the Nagle algorithm **/
l = 1;
setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (void *)&l, sizeof(int)) ;
}
}
#endif
/* set socket to have large I/O buffers */
#ifdef SOCKET_BUFSIZE
q = 0 ; qq = sizeof(int) ;
getsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *)&q, &qq ) ;
if( q < SOCKET_BUFSIZE ){
l = SOCKET_BUFSIZE ;
setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *)&l, sizeof(int)) ;
}
q = 0 ; qq = sizeof(int) ;
getsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *)&q, &qq ) ;
if( q < SOCKET_BUFSIZE ){
l = SOCKET_BUFSIZE ;
setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *)&l, sizeof(int)) ;
}
#endif
/** set port on remote computer **/
memset( &sin , 0 , sizeof(sin) ) ;
sin.sin_family = AF_INET ;
sin.sin_port = htons(port) ;
sin.sin_addr.s_addr = INADDR_ANY ; /* reader reads from anybody */
if( bind(sd , (struct sockaddr *)&sin , sizeof(sin)) == -1 ){
sprintf(serr,"tcp_listen(bind) (Name %s, Port %d, sd %d)",
get_port_numbered(port), port, sd);
PERROR(serr); CLOSEDOWN(sd); return -1;
}
if( listen(sd,1) == -1 ){
sprintf(serr,"tcp_listen(listen) (Name %s, Port %d)",
get_port_numbered(port), port);
PERROR(serr); CLOSEDOWN(sd); return -1;
}
tcp_set_cutoff( sd ) ;
return sd ;
}
/*--------------------------------------------------------------------------*/
/*! Accept incoming connection on a socket.
Return value is the attached socket (which is not the original socket!).
If -1 is returned, some error occured. If the accept works, then the
original socket is still open and listening for further attachments.
Under many circumstances, you will want to close the original socket
immediately. This can be done with CLOSEDOWN(sd), where sd is the
input socket.
If hostname is not NULL, then the char * it points to will be filled
with a pointer to the official name of the host that connected.
If hostaddr is not NULL, then the char * it points to will be filled
with a pointer to the Internet address (in 'dot' form) of the host that
connected.
Both the char * pointers returned are from NI_malloc(), and should be
NI_free()-d when no longer needed. If they aren't needed at all, just
pass in NULL for these arguments.
Note that this routine will block until somebody connects. You can
use tcp_readcheck(sd,0) to see if anyone is waiting to connect before
calling this routine.
However, if someone connects and the IP address isn't on the
trusted list, then the connection will be closed immediately.
---------------------------------------------------------------------------*/
static int tcp_accept( int sd , char **hostname , char **hostaddr )
{
struct sockaddr_in pin ;
int addrlen , sd_new ;
struct hostent *hostp ;
char *str ;
/** accept the connection **/
addrlen = sizeof(pin) ;
sd_new = accept( sd , (struct sockaddr *)&pin , &addrlen ) ;
if( sd_new == -1 ){ PERROR("tcp_accept"); return -1; }
/** get dotted form address of connector **/
str = inet_ntoa( pin.sin_addr ) ;
if( !NI_trust_host(str) ){
fprintf(stderr,"\n** ILLEGAL attempt to connect from host %s\n",str) ;
CLOSEDOWN( sd_new ) ;
return -1 ;
}
if( hostaddr != NULL ) *hostaddr = NI_strdup(str) ;
/** get name of connector **/
if( hostname != NULL ){
hostp = gethostbyaddr( (char *) (&pin.sin_addr) ,
sizeof(struct in_addr) , AF_INET ) ;
if( hostp != NULL ) *hostname = NI_strdup(hostp->h_name) ;
else *hostname = NI_strdup("UNKNOWN") ; /* bad lookup */
}
tcp_set_cutoff( sd_new ) ; /* let it die quickly, we hope */
return sd_new ;
}
/*******************************************************************/
/*** Functions to setup a "trusted host" list for TCP/IP accept. ***/
/*******************************************************************/
static int host_num = 0 ; /*!< Number of trusted hosts. */
static char ** host_list = NULL ; /*!< IP addresses in dotted form. */
static char *init_hosts[] = { /*!< Initial list of OK computers */
"127.0.0.1" , /* localhost is always OK */
"192.168." , /* private class B networks */
"128.231.21" /* SSCC/NIMH/NIH/DHHS/USA */
} ;
#define INIT_NHO (sizeof(init_hosts)/sizeof(char *))
#define HSIZE 32
/*----------------------------------------------------------------*/
/*! Return the Internet address (in 'dot' format, as a string)
given the name of the host. If NULL is returned, some
error occurrrrred. The string is NI_malloc()-ed, and should
be NI_free()-ed when no longer needed.
------------------------------------------------------------------*/
char * NI_hostname_to_inet( char *host )
{
struct hostent *hostp ;
char * iname = NULL , *str ;
int ll ;
if( host == NULL || host[0] == '\0' ) return NULL ;
hostp = gethostbyname(host) ; if( hostp == NULL ) return NULL ;
str = inet_ntoa(*((struct in_addr *)(hostp->h_addr))) ;
if( str == NULL || str[0] == '\0' ) return NULL ;
iname = NI_strdup(str) ; return iname ;
}
/*----------------------------------------------------------------*/
/*! Check if hostname is in dotted form.
------------------------------------------------------------------*/
static int hostname_dotted( char *hnam )
{
int ii, nh ;
if( hnam == NULL ) return 0 ;
nh = strlen(hnam) ;
for( ii=0 ; ii < nh ; ii++ )
if( !isdigit(hnam[ii]) && hnam[ii] != '.' ) return 0 ;
return 1 ;
}
/*----------------------------------------------------------------*/
/*! Add a host to the trusted list (internal version).
------------------------------------------------------------------*/
static void add_trusted_host( char *hnam )
{
char *hh=NULL ;
int ii ;
if( hnam == NULL || hnam[0] == '\0' ) return ;
if( !hostname_dotted(hnam) ){ /* not a dotted number */
hh = NI_hostname_to_inet( hnam ) ; /* so do a lookup on it */
if( hh == NULL ) return ; /* failed? */
} else if( strlen(hnam) > HSIZE-1 ){ /* something bad? */
return ;
} else {
hh = hnam ; /* store dotted number */
}
host_list = NI_realloc(host_list, char*,sizeof(char *)*(host_num+1)) ;
host_list[host_num] = NI_malloc(char, HSIZE) ;
strcpy( host_list[host_num] , hh ) ; host_num++ ;
if( hh != hnam ) NI_free(hh) ;
}
/*--------------------------------------------------------------------------*/
/*! Initialize trusted list from the internal table and the environment.
----------------------------------------------------------------------------*/
static void init_trusted_list(void)
{
int ii ;
char ename[HSIZE] , *str ;
if( host_num == 0 ){ /** only execute this once **/
host_num = INIT_NHO ;
host_list = NI_malloc(char*, sizeof(char *) * INIT_NHO ) ;
for( ii=0 ; ii < INIT_NHO ; ii++ ){
host_list[ii] = NI_malloc(char, HSIZE) ;
strcpy( host_list[ii] , init_hosts[ii] ) ;
}
for( ii=0 ; ii <= 99 ; ii++ ){
sprintf(ename,"NIML_TRUSTHOST_%02d",ii) ; str = getenv(ename) ;
if( str == NULL && ii <= 9 ){
sprintf(ename,"NIML_TRUSTHOST_%1d",ii) ; str = getenv(ename) ;
}
if( str == NULL && ii <= 9 ){
sprintf(ename,"NIML_TRUSTHOST_O%1d",ii) ; str = getenv(ename) ;
}
if( str != NULL ) add_trusted_host(str) ;
}
for( ii=0 ; ii <= 99 ; ii++ ){
sprintf(ename,"AFNI_TRUSTHOST_%02d",ii) ; str = getenv(ename) ;
if( str == NULL && ii <= 9 ){
sprintf(ename,"AFNI_TRUSTHOST_%1d",ii) ; str = getenv(ename) ;
}
if( str == NULL && ii <= 9 ){
sprintf(ename,"AFNI_TRUSTHOST_O%1d",ii) ; str = getenv(ename) ;
}
if( str != NULL ) add_trusted_host(str) ;
}
}
return ;
}
/*--------------------------------------------------------------------------*/
/*! Externally callable routine to add a host to the trusted list.
If call with NULL, will just initialize the default trusted
host list.
----------------------------------------------------------------------------*/
void NI_add_trusted_host( char *hostname )
{
if( host_num == 0 ) init_trusted_list() ;
if( hostname == NULL || hostname[0] == '\0' ) return ;
add_trusted_host(hostname) ;
}
/*---------------------------------------------------------------------------*/
/*! Return 1 if we like hostid, 0 if we don't.
-----------------------------------------------------------------------------*/
int NI_trust_host( char *hostid )
{
int ii ;
char *hh = hostid ;
/* if the trusted list is empty,
see if we want to be completely trusting;
if not, then initialize the trusted list and then check */
if( host_num == 0 ){
char *eee = getenv("NIML_COMPLETE_TRUST") ;
if( eee != NULL && toupper(*eee) == 'Y' ) return 1 ; /* complete trust */
init_trusted_list() ;
}
if( hostid == NULL || hostid[0] == '\0' ) return 0 ;
if( !hostname_dotted(hostid) ){
hh = NI_hostname_to_inet(hostid) ; /* will be NI_malloc()-ed */
if( hh == NULL ) return 0 ;
}
/* to be trusted, hostid must start with same
string as something in the trusted host_list array */
for( ii=0 ; ii < host_num ; ii++ ){
if( strstr(hh,host_list[ii]) == hh ){
if( hh != hostid ) NI_free(hh) ;
return 1 ;
}
}
if( hh != hostid ) NI_free(hh) ;
return 0 ;
}
#ifndef DONT_USE_SHM
/****************************************************************
Routines to manipulate IPC shared memory segments for I/O
[adapted from thd_iochan.c, 31 May 2002 -- RWCox]
*****************************************************************/
/*---------------------------------------------------------------*/
/*! Convert a string to a key, for IPC operations.
Augment sum by port offset value (-np option)
-----------------------------------------------------------------*/
static key_t SHM_string_to_key( char *key_string )
{
int ii , sum = get_user_np() ;
key_t kk ;
sum += 987654321 ;
if( key_string == NULL ) return (key_t) sum ;
for( ii=0 ; key_string[ii] != '\0' ; ii++ )
sum += ((int)key_string[ii]) << ((ii%3)*8) ;
kk = (key_t) sum ;
#ifdef IPC_PRIVATE
if( kk == IPC_PRIVATE || kk <= 0 ) kk = 666 ;
#endif
return kk ;
}
/*---------------------------------------------------------------*/
/*! Get a pre-existing shmem segment.
Returns the shmid >= 0 if successful; returns -1 if failure.
-----------------------------------------------------------------*/
static int SHM_accept( char *key_string )
{
key_t key ;
int shmid ;
key = SHM_string_to_key( key_string ) ;
shmid = shmget( key , 0 , 0777 ) ;
return shmid ;
}
/*---------------------------------------------------------------*/
/*! Connect to, or create if needed, a shmem segment.
Returns the shmid >= 0 if successful; returns -1 if failure.
-----------------------------------------------------------------*/
static int SHM_create( char *key_string , int size )
{
key_t key ;
int shmid ;
key = SHM_string_to_key( key_string ) ;
shmid = shmget( key , size , 0777 | IPC_CREAT ) ;
if( shmid < 0 ) PERROR("SHM_create") ;
return shmid ;
}
/*---------------------------------------------------------------*/
/*! Actually attach to the shmem segment.
Returns the pointer to the segment start.
NULL is returned if an error occurs.
-----------------------------------------------------------------*/
static char * SHM_attach( int shmid )
{
char *adr ;
adr = (char *) shmat( shmid , NULL , 0 ) ;
if( adr == (char *) -1 ){ adr = NULL ; PERROR("SHM_attach") ; }
return adr ;
}
/*---------------------------------------------------------------*/
/*! Find the size of a shmem segment.
Returns -1 if an error occurs.
-----------------------------------------------------------------*/
static int SHM_size( int shmid )
{
int ii ;
struct shmid_ds buf ;
if( shmid < 0 ) return -1 ;
ii = shmctl( shmid , IPC_STAT , &buf ) ;
if( ii < 0 ){ PERROR("SHM_size") ; return -1 ; }
return buf.shm_segsz ;
}
/*---------------------------------------------------------------*/
/*! Find the number of attaches to a shmem segment.
Returns -1 if an error occurs.
-----------------------------------------------------------------*/
static int SHM_nattach( int shmid )
{
int ii ;
static struct shmid_ds buf ;
char *eee = getenv( "NIML_DNAME" ) ;
static int nid=0;
if( shmid < 0 ) return -1 ;
ii = shmctl( shmid , IPC_STAT , &buf ) ;
if( ii < 0 ){
if( eee != NULL )
fprintf(stderr,
"SHM_nattach (%s): (shmid=%d, buf.shm_nattach %d, errno %d), trying again!\n"
" EACCES %d, EFAULT %d, EINVAL %d, EPERM %d\n",
eee, shmid, (int)buf.shm_nattch, errno, EACCES, EFAULT, EINVAL, EPERM) ;
NI_sleep(9) ;
ii = shmctl( shmid , IPC_STAT , &buf ) ;
}
if( ii < 0 ){
char *ppp ;
if( eee != NULL ){
ppp = (char *)calloc(1,strlen(eee)+32) ;
strcpy(ppp,"SHM_nattach (") ;
strcat(ppp,eee) ; strcat(ppp,")") ;
} else {
ppp = strdup("SHM_nattach") ;
}
PERROR(ppp);
fprintf(stderr,"%s: called shmctl(%x,%x,%p), got %d\n"
" (shmid=%d, buf.shm_nattach %d, errno %d)\n",
ppp,(unsigned int)shmid, (unsigned int)IPC_STAT, (void *)&buf,
ii,
shmid, (int)buf.shm_nattch, errno ) ;
nid = 0;
free((void *)ppp); return -1;
} else if( eee != NULL ){
if (!nid)
fprintf(stderr,"SHM_nattach (%s): called shmctl(%x,%x,%p), got %d\n"
" Similar messages muted until SHM_nattach fails again.\n",
eee,
(unsigned int)shmid, (unsigned int)IPC_STAT, (void *)&buf,
(int)buf.shm_nattch ) ;
++nid;
}
return (int)buf.shm_nattch ;
}
/*---------------------------------------------------------------*/
/*! Fill a SHMioc struct that has just been attached as an "r".
- ioc->id should be non-negative at this point.
- return value is 1 if things are good, -1 if not.
-----------------------------------------------------------------*/
static int SHM_fill_accept( SHMioc *ioc )
{
char *bbb ;
int jj ;
if( ioc == NULL || ioc->id < 0 ) return -1 ; /* bad inputs? */
NI_sleep(2) ; /* wait a bit */
bbb = SHM_attach( ioc->id ) ; /* attach it */
if( bbb == NULL ) return -1 ; /* can't? quit */
if( SHM_nattach(ioc->id) != 2 ){ /* 2 processes? */
NI_sleep(10) ; /* wait a bit, */
if( SHM_nattach(ioc->id) != 2 ){ /* and try again */
shmdt( bbb ) ; /* this is bad! */
shmctl( ioc->id , IPC_RMID , NULL ) ;
ioc->bad = SHM_IS_DEAD ; return -1 ;
}
}
jj = SHM_size(ioc->id) ; /* shmbuf size */
if( jj <= SHM_HSIZE ){ /* too small? */
shmdt( bbb ) ; /* this is bad! */
shmctl( ioc->id , IPC_RMID , NULL ) ;
ioc->bad = SHM_IS_DEAD ; return -1 ;
}
ioc->shmbuf = bbb ; /* buffer */
ioc->shmhead = (int *) bbb ; /* buffer as int */
ioc->bufsize1 = ioc->shmhead[SHM_SIZE1] ; /* size of buf 1 */
ioc->bstart1 = ioc->shmhead + SHM_BSTART1 ; /* start marker 1*/
ioc->bend1 = ioc->shmhead + SHM_BEND1 ; /* end marker 1 */
ioc->buf1 = ioc->shmbuf + SHM_HSIZE ; /* buffer 1 */
ioc->bufsize2 = ioc->shmhead[SHM_SIZE2] ; /* size of buf 2 */
ioc->bstart2 = ioc->shmhead + SHM_BSTART2 ; /* start marker 2*/
ioc->bend2 = ioc->shmhead + SHM_BEND2 ; /* end marker 2 */
ioc->buf2 = ioc->buf1 + ioc->bufsize1 ; /* buffer 2 */
if( jj < SHM_HSIZE+ioc->bufsize1+ioc->bufsize2 ){ /* too small? */
shmdt( bbb ) ; /* this is bad! */
shmctl( ioc->id , IPC_RMID , NULL ) ;
ioc->bad = SHM_IS_DEAD ; return -1 ;
}
ioc->bad = 0 ; return 1 ; /** DONE **/
}
/*---------------------------------------------------------------*/
/*! Create a SHMioc struct for use as a 2-way I/O channel, and
return a pointer to it. NULL is returned if an error occurs.
name = "shm:name:size1+size2" to connect a shared memory
segment with buffers of length size1 and size2 bytes.
The creator process will write to the size1 buffer
and read from the size2 buffer. The acceptor
process will reverse this.
- The size strings can end in 'K' to multiply by 1024,
or end in 'M' to multiply by 1024*1024.
- If neither size is given, a default value is used.
- If only size1 is given, size2=size1.
mode = "w" to open a new shared memory channel
= "r" to log into a channel created by someone else
The input "name" is limited to a maximum of 127 bytes.
-----------------------------------------------------------------*/
static SHMioc * SHM_init( char *name , char *mode )
{
SHMioc *ioc ;
int do_create , do_accept ;
char key[128] , *kend ;
int size1=SHM_DEFAULT_SIZE , ii , jj , size2=SHM_DEFAULT_SIZE ;
/** check if inputs are reasonable **/
if( name == NULL ||
strlen(name) > 127 ||
strncmp(name,"shm:",4) != 0 ||
mode == NULL ) return NULL ;
do_create = (*mode == 'w') ; /* writer */
do_accept = (*mode == 'r') ; /* reader */
if( !do_create && !do_accept ) return NULL ;
/** get keystring (after "shm:") **/
for( ii=4 ; name[ii] != ':' && name[ii] != '\0' ; ii++ )
key[ii-4] = name[ii] ;
key[ii-4] = '\0' ;
/** get size1 (after "shm:name:"), if we stopped at a ':' **/
if( do_create && name[ii] == ':' && name[ii+1] != '\0' ){
size1 = strtol( name+ii+1 , &kend , 10 ) ;
if( size1 <= 0 ) size1 = SHM_DEFAULT_SIZE ;
else {
if( *kend == 'K' || *kend == 'k' ){ size1 *= 1024 ; kend++; }
else if( *kend == 'M' || *kend == 'm' ){ size1 *= 1024*1024; kend++; }
}
size2 = size1 ; /* 23 Aug 2002 */
/** get size2, if we stopped at a + **/
if( *kend == '+' ){
size2 = strtol( kend+1 , &kend , 10 ) ;
if( size2 <= 0 ) size2 = SHM_DEFAULT_SIZE ;
else {
if( *kend == 'K' || *kend == 'k' ){ size2 *= 1024 ; kend++; }
else if( *kend == 'M' || *kend == 'm' ){ size2 *= 1024*1024; kend++; }
}
}
}
/** initialize SHMioc **/