fpc/utils/debugsvr/debugserverintf.pp
2012-11-08 16:17:55 +00:00

381 lines
8.7 KiB
ObjectPascal

{
This file is part of the Free Pascal run time library.
Copyright (c) 2003 by the Free Pascal development team
Interface for debug server.
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{$mode objfpc}
{$h+}
unit debugserverintf;
Interface
Uses
msgintf,baseunix,classes,sockets,sysutils;
Const
MsgTypes : Array[-1..3] of string =
('Disconnect','Information','Warning','Error','Identify');
Type
Thandle = Longint; // Abstraction for easier porting.
TClient = Class(TObject)
Handle : THandle;
Peer : ShortString;
Data : Pointer;
end;
TDebugEvent = Record
Client : TClient;
LogCode : Integer;
TimeStamp : TDateTime;
Event : String;
end;
Var
FClients : TList;
Accepting : Boolean;
Quit : Boolean;
DebugLogCallback : Procedure (Const Event : TDebugEvent);
DebugObjLogCallBack : Procedure (Const Event : TDebugEvent) of Object;
CloseConnectionCallBack : Procedure (Client : TClient);
CloseObjConnectionCallBack : Procedure (Client : TClient) of Object;
Procedure OpenDebugServer;
Procedure CloseDebugServer;
Function ClientFromHandle (AHandle : THandle) : TClient;
Procedure ReadMessage(Handle : THandle);
Procedure ReadMessageEvent(Handle : THandle; Var Event : TDebugEvent);
Function CheckNewConnection : TClient;
procedure CloseConnection(Client : TClient);
Procedure CloseClientHandle(Handle : THandle);
ResourceString
SClientLog = 'Client log %d';
SEvent = 'Event';
SMessage = 'Message';
SStopAccepting = 'Stop accepting new connections';
SStartAccepting = 'Start accepting new connections';
SErrSocketFailed = 'Creation of socket failed: %s';
SErrBindFailed = 'Binding of socket failed: %s';
SErrListenFailed = 'Listening on port #%d failed: %s';
SErrAcceptFailed = 'Could not accept a client connection: %d';
SClosingConnection = 'Closing connection.';
SErrFailedToSetSignalHandler = 'Failed to set signal handler.';
SPeerAt = 'Peer at %d';
Implementation
Function ClientFromHandle (AHandle : THandle) : TClient;
Var
I : Longint;
begin
Result:=Nil;
I:=0;
With FClients do
While (I<Count) and (Result=Nil) do
Begin
If TClient(Items[i]).Handle=AHandle then
Result:=TClient(Items[i]);
Inc(I);
end;
end;
{ ---------------------------------------------------------------------
Communications handling: Unix Socket setup
---------------------------------------------------------------------}
Var
FSocket : Integer;
Procedure SetupUnixSocket;
var
Flags,AddrLen : Integer;
FUnixAddr : TUnixSockAddr;
FFileName : String;
Quit : Boolean;
begin
FFileName:=DebugSocket;
FSocket:=FPSocket(AF_UNIX,SOCK_STREAM,0);
If FSocket<0 Then
Raise Exception.Create(SErrSocketFailed);
Flags:=fpFCntl(FSOCket,F_GETFL);
Flags:=Flags or O_NONBLOCK;
fpFCntl(FSocket,F_SETFL,Flags);
Str2UnixSockAddr(FFilename,FUnixAddr,AddrLen);
If FPBind(FSocket,@FUnixAddr,AddrLen)<>0 then
Raise Exception.CreateFmt(SErrBindFailed,[FFileName]);
If (fpListen(FSocket,5)<>0) then
Raise Exception.CreateFmt(SErrListenFailed,[FSocket]);
FClients:=TList.Create;
Accepting:=True;
end;
Procedure DestroyUnixSocket;
Var
C : TClient;
begin
If Assigned(FClients) then
begin
With FClients do
While Count>0 do
begin
C:=TClient(Items[Count-1]);
FileClose(C.Handle);
C.Free;
Delete(Count-1);
end;
FileClose(FSocket);
DeleteFile(DebugSocket);
end;
end;
{ ---------------------------------------------------------------------
Communications handling: Inet Socket setup
---------------------------------------------------------------------}
Procedure SetupInetSocket(Aport : Word);
var
Flags,AddrLen : Integer;
FInetAddr : TInetSockAddr;
FFileName : String;
Quit : Boolean;
begin
FSocket:=FPSocket(AF_INET,SOCK_STREAM,0);
If FSocket<0 Then
Raise Exception.Create(SErrSocketFailed);
Flags:=fpFCntl(FSocket,F_GETFL);
Flags:=Flags or O_NONBLOCK;
fpFCntl(FSocket,F_SETFL,Flags);
FInetAddr.Family := AF_INET;
Writeln('Using port : ',APort);
FInetAddr.Port := Swap(APort);
FInetAddr.Addr := 0;
If FPBind(FSocket,@FInetAddr,SizeOf(FInetAddr))<>0 then
Raise Exception.CreateFmt(SErrBindFailed,[FFileName]);
If fpListen(FSocket,5)<>0 then
Raise Exception.CreateFmt(SErrListenFailed,[FSocket]);
end;
Procedure DestroyInetSocket;
Var
C : TClient;
begin
If Assigned(FClients) then
begin
With FClients do
While Count>0 do
begin
C:=TClient(Items[Count-1]);
FileClose(C.Handle);
C.Free;
Delete(Count-1);
end;
FileClose(FSocket);
end;
end;
{ ---------------------------------------------------------------------
Communications handling: Public interface
---------------------------------------------------------------------}
Procedure OpenDebugServer;
begin
Case DebugConnection of
dcUnix : SetupUnixSocket;
dcInet : SetupInetSocket(DebugPort);
end;
FClients:=TList.Create;
Accepting:=True;
end;
Procedure CloseDebugServer;
begin
Accepting:=False;
Case DebugConnection of
dcUnix : DestroyUnixSocket;
dcInet : DestroyInetSocket;
end;
FClients.Free;
FClients:=Nil;
end;
{ ---------------------------------------------------------------------
Communications handling: Connection handling
---------------------------------------------------------------------}
Function GetNewConnection : THandle;
Var
ClientAddr: TUnixSockAddr;
L : Integer;
begin
If Accepting then
begin
L:=SizeOf(ClientAddr);
Result:=fpAccept(FSocket,@ClientAddr,@L);
If (Result<0) Then
if (Errno<>ESYSEAgain) then
Raise Exception.CreateFmt(SErrAcceptFailed,[FSocket])
else
Result:=-1
{$ifdef debug}
else
Writeln('New connection detected at ',Result)
{$endif debug}
end
else
Result:=-1;
end;
Function CheckNewConnection : TClient;
Var
NC : THandle;
begin
NC:=GetNewConnection;
If (NC=-1) then
Result:=Nil
else
begin
Result:=TClient.Create;
Result.Handle:=NC;
{$ifdef debug}
Writeln('Added new client', nc, ' at : ',FClients.Add(Result));
{$else}
FClients.Add(Result);
{$endif debug}
end;
end;
Procedure CloseClientHandle(Handle : THandle);
begin
fpShutDown(Handle,2);
FileClose(Handle);
end;
Procedure CloseConnection(Client : TClient);
Var
I : longint;
C : TClient;
begin
If Assigned(Client) then
begin
If Assigned(CloseConnectionCallBack) then
CloseConnectionCallBack(Client);
If Assigned(CloseObjConnectionCallBack) then
CloseObjConnectionCallBack(Client);
CloseClientHandle(Client.Handle);
FClients.Remove(Client);
Client.Free;
end;
end;
{ ---------------------------------------------------------------------
Message handling
---------------------------------------------------------------------}
Function MsgToEvent(AHandle: THandle; ALogCode : Integer; ATimeStamp : TDateTime; AEvent : String) : TDebugEvent;
begin
With Result do
begin
Client:=ClientFromHandle(AHandle);
If (Client<>Nil) then
begin
If (ALogCode=lctIdentify) then
Client.Peer:=AEvent;
end;
LogCode:=ALogCode;
TimeStamp:=ATimeStamp;
Event:=AEvent;
end;
end;
Procedure LogEvent(Event : TDebugEvent);
begin
if Assigned(DebugLogCallback) then
DebugLogCallBack(Event);
If Assigned(DebugObjLogCallBack) then
DebugObjLogCallBack(Event);
end;
Procedure ReadMessageEvent(Handle : THandle; Var Event : TDebugEvent);
Var
FDebugMessage : TDebugMessage;
msgSize : Integer;
begin
Try
With FDebugMessage do
begin
// Select reports read ready when closed, so check for this.
If (FileRead(Handle,msgType,SizeOf(Integer))=0) or (MsgType=-1) then
begin
event:=MsgToEvent(Handle,lctStop,Now,SClosingConnection);
If Assigned(Event.Client) then
CloseConnection(Event.Client)
else
CloseClientHandle(Handle);
end
else
begin
FileRead(Handle,msgTimeStamp,sizeof(TDateTime));
FileRead(Handle,MsgSize,SizeOf(Integer));
SetLength(Msg,MsgSize);
FileRead(Handle,Msg[1],MsgSize);
Event:=MsgToEvent(Handle,msgType,msgTimeStamp,Msg);
end
end;
except
On E : Exception do
Event:=MsgToEvent(Handle,lctError,Now,E.Message);
end;
end;
Procedure ReadMessage(Handle : THandle);
Var
Event : TDebugEvent;
begin
ReadMessageEvent(Handle,Event);
LogEvent(Event);
end;
end.