1/* Part of SWI-Prolog 2 3 Author: Jeffrey Rosenwald 4 E-mail: jeffrose@acm.org 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2009-2020, Jeffrey Rosenwald 7 All rights reserved. 8 9 Redistribution and use in source and binary forms, with or without 10 modification, are permitted provided that the following conditions 11 are met: 12 13 1. Redistributions of source code must retain the above copyright 14 notice, this list of conditions and the following disclaimer. 15 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in 18 the documentation and/or other materials provided with the 19 distribution. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 POSSIBILITY OF SUCH DAMAGE. 33*/ 34 35:- module(tipc, 36 [ tipc_socket/2, % -Socket +Type 37 tipc_close_socket/1, % +Socket 38 tipc_setopt/2, % +Socket, +Option 39 tipc_bind/3, % +Socket, +Address +Scope 40 tipc_listen/2, % +Socket, +BackLog 41 tipc_accept/3, % +Master, -Slave, -PeerName 42 tipc_open_socket/3, % +Socket, -Read, -Write 43 tipc_get_name/2, % +Socket, -Address 44 tipc_get_peer_name/2, % +Socket, -Address 45 tipc_connect/2, % +Socket, +Address 46 tipc_receive/4, % +Socket, -Data, -Sender, +Options 47 tipc_send/4, % +Socket, +Data, +Receiver, +Options 48% tipc_subscribe/5, % +Socket, +Address, +Timeout, +Filter, 49% +Usr_handle 50 51% tipc_receive_subscr_event/2, % +Socket, -Event 52 tipc_canonical_address/2, % -Address, +port_id/2 53 tipc_service_probe/1, % ?Address 54 tipc_service_probe/2, % ?Address, ?PortId 55 tipc_service_port_monitor/2, % +Address, :Goal 56 tipc_service_port_monitor/3, % +Address, :Goal, +Timeout 57 tipc_service_exists/1, % +Address 58 tipc_service_exists/2, % +Address +Timeout 59 tipc_initialize/0 % 60 ]). 61:- autoload(library(apply),[maplist/3]). 62:- autoload(library(lists),[member/2]). 63 64:- use_foreign_library(foreign(tipc)). 65 66:- multifile tipc_stack_initialize/0. 67 68/** <module> TIPC Sockets 69 70Transparent Inter-Process Communication (TIPC) provides a flexible, 71reliable, fault-tolerant, high-speed, and low-overhead framework for 72inter-process communication between federations of trusted peers, 73operating as a unit. It was developed by Ericsson AB, as a means to 74provide for communications between Common Control Systems processes and 75Network Element peers in telephone switching systems, sometimes 76operating at arm's length on different line cards or mainframes. 77Delegation of responsibility in this way is one of the fundamental 78precepts of the Erlang programming system, also developed at Ericsson. 79TIPC represents a more generalized version of the same behavioral design 80pattern. For an overview, please see: tipc_overview.md. 81 82__Errors__ 83 84The TIPC module uses the error handling functions from library(socket) 85and therefore all the functions below may throw error(socket_error(Code, 86Message)) where `Code` is the lowercase version of the C-macro error 87code and `Message` is an atom describing the error in a human friendly 88format, depending on the current locale. See the socket library for 89details. 90 91@author Jeffrey Rosenwald (JeffRose@acm.org) 92@see <http://tipc.sf.net>, <http://www.erlang.org> 93@compat Linux only 94*/ 95 96%! tipc_socket(-SocketId, +SocketType) is det. 97% 98% Creates a TIPC-domain socket of the type specified by 99% SocketType, and unifies it to an identifier, SocketId. 100% 101% @param SocketType is one of the following atoms: 102% 103% * rdm - unnumbered, reliable datagram service, 104% * dgram - unnumbered, unreliable datagram service, 105% * seqpacket - numbered, reliable datagram service, and 106% * stream - reliable, connection-oriented byte-stream 107% service 108 109%! tipc_close_socket(+SocketId) is det. 110% 111% Closes the indicated socket, making SocketId invalid. In stream 112% applications, sockets are closed by closing both stream handles 113% returned by tipc_open_socket/3. There are two cases where 114% tipc_close_socket/1 is used because there are no 115% stream-handles: 116% 117% * After tipc_accept/3, the server does a fork/1 to handle the 118% client in a sub-process. In this case the accepted socket is 119% not longer needed from the main server and must be discarded 120% using tipc_close_socket/1. 121% 122% * If, after discovering the connecting client with 123% tipc_accept/3, the server does not want to accept the 124% connection, it should discard the accepted socket immediately 125% using tipc_close_socket/1. 126% 127% @param SocketId the socket identifier returned by tipc_socket/2 128% or tipc_accept/3. 129 130%! tipc_subscribe(+SocketId, +NameSeqAddress, +Timeout, +Filter, +UserHandle) is det. 131% 132% Subscribes to events related to a publisher that is bound to 133% the multi-cast address specified in NameSeqAddress. 134% 135% Timeout specifies the duration of the subscription in 136% milliseconds. Specifying a Timeout of -1, provides for a 137% subscription of infinite duration. Filter specifies the events 138% of interest to the subscriber. It is an integer bit field 139% where: 140% 141% * (1 is X /\ 1) -> "notify port availability", 142% * (2 is X /\ 2) -> "notify service availability", and 143% * (4 is X /\ 4) -> "notify subscription cancellation". 144% 145% UserHandle is an eight-byte, user-defined buffer that is passed 146% transparently to the subscriber with every notification. An 147% application that uses this service must be prepared to parse 148% event notifications received from the TIPC topology server. 149% tipc_receive_subscr_event/4, is provided for this purpose. 150% 151% It is possible to have multiple subscriptions active on the 152% same socket any one time, making periodic subscription renewal 153% easy. 154% 155% _|This predicate is valid only on connections to the topology 156% server: name(1,1,0).|_ 157% 158% _|Please note that this predicate should be considered private. 159% It has been removed from the Prolog public API. Its use in user 160% programs is strongly discouraged. See the "tipc_service" 161% predicates for equivalent functionality.|_ 162% 163% @param SocketId the socket identifier that was provided by 164% tipc_socket/2, and is connected to the topology server, 165% name(1,1,0), via tipc_connect/2. 166% 167% @param NameSeqAddress the name_seq/3 address of the service to 168% be monitored. 169% 170% @param PortId the port-id of the socket that is bound to the 171% service 172% 173% @param Timeout an integer that specifies the duration of the 174% subscription in milliseconds. A duration of -1, specifies a 175% subscription of infinite duration. @param Filter the event 176% filter bit map. 177% 178% @param UserHandle an eight-byte code that is passed 179% transparently to the user with each event notification. 180 181%! tipc_open_socket(+SocketId, -InStream, -OutStream) is det. 182% 183% Opens two SWI-Prolog I/O-streams, one to deal with input from 184% the socket and one with output to the socket. If tipc_bind/3 185% has been called on the socket, OutStream is useless and will 186% not be created. After closing both InStream and OutStream, the 187% socket itself is discarded. 188 189%! tipc_bind(+Socket, +Address, +ScopingOption) is det. 190% 191% Associates/disassociates a socket with the name/3 or name_seq/3 192% address specified in Address. It also registers/unregisters it in 193% the topology server name table. This makes the address 194% visible/invisible to the rest of the network according to the 195% scope specified in ScopingOption. ScopingOption is a grounded 196% term that is one of: 197% 198% $ scope(Scope) : where Scope is one of: =zone=, =cluster=, or 199% =node=. Servers may bind to more than one address by making 200% successive calls to tipc_bind/3, one for each address that it 201% wishes to advertise. The server will receive traffic for all 202% of them. A server may, for example, register one address with 203% node scope, another with cluster scope, and a third with zone 204% scope. A client may then limit the scope of its transmission 205% by specifying the appropriate address. 206% 207% $ no_scope(Scope) : where Scope is as defined above. An 208% application may target a specific address for removal from its 209% collection of addresses by specifying the address and its 210% scope. The scoping option, =|no_scope(all)|=, may be used to 211% unbind the socket from all of its registered addresses. This 212% feature allows an application to gracefully exit from service. 213% Because the socket remains open, the application may continue 214% to service current transactions to completion. TIPC however, 215% will not schedule any new work for the server instance. If no 216% other servers are available, the work will be rejected or 217% dropped according to the socket options specified by the 218% client. 219% 220% Connection-oriented, byte-stream services are implemented with 221% this predicate combined with tipc_listen/2 and tipc_accept/3. 222% Connectionless, datagram services may be implemented using 223% tipc_receive/4. 224% 225% Note that clients do not need to bind to any address. Its 226% port-id is sufficient for this role. And server sockets (e.g. 227% those that are bound to name/3 or name_seq/3, addresses) may 228% not act as clients. That is, they may not originate connections 229% from the socket using tipc_connect/2. Servers however, may 230% originate datagrams from bound sockets using tipc_send/4. 231% Please see the TIPC programmers's guide for other restrictions. 232 233%! tipc_listen(+Socket,+Backlog) is det. 234% 235% Listens for incoming requests for connections. Backlog 236% indicates how many pending connection requests are allowed. 237% Pending requests are requests that are not yet acknowledged 238% using tipc_accept/3. If the indicated number is exceeded, the 239% requesting client will be signalled that the service is 240% currently not available. A suggested default value is 5. 241 242%! tipc_accept(+Socket, -Slave, -Peer) is det. 243% 244% Blocks on a server socket and waits for connection requests 245% from clients. On success, it creates a new socket for the 246% client and binds the identifier to Slave. Peer is bound to the 247% TIPC address, port_id/2, of the client. 248 249%! tipc_connect(+Socket, +TIPC_address) is det. 250% 251% Provides a connection-oriented, client-interface to connect a 252% socket to a given TIPC_address. After successful completion, 253% tipc_open_socket/3 may be used to create I/O-Streams to the 254% remote socket. 255 256%! tipc_get_name(+Socket, -TIPC_address) is det. 257% 258% Unifies TIPC_address with the port-id assigned to the socket. 259 260%! tipc_get_peer_name(+Socket, -TIPC_address) is det. 261% 262% Unifies TIPC_address with the port-id assigned to the socket 263% that this socket is connected to. 264 265%! tipc_setopt(+Socket,+Option) is det. 266% 267% Sets options on the socket. Defined options are: 268% 269% $ importance(+Priority) : 270% Allow sockets to assign a priority to their traffic. Priority 271% is one of : =low= (default), =medium=, =high=, or =critical=. 272% 273% $ src_droppable(+Boolean) : 274% Allow TIPC to silently discard packets in congested situations, 275% rather than queuing them for later transmission. 276% 277% $ dest_droppable(+Boolean) : 278% Allow TIPC to silently discard packets in congested situations, 279% rather than returning them to the sender as undeliverable. 280% 281% $ conn_timeout(+Seconds) : 282% Specifies the time interval that tipc_connect/2 will use before 283% abandoning a connection attempt. Default: 8.000 sec. 284 285%! tipc_receive(+Socket, -Data, -From, +OptionList) is det. 286% 287% Waits for, and returns the next datagram. Like its UDP 288% counterpart, the data are returned as a Prolog string object 289% (see string_codes/2). From is an address structure of the 290% form port_id/2, indicating the sender of the message. 291% 292% Defined options are: 293% 294% * as(+Type) 295% Defines the returned term-type. Type is one of atom, codes or 296% string (default). 297% 298% * nonblock 299% Poll the socket and return immediately. If a message is 300% present, it is returned. If not, then an exception, 301% error(socket_error(eagain, Message), _), will be thrown. Users 302% are cautioned not to "spin" unnecessarily on non-blocking 303% receives as they may prevent the system from servicing other 304% background activities such as XPCE event dispatching. 305% 306% The typical sequence to receive a connectionless TIPC datagram is: 307% 308% == 309% receive :- 310% tipc_socket(S, dgram), 311% tipc_bind(S, name(18888, 10, 0), scope(zone)), 312% repeat, 313% tipc_receive(Socket, Data, From, [as(atom)]), 314% format('Got ~q from ~q~n', [Data, From]), 315% Data == quit, 316% !, tipc_close_socket(S). 317% == 318% 319 320%! tipc_send(+Socket, +Data, +To, +Options) is det. 321% 322% sends a TIPC datagram to one or more destinations. Like its UDP 323% counterpart, Data is a string, atom or code-list providing the 324% data to be sent. To is a name/3, name_seq/3, or port_id/2 325% address structure. See tipc_overview.txt, for more information 326% on TIPC Address Structures. Options is currently unused. 327% 328% A simple example to send a connectionless TIPC datagram is: 329% 330% == 331% send(Message) :- 332% tipc_socket(S, dgram), 333% tipc_send(S, Message, name(18888, 10,0), []), 334% tipc_close_socket(S). 335% == 336% 337% Messages are delivered silently unless some form of congestion 338% was encountered and the =|dest_droppable(false)|= option was 339% issued on the sender's socket. In this case, the send succeeds 340% but a notification in the form of an empty message is returned 341% to the sender from the receiver, indicating some kind of 342% delivery failure. The port-id of the receiver is returned in 343% congestion conditions. A =|port_id(0,0)|=, is returned if the 344% destination address was invalid. Senders and receivers should 345% beware of this possibility. 346% 347% 348 349%! tipc_event(+Data, -Event, -Residue) is det. 350% 351% Parses event notifications received from the topology server 352% into Prolog structures. This predicate has been permanently 353% removed. 354% 355 356%! tipc_receive_subscr_event(+Socket, -Event) is semidet. 357% 358% Receives and parses event notifications received from the 359% TIPC Topology Server. 360% 361% _|Please note that this predicate is considered private. Its use 362% in user programs is strongly discouraged. See the 363% tipc-service predicates for alternatives.|_ 364% 365% @param Socket is a TIPC socket that has been previously 366% connected to the topology server using tipc_connect/2, and for 367% which a subscription has been sent using tipc_subscribe/5, 368% above. 369% 370% @param Event is the structure: =|tipc_event(-Action, -Subscr, 371% -Found, -Port_id)|=. On subscription timeout, the 372% atom, =subscr_timeout= is returned. Subscr is the name-sequence 373% address of the original subscription. 374% 375% Action is one of: 376% 377% $ published : 378% The socket specified by Port_id has been bound to 379% the name_seq/3 address specified in Found. 380% 381% $ withdrawn : 382% The socket specified by Port_id has been 383% unbound from the name_seq/3 address specified in Found. See 384% the no_scope/1 option of tipc_bind/3. 385 386:- multifile 387 prolog:message/3. 388 389prologmessage(error(socket_error(_Code, Message), _)) --> 390 [ 'Socket error: ~w'-[Message] ]. 391 392%! tipc_canonical_address(-CanonicalAddress, +PortId) is det. 393% 394% Translates a port_id/2 address into canonical TIPC form: 395% 396% * tipc_address(Zone, Cluster, Node, Reference) 397% 398% It is provided for debugging an printing purposes only. The 399% canonical address is not used for any other purpose. 400 401integerAsU32(In, Out) :- 402 nonvar(In), 403 ( In < 0 404 -> Out is In + 0x100000000 405 ; Out is In 406 ). 407integerAsU32(In, Out) :- 408 nonvar(Out), 409 ( Out > 0x7fffffff 410 -> In is Out - 0x100000000 411 ; In is Out 412 ). 413 414tipc_canonical_address(tipc_address(Z,C,N, Ref1), port_id(Ref, Node)) :- 415 integerAsU32(Ref, Ref1), 416 integerAsU32(Node, X), 417 Z is (X >> 24) /\ 0xFF, 418 C is (X >> 12) /\ 0xFFF, 419 N is X /\ 0xFFF. 420 421user:portray(port_id(Ref, Node)) :- 422 tipc_canonical_address(tipc_address(Z,C,N, Ref1), port_id(Ref, Node)), 423 format('port_id(''<~w.~w.~w:~w>'')', [Z,C,N, Ref1]). 424 425%! tipc_service_exists(+Address, +Timeout) is semidet. 426%! tipc_service_exists(+Address) is semidet. 427% 428% Interrogates the TIPC topology server to see if a service is 429% available at an advertised Address. 430% 431% @param Address is one of: =|name(Type, Instance, Domain)|= or 432% =|name_seq(Type, Lower, Upper)|=. A name/3, address is 433% translated to a name_seq/3, following, where Lower and Upper are 434% assigned the value of Instance. Domain is unused and must be 435% zero. A =|name_seq(Type, Lower, Upper)|= is a multi-cast 436% address. This predicate succeeds if there is at least one 437% service that would answer according to multi-cast addressing 438% rules. 439% 440% @param Timeout is optional. It is a non-negative real number 441% that specifies the amount of time in seconds to block and wait 442% for a service to become available. Fractions of a second are 443% also permissible. 444% 445 446tipc_address(name(T, I, 0), name_seq(T, I, I)). 447tipc_address(name_seq(T, L, U), name_seq(T, L, U)). 448tipc_address(mcast(T, L, U), name_seq(T, L, U)). 449% 450% 451 452tipc_service_exists(Address) :- 453 tipc_service_exists(Address, 0.0). 454 455tipc_service_exists(Address, Timeout) :- 456 tipc_address(Address, NameSeq), 457 !, 458 ITime is integer(Timeout * 1000), 459 try_finally(tipc_socket(S, seqpacket), 460 tipc_close_socket(S)), 461 tipc_connect(S, name(1,1,0)), % connect to the topology server 462 tipc_subscribe(S, NameSeq, ITime, 2, "swipl"), 463 repeat, 464 tipc_receive_subscr_event(S, Data), 465 ( Data == subscr_timeout 466 -> !, fail 467 ; Data = tipc_event(published, NameSeq, _FoundSeq, _Port_id) 468 ), 469 !. 470 471%! tipc_service_probe(?Address) is nondet. 472%! tipc_service_probe(?Address, ?PortId) is nondet. 473% Allows a user to discover the instance ranges and/or port-ids 474% for a particular service. 475% 476% @param Address is a name_seq/3 address. The address type must be 477% grounded. 478% 479% @param PortId is unified with the port-id for a specific 480% name_sequence address. 481% 482 483try_finally(Setup, Cleanup) :- 484 setup_call_cleanup(Setup, ( Solution = yes ; Solution = no ), Cleanup), 485 Solution = yes. 486 487tipc_service_probe(Address) :- 488 tipc_address(Address, name_seq(Type, Lower, Upper)), 489 integer(Type), 490 NameSeq = name_seq(Type, Lower, Upper), 491 try_finally(tipc_socket(S, seqpacket), tipc_close_socket(S)), 492 tipc_connect(S, name(1,1,0)), % connect to the topology server 493 tipc_subscribe(S, name_seq(Type, 0, 4294967295), 0, 2, "swipl"), % look for everything 494 sp_collect(S, Members), 495 !, 496 member([NameSeq, _], Members). 497 498tipc_service_probe(Address, PortId) :- 499 tipc_address(Address, name_seq(Type, Lower, Upper)), 500 integer(Type), 501 NameSeq = name_seq(Type, Lower, Upper), 502 try_finally(tipc_socket(S, seqpacket), tipc_close_socket(S)), 503 tipc_connect(S, name(1,1,0)), % connect to the topology server 504 tipc_subscribe(S, name_seq(Type, 0, 4294967295), 0, 1, "swipl"), % look for everything 505 sp_collect(S, Members), 506 !, 507 member([NameSeq, PortId], Members). 508 509sp_collect(S, Members) :- 510 findall([NameSeq, PortId], 511 ( repeat, 512 tipc_receive_subscr_event(S, Data), 513 ( Data == subscr_timeout 514 -> !, fail 515 ; Data = tipc_event(published, _Service, NameSeq, PortId) 516 ) 517 ), Members). 518 519%! tipc_service_port_monitor(+Addresses, :Goal) is det. 520%! tipc_service_port_monitor(+Addresses, :Goal, ?Timeout) is det. 521% 522% Monitors a collection of worker threads that are bound to a list 523% of Addresses. A single port monitor may be used to provide 524% surveillance over workers that are providing a number of 525% different services. For a given address type, discontiguous 526% port ranges may be specified, but overlapping port ranges may 527% not. Goal for example, may simply choose to broadcast the 528% notification, thus delegating the notification event handling to 529% others. 530% 531% @param Addresses is a list of name/3 or name_seq/3 addresses 532% for the services to be monitored. 533% 534% @param Goal is a predicate that will be called when a 535% worker's publication status changes. The Goal 536% is called exactly once per event with its the last argument 537% unified with the structure: 538% 539% $ published(-NameSeq, -PortId) : when the worker binds 540% its socket to the address. 541% 542% $ withdrawn(-NameSeq, -PortId) : when the worker 543% unbinds its socket from the address. 544% 545% @param Timeout is optional. It is one of: 546% 547% $ Timeout : a non-negative real number that specifies the 548% number of seconds that surveillance is to be continued. 549% 550% $ infinite : causes the monitor to run forever in the current 551% thread (e.g. never returns). 552% 553% $ detached(-ThreadId) : causes the monitor to run 554% forever as a separate thread. ThreadId is unified with the 555% thread identifier of the monitor thread. This is useful when the 556% monitor is required to provide continuous surveillance, while 557% operating in the background. 558% 559 560spm_dispatch(_Goal, subscr_timeout) :- !. 561spm_dispatch(Goal, tipc_event(Action, _Subscr, NameSeq, PortId)) :- 562 Event =.. [Action, NameSeq, PortId], 563 once(call(Goal, Event)), fail. 564 565:- meta_predicate 566 tipc_service_port_monitor( , ), 567 tipc_service_port_monitor( , , ). 568 569tipc_service_port_monitor(Address, Goal) :- 570 tipc_service_port_monitor(Address, Goal, 0.0), 571 !. 572 573tipc_service_port_monitor(Address, Goal, detached(ThreadId)) :- 574 !, 575 thread_create(tipc_service_port_monitor(Address, Goal, infinite), 576 ThreadId, [detached(true)]). 577tipc_service_port_monitor(Address, Goal, infinite) :- 578 tipc_service_port_monitor(Address, Goal, -0.001), 579 !. 580 581tipc_service_port_monitor(Addresses, Goal, Timeout) :- 582 maplist(tipc_address,Addresses, NameSeqs), 583 !, 584 ITime is integer(Timeout * 1000), 585 try_finally(tipc_socket(S, seqpacket), tipc_close_socket(S)), 586 tipc_connect(S, name(1,1,0)), % connect to the topology server 587 forall(member(NameSeq, NameSeqs), 588 tipc_subscribe(S, NameSeq, ITime, 1, "swipl")), 589 repeat, 590 tipc_receive_subscr_event(S, Data), 591 spm_dispatch(Goal, Data), 592 !. 593 594%% tipc_initialize is semidet. 595% 596% causes the TIPC service and the TIPC stack to be initialized 597% and made ready for service. An application must call this 598% predicate as part of its initialization prior to any use of 599% TIPC predicates. _|Please note the change of the API.|_ 600 601tipc_initialize :- 602 with_mutex(tipc_mutex, 603 forall(tipc_stack_initialize, true)). 604 605tipc_stack_initialize :- 606 tipc_service_exists(name(1,1,0))