mirror of https://github.com/red-prig/fpPS4.git
304 lines
8.2 KiB
Plaintext
304 lines
8.2 KiB
Plaintext
unit vsocketvar;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
mqueue,
|
|
vfile,
|
|
vsockstate,
|
|
vsockbuf,
|
|
kern_mtx;
|
|
|
|
//#include <sys/sockopt.h>
|
|
|
|
{
|
|
* Kernel structure per socket.
|
|
* Contains send and receive buffer queues,
|
|
* handle on protocol and pointer to protocol
|
|
* private data and error information.
|
|
}
|
|
type
|
|
so_gen_t=QWORD;
|
|
|
|
t_accf_callback=function(so,arg:Pointer;waitflag:Integer):Integer;
|
|
t_accf_create =function(so,arg:Pointer):Pointer;
|
|
t_accf_destroy =procedure(so:Pointer);
|
|
|
|
p_accept_filter=^t_accept_filter;
|
|
t_accept_filter=packed record
|
|
accf_name :array[0..15] of AnsiChar;
|
|
accf_callback:t_accf_callback;
|
|
accf_create :t_accf_create ;
|
|
accf_destroy :t_accf_destroy ;
|
|
accf_next :SLIST_ENTRY ;
|
|
end;
|
|
|
|
{-
|
|
* Locking key to struct socket:
|
|
* (a) constant after allocation, no locking required.
|
|
* (b) locked by SOCK_LOCK(so).
|
|
* (c) locked by SOCKBUF_LOCK(@so^.so_rcv).
|
|
* (d) locked by SOCKBUF_LOCK(@so^.so_snd).
|
|
* (e) locked by ACCEPT_LOCK().
|
|
* (f) not locked since integer reads/writes are atomic.
|
|
* (g) used only as a sleep/wakeup address, no value.
|
|
* (h) locked by global mutex so_global_mtx.
|
|
}
|
|
pp_socket=^p_socket;
|
|
p_socket=^t_socket;
|
|
t_socket=packed record
|
|
so_count :Integer; { (b) reference count }
|
|
so_type :Word; { (a) generic type, see socket.h }
|
|
so_options:Word; { from socket call, see socket.h }
|
|
so_linger :Word; { time to linger while closing }
|
|
so_state :Word; { (b) internal state flags SS_* }
|
|
so_qstate :Integer; { (e) internal state flags SQ_* }
|
|
so_pcb :Pointer; { protocol control block }
|
|
so_vnet :Pointer; { network stack instance }
|
|
so_proto :Pointer; { (a) protocol handle }
|
|
{
|
|
* Variables for connection queuing.
|
|
* Socket where accepts occur is so_head in all subsidiary sockets.
|
|
* If so_head is 0, socket is not related to an accept.
|
|
* For head socket so_incomp queues partially completed connections,
|
|
* while so_comp is a queue of connections ready to be accepted.
|
|
* If a connection is aborted and it has so_head set, then
|
|
* it has to be pulled out of either so_incomp or so_comp.
|
|
* We allow connections to queue up based on current queue lengths
|
|
* and limit on number of queued connections for this socket.
|
|
}
|
|
so_head :p_socket ; { (e) back pointer to listen socket }
|
|
so_incomp :TAILQ_HEAD ; { (e) queue of partial unaccepted connections }
|
|
so_comp :TAILQ_HEAD ; { (e) queue of complete unaccepted connections }
|
|
so_list :TAILQ_ENTRY; { (e) list of unaccepted connections }
|
|
so_qlen :Word ; { (e) number of unaccepted connections }
|
|
so_incqlen:Word ; { (e) number of unaccepted incomplete connections }
|
|
so_qlimit :Word ; { (e) max number queued connections }
|
|
so_timeo :Word ; { (g) connection timeout }
|
|
so_error :Word ; { (f) error affecting connection }
|
|
so_sigio :Pointer ; { [sg] information for async I/O or out of band data (SIGURG) }
|
|
so_oobmark:QWORD ; { (c) chars to oob mark }
|
|
so_aiojobq:TAILQ_HEAD ; { AIO ops waiting on socket }
|
|
|
|
so_rcv :t_sockbuf;
|
|
so_snd :t_sockbuf;
|
|
|
|
//label *so_label ; { (b) MAC label for socket }
|
|
//label *so_peerlabel ; { (b) cached MAC label for peer }
|
|
{ NB: generation count must not be first. }
|
|
so_gencnt :so_gen_t; { (h) generation count }
|
|
so_emuldata:Pointer ; { (b) private data for emulators }
|
|
|
|
so_accf:packed record
|
|
so_accept_filter :p_accept_filter;
|
|
so_accept_filter_arg:Pointer; { saved filter args }
|
|
so_accept_filter_str:Pointer; { saved user args }
|
|
end;
|
|
{
|
|
* so_fibnum, so_user_cookie and friends can be used to attach
|
|
* some user-specified metadata to a socket, which then can be
|
|
* used by the kernel for various actions.
|
|
* so_user_cookie is used by ipfw/dummynet.
|
|
}
|
|
so_fibnum :Integer; { routing domain for this socket }
|
|
so_user_cookie:DWORD;
|
|
end;
|
|
|
|
{
|
|
* Global accept mutex to serialize access to accept queues and
|
|
* fields associated with multiple sockets. This allows us to
|
|
* avoid defining a lock order between listen and accept sockets
|
|
* until such time as it proves to be a good idea.
|
|
}
|
|
var
|
|
accept_mtx:mtx;
|
|
|
|
const
|
|
{
|
|
* Socket state bits stored in so_qstate.
|
|
}
|
|
SQ_INCOMP=$0800; { unaccepted, incomplete connection }
|
|
SQ_COMP =$1000; { unaccepted, complete connection }
|
|
|
|
type
|
|
{
|
|
* Externalized form of struct socket used by the sysctl(3) interface.
|
|
}
|
|
xsocket=packed record
|
|
xso_len :QWORD ; { length of this structure }
|
|
xso_so :p_socket; { makes a convenient handle sometimes }
|
|
so_type :Word;
|
|
so_options :Word;
|
|
so_linger :Word;
|
|
so_state :Word;
|
|
so_pcb :Pointer; { another convenient handle }
|
|
xso_protocol:Integer;
|
|
xso_family :Integer;
|
|
so_qlen :Word;
|
|
so_incqlen :Word;
|
|
so_qlimit :Word;
|
|
so_timeo :Word;
|
|
so_error :Word;
|
|
so_pgid :Integer;
|
|
so_oobmark :QWORD;
|
|
so_rcv :t_xsockbuf;
|
|
so_snd :t_xsockbuf;
|
|
so_uid :uid_t; { XXX }
|
|
end;
|
|
|
|
{
|
|
* Macros for sockets and socket buffering.
|
|
}
|
|
|
|
const
|
|
{
|
|
* Flags to sblock().
|
|
}
|
|
SBL_WAIT =$00000001; { Wait if not immediately available. }
|
|
SBL_NOINTR=$00000002; { Force non-interruptible sleep. }
|
|
SBL_VALID =(SBL_WAIT or SBL_NOINTR);
|
|
|
|
{ 'which' values for socket upcalls. }
|
|
SO_RCV=1;
|
|
SO_SND=2;
|
|
|
|
{ Return values for socket upcalls. }
|
|
SU_OK =0;
|
|
SU_ISCONNECTED=1;
|
|
|
|
procedure ACCEPT_LOCK_ASSERT(); inline;
|
|
procedure ACCEPT_LOCK(); inline;
|
|
procedure ACCEPT_UNLOCK(); inline;
|
|
|
|
function SOCK_MTX(_so:p_socket):p_mtx; inline;
|
|
procedure SOCK_LOCK(_so:p_socket); inline;
|
|
function SOCK_OWNED(_so:p_socket):Boolean; inline;
|
|
procedure SOCK_UNLOCK(_so:p_socket); inline;
|
|
procedure SOCK_LOCK_ASSERT(_so:p_socket); inline;
|
|
|
|
function sb_notify(sb:p_sockbuf):Boolean; inline;
|
|
function sosendallatonce(so:p_socket):Boolean; inline;
|
|
function soreadabledata(so:p_socket):Boolean; inline;
|
|
function soreadable(so:p_socket):Boolean; inline;
|
|
function sowriteable(so:p_socket):Boolean; inline;
|
|
procedure soref(so:p_socket);
|
|
procedure sorele(so:p_socket);
|
|
|
|
implementation
|
|
|
|
procedure ACCEPT_LOCK_ASSERT(); inline;
|
|
begin
|
|
mtx_assert(accept_mtx);
|
|
end;
|
|
|
|
procedure ACCEPT_LOCK(); inline;
|
|
begin
|
|
mtx_lock(accept_mtx);
|
|
end;
|
|
|
|
procedure ACCEPT_UNLOCK(); inline;
|
|
begin
|
|
mtx_unlock(accept_mtx);
|
|
end;
|
|
|
|
//
|
|
|
|
function SOCK_MTX(_so:p_socket):p_mtx; inline;
|
|
begin
|
|
Result:=SOCKBUF_MTX(@_so^.so_rcv)
|
|
end;
|
|
|
|
procedure SOCK_LOCK(_so:p_socket); inline;
|
|
begin
|
|
SOCKBUF_LOCK(@_so^.so_rcv)
|
|
end;
|
|
|
|
function SOCK_OWNED(_so:p_socket):Boolean; inline;
|
|
begin
|
|
Result:=SOCKBUF_OWNED(@_so^.so_rcv);
|
|
end;
|
|
|
|
procedure SOCK_UNLOCK(_so:p_socket); inline;
|
|
begin
|
|
SOCKBUF_UNLOCK(@_so^.so_rcv)
|
|
end;
|
|
|
|
procedure SOCK_LOCK_ASSERT(_so:p_socket); inline;
|
|
begin
|
|
SOCKBUF_LOCK_ASSERT(@_so^.so_rcv);
|
|
end;
|
|
|
|
//
|
|
|
|
{
|
|
* Do we need to notify the other side when I/O is possible?
|
|
}
|
|
function sb_notify(sb:p_sockbuf):Boolean; inline;
|
|
begin
|
|
Result:=(sb^.sb_flags and (SB_WAIT or SB_SEL or SB_ASYNC or SB_UPCALL or SB_AIO or SB_KNOTE))<>0;
|
|
end;
|
|
|
|
{ do we have to send all at once on a socket? }
|
|
function sosendallatonce(so:p_socket):Boolean; inline;
|
|
begin
|
|
//Result:=((so^.so_proto^.pr_flags and PR_ATOMIC)<>0);
|
|
Result:=False;
|
|
end;
|
|
|
|
{ can we read something from so? }
|
|
function soreadabledata(so:p_socket):Boolean; inline;
|
|
begin
|
|
Result:=(so^.so_rcv.sb_cc >= so^.so_rcv.sb_lowat) or
|
|
(not TAILQ_EMPTY(@so^.so_comp)) or (so^.so_error<>0);
|
|
end;
|
|
|
|
function soreadable(so:p_socket):Boolean; inline;
|
|
begin
|
|
Result:=soreadabledata(so) or ((so^.so_rcv.sb_state and SBS_CANTRCVMORE)<>0);
|
|
end;
|
|
|
|
{ can we write something to so? }
|
|
function sowriteable(so:p_socket):Boolean; inline;
|
|
begin
|
|
Result:=((sbspace(@so^.so_snd)>=so^.so_snd.sb_lowat) and
|
|
(((so^.so_state and SS_ISCONNECTED)<>0) or
|
|
({(so^.so_proto^.pr_flags and PR_CONNREQUIRED)=0}False))) or
|
|
((so^.so_snd.sb_state and SBS_CANTSENDMORE)<>0) or
|
|
(so^.so_error<>0);
|
|
end;
|
|
|
|
{
|
|
* soref()/sorele() ref-count the socket structure. Note that you must
|
|
* still explicitly close the socket, but the last ref count will free
|
|
* the structure.
|
|
}
|
|
procedure soref(so:p_socket);
|
|
begin
|
|
SOCK_LOCK_ASSERT(so);
|
|
Inc(so^.so_count);
|
|
end;
|
|
|
|
procedure sorele(so:p_socket);
|
|
begin
|
|
ACCEPT_LOCK_ASSERT();
|
|
SOCK_LOCK_ASSERT(so);
|
|
if (so^.so_count <= 0) then
|
|
Assert(false,'sorele');
|
|
Dec(so^.so_count);
|
|
if (so^.so_count=0) then
|
|
begin
|
|
//sofree(so);
|
|
end else
|
|
begin
|
|
SOCK_UNLOCK(so);
|
|
ACCEPT_UNLOCK();
|
|
end
|
|
end;
|
|
|
|
|
|
end.
|
|
|