FPPS4/sys/kern/uipc_syscalls.pas

608 lines
10 KiB
Plaintext

unit uipc_syscalls;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
function sys_socket (domain,stype,protocol:Integer):Integer;
function sys_socketex (name:pchar;domain,stype,protocol:Integer):Integer;
function sys_socketclose(fd:Integer):Integer;
function sys_bind (s:Integer;name:Pointer;namelen:Integer):Integer;
function sys_listen (s,backlog:Integer):Integer;
function sys_accept (s:Integer;aname,anamelen:Pointer):Integer;
function sys_connect (fd:Integer;name:Pointer;namelen:Integer):Integer;
function sys_setsockopt (s,level,name:Integer;val:Pointer;valsize:Integer):Integer;
function sys_netabort (fd,flags:Integer):Integer;
implementation
uses
errno,
mqueue,
systm,
md_sleep,
kern_thr,
kern_descrip,
sys_conf,
kern_socket,
vsocket,
sockopt,
vsocketvar,
vuio,
vfile,
vfcntl,
vcapability;
{
Convert a user file descriptor to a kernel file entry and check that, if
it is a capability, the right rights are present. A reference on the file
entry is held upon returning.
}
function getsock_cap(fd :Integer;
rights:cap_rights_t;
fpp :pp_file;
fflagp:PDWORD):Integer;
var
fp:p_file;
begin
fp:=fget_unlocked(fd);
if (fp=nil) then
begin
Exit(EBADF);
end;
if (fp^.f_type<>DTYPE_SOCKET) and
(fp^.f_type<>DTYPE_IPCSOCKET) then
begin
fdrop(fp);
Exit(ENOTSOCK);
end;
if (fflagp<>nil) then
begin
fflagp^:=fp^.f_flag;
end;
fpp^:=fp;
Exit(0);
end;
function sys_socket(domain,stype,protocol:Integer):Integer;
var
td:p_kthread;
so:Pointer;
fp:p_file;
fd:Integer;
dtype:Integer;
begin
td:=curkthread;
if (td=nil) then Exit(-1);
fd:=0;
Result:=falloc(@fp,@fd,0);
if (Result<>0) then Exit();
so:=nil; /////socreate
dtype:=DTYPE_SOCKET;
if (domain=PF_UNIX) then
begin
dtype:=DTYPE_IPCSOCKET;
end;
finit(fp,FREAD or FWRITE, dtype, so, @socketops);
fdrop(fp);
td^.td_retval[0]:=fd;
end;
function sys_socketex(name:pchar;domain,stype,protocol:Integer):Integer;
var
td:p_kthread;
so:Pointer;
fp:p_file;
fd:Integer;
dtype:Integer;
begin
td:=curkthread;
if (td=nil) then Exit(-1);
fd:=0;
Result:=falloc(@fp,@fd,0);
if (Result<>0) then Exit();
so:=nil; /////socreate
dtype:=DTYPE_SOCKET;
if (domain=PF_UNIX) then
begin
dtype:=DTYPE_IPCSOCKET;
end;
finit(fp,FREAD or FWRITE, dtype, so, @socketops);
fdrop(fp);
td^.td_retval[0]:=fd;
end;
function sys_socketclose(fd:Integer):Integer;
begin
Result:=kern_close(fd);
end;
function getsockaddr(namp:pp_sockaddr;uaddr:Pointer;len:size_t):Integer;
var
sa:p_sockaddr;
begin
sa:=nil;
if (len>SOCK_MAXADDRLEN) then
begin
Exit(ENAMETOOLONG);
end;
if (len<size_t(@p_sockaddr(nil)^.sa_data[0])) then
begin
Exit(EINVAL);
end;
sa:=AllocMem(len);
Result:=copyin(uaddr, sa, len);
if (Result<>0) then
begin
FreeMem(sa);
end else
begin
sa^.sa_len:=len;
namp^:=sa;
end;
end;
function kern_bind(fd:Integer;sa:p_sockaddr):Integer;
var
//so:p_socket;
fp:p_file;
error:Integer;
begin
error:=getsock_cap(fd, CAP_BIND, @fp, nil);
if (error<>0) then
begin
Exit(error);
end;
//so:=fp^.f_data;
//error:=sobind(so, sa, td);
fdrop(fp);
Exit(error);
end;
function sys_bind(s:Integer;name:Pointer;namelen:Integer):Integer;
var
sa:p_sockaddr;
error:Integer;
begin
error:=getsockaddr(@sa, name, namelen);
if (error<>0) then
begin
Exit(error);
end;
error:=kern_bind(s, sa);
FreeMem(sa);
Exit(error);
end;
function kern_connect(fd:Integer;sa:p_sockaddr):Integer;
var
fp:p_file;
begin
fp:=nil;
Result:=getsock_cap(fd, CAP_CONNECT, @fp, nil);
if (Result<>0) then Exit;
Result:=ECONNREFUSED;
fdrop(fp);
end;
function sys_listen(s,backlog:Integer):Integer;
var
//so:p_socket;
fp:p_file;
error:Integer;
begin
error:=getsock_cap(s, CAP_LISTEN, @fp, nil);
if (error=0) then
begin
//so:=fp^.f_data;
//error:=solisten(so, uap^.backlog, td);
fdrop(fp);
end;
Exit(error);
end;
function kern_accept(s:Integer;name:pp_sockaddr;
namelen:p_socklen_t;fp:pp_file):Integer;
label
done,
noconnection;
var
td:p_kthread;
headfp,nfp:p_file;
sa:p_sockaddr;
error:Integer;
//so,head:p_socket;
fd:Integer;
fflag:DWORD;
pgid:Integer; //pid_t
tmp:Integer;
begin
td:=curkthread;
headfp:=nil;
nfp :=nil;
sa :=nil;
if (name<>nil) then
begin
name^:=nil;
if (namelen^ < 0) then
begin
Exit(EINVAL);
end;
end;
error:=getsock_cap(s, CAP_ACCEPT, @headfp, @fflag);
if (error<>0) then
begin
Exit(error);
end;
{
head:=headfp^.f_data;
if ((head^.so_options and SO_ACCEPTCONN)=0) then
begin
error:=EINVAL;
goto done;
end;
}
error:=falloc(@nfp, @fd, 0);
if (error<>0) then
begin
goto done;
end;
//////////////////
msleep_td(0);
error:=EWOULDBLOCK;
goto noconnection;
//////////////////
ACCEPT_LOCK();
{
if (((head^.so_state and SS_NBIO)<>0) and TAILQ_EMPTY(@head^.so_comp)) then
begin
ACCEPT_UNLOCK();
error:=EWOULDBLOCK;
goto noconnection;
end;
}
{
while ((TAILQ_EMPTY(@head^.so_comp)) and (head^.so_error=0)) do
begin
if (head^.so_rcv.sb_state & SBS_CANTRCVMORE) then
begin
head^.so_error:=ECONNABORTED;
break;
end;
error:=msleep(@head^.so_timeo, @accept_mtx, PSOCK or PCATCH, 'accept', 0);
if (error) then
begin
ACCEPT_UNLOCK();
goto noconnection;
end;
end;
}
{
if (head^.so_error<>0) then
begin
error:=head^.so_error;
head^.so_error:=0;
ACCEPT_UNLOCK();
goto noconnection;
end;
}
{
so:=TAILQ_FIRST(@head^.so_comp);
Assert(not (so^.so_qstate and SQ_INCOMP), 'accept1: so SQ_INCOMP');
Assert(so^.so_qstate and SQ_COMP, 'accept1: so not SQ_COMP');
}
{
* Before changing the flags on the socket, we have to bump the
* reference count. Otherwise, if the protocol calls sofree(),
* the socket will be released due to a zero refcount.
}
{
SOCK_LOCK(so); { soref() and so_state update }
soref(so); { file descriptor reference }
TAILQ_REMOVE(@head^.so_comp, so, so_list);
Dec(head^.so_qlen);
so^.so_state :=so^.so_state or (head^.so_state and SS_NBIO);
so^.so_qstate:=so^.so_qstate and (not SQ_COMP);
so^.so_head :=nil;
SOCK_UNLOCK(so);
}
ACCEPT_UNLOCK();
{ An extra reference on `nfp' has been held for us by falloc(). }
td^.td_retval[0]:=fd;
{
{ connection has been removed from the listen queue }
KNOTE_UNLOCKED(@head^.so_rcv.sb_sel.si_note, 0);
pgid:=fgetown(@head^.so_sigio);
if (pgid<>0) then
begin
fsetown(pgid, @so^.so_sigio);
end;
finit(nfp, fflag, DTYPE_SOCKET, so, @socketops);
{ Sync socket nonblocking/async state with file flags }
tmp:=fflag and FNONBLOCK;
fo_ioctl(nfp, FIONBIO, @tmp, td^.td_ucred, td);
tmp:=fflag and FASYNC;
fo_ioctl(nfp, FIOASYNC, @tmp, td^.td_ucred, td);
sa:=0;
error:=soaccept(so, @sa);
}
if (error<>0) then
begin
{
* Exit a namelen of zero for older code which might
* ignore the Exit value from accept.
}
if (name<>nil) then
begin
namelen^:=0;
end;
goto noconnection;
end;
if (sa=nil) then
begin
if (name<>nil) then
begin
namelen^:=0;
end;
goto done;
end;
if (name<>nil) then
begin
{ check sa_len before it is destroyed }
if (namelen^ > sa^.sa_len) then
begin
namelen^:=sa^.sa_len;
end;
name^:=sa;
sa:=nil;
end;
noconnection:
if (sa<>nil) then
begin
FreeMem(sa);
end;
{
* close the new descriptor, assuming someone hasn't ripped it
* out from under us.
}
if (error<>0) then
begin
fdclose(nfp, fd);
end;
{
* Release explicitly held references before Exiting. We Exit
* a reference on nfp to the caller on success if they request it.
}
done:
if (fp<>nil) then
begin
if (error=0) then
begin
fp^:=nfp;
nfp:=nil;
end else
begin
fp^:=nil;
end;
end;
//
if (nfp<>nil) then
begin
fdrop(nfp);
end;
fdrop(headfp);
//
Exit(error);
end;
function accept1(s:Integer;aname:p_sockaddr;anamelen:p_socklen_t):Integer;
var
td:p_kthread;
name:p_sockaddr;
namelen:socklen_t;
fp:p_file;
error:Integer;
begin
td:=curkthread;
if (aname=nil) then
begin
Exit(kern_accept(s, nil, nil, nil));
end;
error:=copyin(anamelen, @namelen, sizeof(namelen));
if (error<>0) then
begin
Exit(error);
end;
error:=kern_accept(s, @name, @namelen, @fp);
{
* Exit a namelen of zero for older code which might
* ignore the Exit value from accept.
}
if (error<>0) then
begin
copyout(@namelen, anamelen, sizeof(anamelen^));
Exit(error);
end;
if (error=0) and (name<>nil) then
begin
error:=copyout(name, name, namelen);
end;
if (error=0) then
begin
error:=copyout(@namelen,anamelen,sizeof(namelen));
end;
if (error<>0) then
begin
fdclose(fp, td^.td_retval[0]);
end;
fdrop(fp);
FreeMem(name);
Exit(error);
end;
function sys_accept(s:Integer;aname,anamelen:Pointer):Integer;
begin
Exit(accept1(s,aname,anamelen));
end;
function sys_connect(fd:Integer;name:Pointer;namelen:Integer):Integer;
var
sa:p_sockaddr;
begin
sa:=nil;
Result:=getsockaddr(@sa, name, namelen);
if (Result<>0) then Exit;
Result:=kern_connect(fd, sa);
FreeMem(sa);
end;
function kern_setsockopt(s,level,name:Integer;val:Pointer;valseg:uio_seg;valsize:Integer):Integer;
var
error:Integer;
//so:p_socket;
fp:p_file;
sopt:t_sockopt;
begin
if (val=nil) and (valsize<>0) then
begin
Exit(EFAULT);
end;
if (valsize < 0) then
begin
Exit(EINVAL);
end;
sopt.sopt_dir :=SOPT_SET;
sopt.sopt_level :=level;
sopt.sopt_name :=name;
sopt.sopt_val :=val;
sopt.sopt_valsize:=valsize;
case (valseg) of
UIO_USERSPACE:sopt.sopt_td:=curkthread;
UIO_SYSSPACE :sopt.sopt_td:=nil;
else
Assert(false,'kern_setsockopt called with bad valseg');
end;
error:=getsock_cap(s, CAP_SETSOCKOPT, @fp, nil);
if (error=0) then
begin
//so:=fp->f_data;
//error:=sosetopt(so, @sopt);
fdrop(fp);
end;
Exit(error);
end;
function sys_setsockopt(s,level,name:Integer;val:Pointer;valsize:Integer):Integer;
begin
Result:=kern_setsockopt(s,level,name,val,UIO_USERSPACE,valsize);
end;
const
SOCKET_ABORT_FLAG_RCV =$00000001;
SOCKET_ABORT_FLAG_SND =$00000002;
SOCKET_ABORT_FLAG_SND_AGAIN=$00000004;
function sys_netabort(fd,flags:Integer):Integer;
var
error:Integer;
fp:p_file;
begin
if ((flags and $1000000)=0) then
begin
error:=getsock_cap(fd, flags, @fp, nil);
if (error=0) then
begin
//error:=bnet_netabort(td,fp->f_data,flags);
fdrop(fp);
end;
end else
begin
flags:=flags and $feffffff;
error:=0;
//error:=kern_epollabort(td,fd,flags);
end;
Exit(error);
end;
end.