Recent

Author Topic: Textfiles and unix-domain sockets  (Read 8856 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Textfiles and unix-domain sockets
« on: January 01, 2022, 09:56:02 pm »
Has anybody ever explored the situation where output is to go to a "named file" which can either be on disc or a unix-domain socket?

I think that they are distinct: true files need WriteLn() etc. while sockets need fpSend(). However I'd be interested in anybody else's experience in this area.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11947
  • FPC developer.
Re: Textfiles and unix-domain sockets
« Reply #1 on: January 01, 2022, 10:54:21 pm »
You really never realized that many socket functions have text overloads?

https://www.freepascal.org/docs-html/current/rtl/sockets/connect.html

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Textfiles and unix-domain sockets
« Reply #2 on: January 02, 2022, 10:04:06 am »
I was- ovbiously- aware of the fact that both files and sockets are handle-based. I wasn't confident that the higher-level FPC functions would work transparently, hence my question.

Thanks for the example, which will be useful particularly if I end up looking at both ends of the connection. I'll report back if unix-domain sockets appear special in any way: I've only used them for datagrams so far.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11947
  • FPC developer.
Re: Textfiles and unix-domain sockets
« Reply #3 on: January 02, 2022, 12:02:40 pm »
I tried to use them a few times, but error handling gets more complex, so I abandoned it.

When I wanted to remove them at some point, Michael says he used them in a work environment

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Textfiles and unix-domain sockets
« Reply #4 on: January 02, 2022, 04:13:33 pm »
I tried to use them a few times, but error handling gets more complex, so I abandoned it.

When I wanted to remove them at some point, Michael says he used them in a work environment

I think the key thing is that the "handle" parameter is the same, i.e. it's not simply WriteLn() overloaded for different "handle" types.

What I'm doing is collecting instrument state via HPIB, then decoding it to a text file... and I find myself in the position that the more flexible the output is (i.e. not just stdout) the better.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Textfiles and unix-domain sockets
« Reply #5 on: January 02, 2022, 09:09:15 pm »
Couple of questions arising from this.

The first is that Connect() is marked deprecated but fpConnect() isn't overlaid with Text or File parameters... what should I be using here?

The second is that Connect() doesn't have an overlaid variant with the TUnixSockAddr parameter which is needed for a unix-domain socket (which is /specifically/ what I was asking about when I stared the thread)... any comments?

I could probably hack this from the sockets POV, but I'm concerned that this would be merely a slippery slope and that changes would be needed in WriteLn() etc. if the existing socket support is entirely for the inet domain.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11947
  • FPC developer.
Re: Textfiles and unix-domain sockets
« Reply #6 on: January 02, 2022, 11:08:13 pm »
Couple of questions arising from this.

The first is that Connect() is marked deprecated but fpConnect() isn't overlaid with Text or File parameters... what should I be using here?

Non FP version might have 1.0.x errorhandling that is not entirely ok. fp* versions have error handling the same as the APIs it connects too.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Textfiles and unix-domain sockets
« Reply #7 on: January 03, 2022, 11:29:50 am »
Connect() fails with a unix-domain socket since the length of the address structure is truncated when it's passed to fpConnect() internally.

Code: Pascal  [Select][+][-]
  1.   var
  2.     reopenedInput: Text;                (* Placeholder, probably not needed     *)
  3.  
  4.  
  5.   procedure reopenUDSocket(var t: Text; const n: string);
  6.  
  7.   type
  8.     TSockAddr= record
  9.                  case boolean of
  10.                    false: (inet: TInetSockAddr);
  11.                    true:  (udom: TUnixSockAddr)
  12.                  end;
  13.  
  14.   var
  15.     skt: TSocket;
  16.     ipcAddr: TSockAddr;
  17.  
  18.     i: integer;
  19.  
  20.   begin
  21.     CloseFile(t);
  22.     skt:= fpSocket(PF_UNIX, SOCK_STREAM, 0);
  23.     if skt < 0 then begin
  24.       if not quiet then
  25.         WriteLn(stderr, 'Cannot create unix-domain socket handle');
  26.       Halt(9)
  27.     end;
  28.     FillChar(ipcAddr, SizeOf(ipcAddr), 0);
  29.     ipcAddr.udom.family:= AF_UNIX;
  30.     StrPCopy(ipcAddr.udom.path, n);
  31.  
  32.  i := fpConnect(skt, @ipcAddr.inet, SizeOf(ipcAddr.inet));   // <---- this fails
  33.  i := fpConnect(skt, @ipcAddr.inet, SizeOf(ipcAddr.udom));   // <---- this works
  34.  
  35.     if not Connect(skt, ipcAddr.inet, reopenedInput, t) then begin // <---- this fails
  36.       if not quiet then
  37.         WriteLn(stderr, 'Cannot open unix-domain socket "' + n + '"');
  38.       Halt(9)
  39.     end;
  40.     Rewrite(t)
  41.   end { reopenUDSocket } ;
  42.  

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Textfiles and unix-domain sockets
« Reply #8 on: January 03, 2022, 12:07:46 pm »
I've added this:

Code: Pascal  [Select][+][-]
  1.   (* This is a reimplementation of the standard Connect() since it doesn't
  2.     support a unix-domain address and truncates it if told to cast it to an
  3.     inet domain address structure.
  4.   *)
  5.   Function Connect(Sock:longint;const addr: TUnixSockAddr;var SockIn,SockOut:text):Boolean;
  6.  
  7.  
  8.     Function DoConnect(Sock:longint;const addr: TUnixSockAddr): Boolean;
  9.  
  10.     var
  11.       res: longint;
  12.     begin
  13.       repeat
  14.         res:=fpconnect(Sock,@Addr,SizeOF(TUnixSockAddr)); (* NOTA BENE *)
  15.       until (res<>-1) or (SocketError <> EsockEINTR);
  16.       DoConnect:= res = 0;
  17.     end;
  18.  
  19.  
  20.   begin
  21.     Connect:=DoConnect(Sock,addr);
  22.     If Connect then
  23.        Sock2Text(Sock,SockIn,SockOut);
  24.   end { Connect } ;
  25.  

If it fails, check whether the file exists; if it exists then it probably means that the listener has terminated.

Test with e.g.

$ nc -lkU ~/dsocket

If the -k option is omitted then Netcat will terminate without deleting the placeholder in the filesystem when the writer closes the connection.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

RickE

  • New Member
  • *
  • Posts: 13
Re: Textfiles and unix-domain sockets
« Reply #9 on: March 07, 2022, 04:27:08 pm »
I got a little "sequential packet Unix (local) socket" unit working, and was impressed with the sequential packet vs. stream socket type. Linux rightly brags about such a useful feature. In years past I would make a formatted web page to down load, but please forgive if I just try insert some code here;

Code: Pascal  [Select][+][-]
  1. UNIT LocalPacketSockets_Text_Unit;
  2. {******************************************************************************
  3.         FREEPASCAL LINUX C LIBRARY SOCKETS INTERFACE
  4. ******************************************************************************}
  5.  
  6. {*****************************************************************************}
  7. INTERFACE
  8. {$PACKRECORDS C}
  9. Uses Sockets_Text_Unit;
  10. {*****************************************************************************}
  11.  
  12. {==============================================================================
  13.         LOCAL (UNIX) PACKET SOCKETS INTERFACE
  14. ==============================================================================}
  15. Const
  16. { Unix/Local Domain Socket Address Family }
  17.         AF_LOCAL = 1;
  18.         AF_UNIX = AF_LOCAL;
  19.  
  20. { Unix/Local Domain Socket Types }
  21.         SOCK_STREAM = 1;                { Sequenced, reliable, connection-based byte streams. }
  22.         SOCK_SEQPACKET = 5;             { Sequenced, reliable, connection-based datagrams of fixed maximum length.}
  23.  
  24. { Unix/Local Protocol Family }
  25.         PF_UNSPEC = 0;                  { Unspecified. Normally OK. }
  26.         PF_LOCAL = 1;                   { Local to host (pipes and file-domain). }
  27.         PF_UNIX = PF_LOCAL;             { POSIX name for PF_LOCAL. }
  28.  
  29.         SOMAXCONN =     128;            { Maximum connect queue length specifiable by listen. }
  30.         Max_ConnectQueue = SOMAXCONN;
  31.  
  32.         Max_SockAddrLength = 110;{ longest possible unix local domain socket address record }
  33.  
  34. Type
  35. { FreePascal String Type variable to hold a "FileName" }
  36.         t_UnixSocket_FilePathName = String[107];
  37.  
  38. { FreePascal Record Type variable to hold the C Structure parameter }
  39.         p_UnixSocket_AddressRecord = ^t_UnixSocket_AddressRecord ;
  40.         t_UnixSocket_AddressRecord = Record
  41.                                                                 AddressFamily: word;
  42.                                                                 FilePathName: array[0..107] of char;
  43.                                                                 End; { t_UnixSocket_AddressRecord }
  44.  
  45. { ============================================================================ }
  46. { Create a New Local Packet Type Socket FileDescriptor.
  47.  Returns False for errors. }
  48. Function CreateNew_LocalPacketSocket_OK( var param_var_NewSocketFD: Longint ): Boolean;
  49.  
  50.  
  51. { Bind a Local Packet Type Socket FileDescriptor to a New Socket FilePathName.
  52.  Returns False for errors. }
  53. Function Bind_LocalPacketSocket_PathName_OK( param_SocketFD: longint;
  54.                                                                                 const param_NewSocketFilePathName: t_UnixSocket_FilePathName ):
  55.                                                                                 Boolean;
  56.  
  57. { Server Listens on a Local Packet Type Socket FileDescriptor for Client Connection Requests.
  58.  Returns False for errors. }
  59. Function Listen_LocalPacketSocket_OK( param_ServerSocketFD,
  60.                                                                                 param_MaxConnectRequests: Longint ):
  61.                                                                                 Boolean;
  62.  
  63. { Client Requests a Connection to a Listening  Local Packet Type Socket FileName.
  64.  Create New Text Type I/O Channels on Client FileDescriptor.
  65.  Returns False for errors. }
  66. Function ConnectRequest_NewTextPacketSocket_OK( param_ClientSocketFD: longint;
  67.                                                                                                 const param_ServerSocket_FilePathName: t_UnixSocket_FilePathName;
  68.                                                                                                 var param_var_NewClientTextInputChannel: Text;
  69.                                                                                                 var param_var_NewClientTextOutputChannel: Text ):
  70.                                                                                                 Boolean;
  71.  
  72. { Server Accepts a Client Connection Request on a Listening Local Domain Server Socket FileDescriptor.
  73.  Create a New AcceptConnection FileDescriptor on Server Socket.
  74.  Create New Text Type I/O Channels on New Server Socket AcceptConnection FileDescriptor.
  75.  Returns False for errors. }
  76. Function AcceptRequest_NewTextPacketSocket_OK( param_ServerSocketFD : longint;
  77.                                                                                                 var param_var_NewAcceptConnectFD: longint;
  78.                                                                                                 var param_var_ClientSocket_FilePathName: t_UnixSocket_FilePathName ;
  79.                                                                                                 var param_var_NewServerTextInputChannel: Text;
  80.                                                                                                 var param_var_NewServerTextOutputChannel: Text ):
  81.                                                                                                 Boolean;
  82.  
  83. {*****************************************************************************}
  84. IMPLEMENTATION
  85. {*****************************************************************************}
  86.  
  87. { Construct t_UnixSocket_AddressRecord }
  88. Procedure PathName_to_SockAddrRec(const param_UnixSocket_FilePathName: t_UnixSocket_FilePathName;
  89.                                                                 var param_var_SockAddrRecord: t_UnixSocket_AddressRecord;
  90.                                                                 var param_var_SockAddrRecordLength: longint );
  91.         Begin
  92.         { Fill in AddressFamily field }
  93.                 param_var_SockAddrRecord.AddressFamily := 1 ;   {AF_UNIX}
  94.  
  95.         { Fill in FilePathName field with all 0s ==>> null terminated }
  96.             FillByte ( param_var_SockAddrRecord.FilePathName,
  97.                                 SizeOf( param_var_SockAddrRecord.FilePathName ),
  98.                                 0 );
  99.  
  100.         { Assign FilePathName field }
  101.                 Move( param_UnixSocket_FilePathName[1],
  102.                                 param_var_SockAddrRecord.FilePathName,
  103.                                 length( param_UnixSocket_FilePathName ) );
  104.  
  105.         { Calculate sizeof(sockaddr_un) := sizeof(sa_family_t) + sizeof(sun_path) + sizeof(null character ) }
  106.                 param_var_SockAddrRecordLength:= 2 + Length( param_UnixSocket_FilePathName ) + 1 ;
  107.  
  108.         End; { Procedure PathName_to_SockAddrRec }
  109.  
  110. { ============================================================================ }
  111. { Create a NEW SOCKET of DOMAIN, TYPE, PROTOCOL. }
  112. { Returns a new file descriptor for the new socket, or -1 for errors.   }
  113. { Uses domain: AF_LOCAL = 1, type: SOCK_SEQPACKET = 5. }
  114. { ============================================================================ }
  115. {libc}
  116.         Function LibcCall_Socket( __domain: longint;
  117.                                                                 __type: longint;
  118.                                                                 __protocol: longint ):
  119.                                                                 longint; cdecl; external 'c' name 'socket';
  120.  
  121. { ---------------------------------------------------------------------------- }
  122. { Libc Functions ==>> Pascal Functions }
  123. { ---------------------------------------------------------------------------- }
  124. Function CreateNew_LocalPacketSocket_OK( var param_var_NewSocketFD: Longint ): Boolean;
  125.         Var
  126.                 loc_var_LibcCall_Return : longint ;
  127.         Begin
  128.         { Initialize }
  129.                 param_var_NewSocketFD := -1;
  130.                 CreateNew_LocalPacketSocket_OK := False;
  131.  
  132.         { Do Linux libc call }
  133.                 loc_var_LibcCall_Return := LibcCall_Socket( 1, 5, 0 );
  134.  
  135.         { If Success }
  136.                 If loc_var_LibcCall_Return > 0
  137.                         Then
  138.                         Begin
  139.                                 param_var_NewSocketFD := loc_var_LibcCall_Return;
  140.                                 CreateNew_LocalPacketSocket_OK := True;
  141.                         End;
  142.  
  143.         End; { Function CreateNew_LocalPacketSocket_OK }
  144.  
  145. { ============================================================================ }
  146. { Bind a Local Unix Socket FileName to a Local Unix Socket FileDescriptor }
  147. { Give the Local Unix Socket FD the Local Unix Socket Address Record (which is LEN bytes long). }
  148. { ============================================================================ }
  149. {libc}
  150.         Function LibcCall_Bind( __fd: longint ;
  151.                                                                 __addr: p_UnixSocket_AddressRecord;
  152.                                                                 __len: longint ):
  153.                                                                 longint; cdecl; external 'c' name 'bind';
  154.  
  155. { ---------------------------------------------------------------------------- }
  156. { Libc Functions ==>> Pascal Functions }
  157. { ---------------------------------------------------------------------------- }
  158. Function Bind_LocalPacketSocket_PathName_OK( param_SocketFD: longint;
  159.                                                                                         const param_NewSocketFilePathName: t_UnixSocket_FilePathName ):
  160.                                                                                         Boolean;
  161.         Var
  162.                 loc_var_SockAddrRecord:                 t_UnixSocket_AddressRecord;
  163.                 loc_var_SockAddrRecordLength:   longint;
  164.                 loc_var_LibcCall_Return:        longint;
  165.         Begin
  166.         { Initialize }
  167.                 Bind_LocalPacketSocket_PathName_OK := False;
  168.  
  169.         { Construct UnixDomainSocketAddress_Record }
  170.                 PathName_to_SockAddrRec( param_NewSocketFilePathName,
  171.                                                                         loc_var_SockAddrRecord,
  172.                                                                         loc_var_SockAddrRecordLength );
  173.         { Do Linux libc call }
  174.                 loc_var_LibcCall_Return := LibcCall_Bind( param_SocketFD,
  175.                                                                                                 @loc_var_SockAddrRecord,
  176.                                                                                                 loc_var_SockAddrRecordLength );
  177.         { If Success }
  178.                 If loc_var_LibcCall_Return = 0
  179.                         Then Bind_LocalPacketSocket_PathName_OK := True;
  180.  
  181.         End; { Function Bind_LocalPacketSocket_PathName_OK }
  182.  
  183. { ============================================================================ }
  184. { Listen and Wait until a Client Connection Request on Server Socket FD. }
  185. { Server Listens prepared to accept connections on socket FD. }
  186. { N connection requests will be queued before further requests are refused.}
  187. { ============================================================================ }
  188. {libc}
  189.         Function LibcCall_Listen( __fd : longint ;
  190.                                                                 __n : longint ):
  191.                                                                 longint; cdecl; external 'c' name 'listen';
  192.  
  193. { ---------------------------------------------------------------------------- }
  194. { Libc Functions ==>> Pascal Functions }
  195. { ---------------------------------------------------------------------------- }
  196. Function Listen_LocalPacketSocket_OK( param_ServerSocketFD,
  197.                                                                                         param_MaxConnectRequests: Longint ):
  198.                                                                                         Boolean;
  199.         Var
  200.                 loc_var_LibcCall_Return: longint;
  201.         Begin
  202.         { Initialize }
  203.                 Listen_LocalPacketSocket_OK := False ;
  204.  
  205.         { Do Linux libc call }
  206.                 loc_var_LibcCall_Return := LibcCall_Listen( param_ServerSocketFD,
  207.                                                                                                         param_MaxConnectRequests );
  208.         { If Success }
  209.                 If loc_var_LibcCall_Return = 0
  210.                         Then Listen_LocalPacketSocket_OK := True;
  211.  
  212.         End; { Function Listen_LocalPacketSocket_OK }
  213.  
  214. { ============================================================================ }
  215. { Client Requests a connection to a Listening Server Socket FileName }
  216. { Open a Connection on Client Socket FD to Server Socket at ADDR .}
  217. { ============================================================================ }
  218. {libc}
  219.         Function LibcCall_Connect( __fd : longint ;
  220.                                                                 __addr : p_UnixSocket_AddressRecord ;
  221.                                                                 __len : longint ):
  222.                                                                 longint; cdecl; external 'c' name 'connect';
  223.  
  224. { ---------------------------------------------------------------------------- }
  225. { Libc Functions ==>> Pascal Functions }
  226. { ---------------------------------------------------------------------------- }
  227. Function ConnectRequest_NewTextPacketSocket_OK( param_ClientSocketFD: longint;
  228.                                                                                         const param_ServerSocket_FilePathName: t_UnixSocket_FilePathName ;
  229.                                                                                         var param_var_NewClientTextInputChannel: Text ;
  230.                                                                                         var param_var_NewClientTextOutputChannel: Text ):
  231.                                                                                         Boolean;
  232.         Var
  233.                 loc_var_ServerSockAddrRecord:           t_UnixSocket_AddressRecord;
  234.                 loc_var_ServerSockAddrRecordLength:     longint;
  235.                 loc_var_LibcCall_Return:                longint;
  236.  
  237.         Begin
  238.         { Initialize }
  239.                 ConnectRequest_NewTextPacketSocket_OK := False ;
  240.  
  241.         { Construct UnixSockAddrRecord Path to Server }
  242.                 PathName_to_SockAddrRec( param_ServerSocket_FilePathName,
  243.                                                                                 loc_var_ServerSockAddrRecord,
  244.                                                                                 loc_var_ServerSockAddrRecordLength ) ;
  245.         { Do Linux libc call }
  246.                 loc_var_LibcCall_Return := LibcCall_Connect( param_ClientSocketFD,
  247.                                                                                                         @loc_var_ServerSockAddrRecord,
  248.                                                                                                         loc_var_ServerSockAddrRecordLength ) ;
  249.         { If Success }
  250.                 If loc_var_LibcCall_Return = 0
  251.                         Then
  252.                         { Construct NEW Socket Communication channels }
  253.                         Begin
  254.                                 Assign_SocketFD_to_SocketIO_TextChannels( param_ClientSocketFD,
  255.                                                                                                                         param_var_NewClientTextInputChannel,
  256.                                                                                                                         param_var_NewClientTextOutputChannel );
  257.  
  258.                                 ConnectRequest_NewTextPacketSocket_OK := True ;
  259.                         End
  260.  
  261.         End; { Function ConnectRequest_NewTextPacketSocket_OK }
  262.  
  263. { ============================================================================ }
  264. { When a Client Connection Request arrives, }
  265. {  create a NEW Server AcceptConnect Socket FD to communicate with it, }
  266. {  set  ADDR to the Address Record of the connecting Client Peer, }
  267. {  and return the new Server AcceptConnect Socket File Descriptor, or -1 for errors. }
  268. { ============================================================================ }
  269. {libc}
  270.         Function LibcCall_Accept( __fd: longint;
  271.                                                                 __addr: p_UnixSocket_AddressRecord;
  272.                                                                 __addr_len: pLongint ):
  273.                                                                 longint; cdecl; external 'c' name 'accept';
  274.  
  275. { ---------------------------------------------------------------------------- }
  276. { Libc Functions ==>> Pascal Functions }
  277. { ---------------------------------------------------------------------------- }
  278. Function AcceptRequest_NewTextPacketSocket_OK( param_ServerSocketFD : longint ;
  279.                                                                                                 var param_var_NewAcceptConnectFD: longint;
  280.                                                                                                 var param_var_ClientSocket_FilePathName: t_UnixSocket_FilePathName;
  281.                                                                                                 var param_var_NewServerTextInputChannel: Text;
  282.                                                                                                 var param_var_NewServerTextOutputChannel : Text ):
  283.                                                                                                 Boolean;
  284.         var
  285.                 loc_var_NewAcceptConnectFD: longint;
  286.                 loc_var_ClientSockAddrRecord: t_UnixSocket_AddressRecord;
  287.                 loc_var_ClientSockAddrRecordLength: longint;
  288.                 loc_var_LibcCall_Return: longint;
  289.  
  290.         Begin
  291.         { Initialize }
  292.                 AcceptRequest_NewTextPacketSocket_OK := False;
  293.  
  294.         { Initialize SockAddrRecord }
  295.                 loc_var_ClientSockAddrRecord.FilePathName := '' ;
  296.  
  297.         { Create enough space for return variable }
  298.                 loc_var_ClientSockAddrRecordLength := Max_SockAddrLength;
  299.  
  300.         { Do Linux libc call }
  301.                 loc_var_LibcCall_Return := LibcCall_Accept( param_ServerSocketFD,
  302.                                                                                                 @loc_var_ClientSockAddrRecord,
  303.                                                                                                 @loc_var_ClientSockAddrRecordLength ) ;
  304.         { If Success }
  305.                 If loc_var_LibcCall_Return > 0
  306.                         Then
  307.                         Begin
  308.                         { Set the NEW Server Connection ID }
  309.                                 param_var_NewAcceptConnectFD := loc_var_LibcCall_Return ;
  310.                                 loc_var_NewAcceptConnectFD := loc_var_LibcCall_Return ;
  311.  
  312.                         { Construct returned Client Name String }
  313.                                 Move( loc_var_ClientSockAddrRecord.FilePathName,
  314.                                                 param_var_ClientSocket_FilePathName[1],
  315.                                                 loc_var_ClientSockAddrRecordLength - 3 );
  316.  
  317.                                 SetLength( param_var_ClientSocket_FilePathName,
  318.                                                         loc_var_ClientSockAddrRecordLength - 3 );
  319.  
  320.  
  321.                         { Construct NEW Socket Communication channels }
  322.                                 Assign_SocketFD_to_SocketIO_TextChannels( loc_var_NewAcceptConnectFD,
  323.                                                                                                                         param_var_NewServerTextInputChannel,
  324.                                                                                                                         param_var_NewServerTextOutputChannel );
  325.  
  326.                                 AcceptRequest_NewTextPacketSocket_OK := True;
  327.                         End
  328.  
  329.         End ; { Function AcceptRequest_NewTextPacketSocket_OK }
  330.  
  331. {******************************************************************************
  332. Initialize Unit
  333. ******************************************************************************}
  334. Begin
  335. { nothing }
  336. End.
  337.  
  338.  
  339.  


And always to thank the FreePascal team for their wonderful development system.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Textfiles and unix-domain sockets
« Reply #10 on: March 07, 2022, 07:24:44 pm »
UD sockets are a nice feature, but I think their real strength is as a "short circuit" if data is to be transferred locally (e.g. an X11 connection between client and server on the same system).

(Named) FIFOs are somewhat easier to work with if a stream (rather than a sequence of datagrams) is to be handled and there is absolutely no need to map it over the network.

In either case a nodding familiarity with POSIX capabilities is useful, so that if the (name representing the) socket or FIFO has to be in a directory owned by root a program can manipulate it during startup.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

RickE

  • New Member
  • *
  • Posts: 13
Re: Textfiles and unix-domain sockets
« Reply #11 on: March 07, 2022, 09:28:23 pm »
Here is the "Sockets_Text_Unit" required above in the Uses clause;


Code: Pascal  [Select][+][-]
  1.  
  2. UNIT Sockets_Text_Unit;
  3. {******************************************************************************
  4.         FREEPASCAL SOCKETS TEXT FILE I/O INTERFACE
  5. ******************************************************************************}
  6.  
  7. {*****************************************************************************}
  8. INTERFACE
  9. {$PACKRECORDS C}
  10. {*****************************************************************************}
  11. {
  12. Text FileType Record
  13. Read + Write Support
  14.  
  15. Types and Constants from FreePascal System Unit textrec.inc
  16.  
  17. const
  18.         TextRecNameLength = 256;
  19.         TextRecBufSize    = 256;
  20.  
  21. type
  22.         TFileTextRecChar = widechar;
  23.         TLineEndStr = string [3];
  24.         TextBuf = array[0..TextRecBufSize-1] of ansichar;
  25.         TTextBuf = TextBuf;
  26.  
  27.         TextRec = Record
  28.                                 Handle    : THandle;
  29.                                 Mode      : longint;
  30.                                 bufsize   : SizeInt;
  31.                                 _private  : SizeInt;
  32.                                 bufpos,
  33.                                 bufend    : SizeInt;
  34.                                 bufptr    : ^textbuf;
  35.                                 openfunc,
  36.                                 inoutfunc,
  37.                                 flushfunc,
  38.                                 closefunc : codepointer;
  39.                                 UserData  : array[1..32] of byte;
  40.                                 name      : array[0..textrecnamelength-1] of TFileTextRecChar;
  41.                                 LineEnd   : TLineEndStr; Line ending to use
  42.                                 buffer    : textbuf;
  43.         End;
  44. }
  45.  
  46. Procedure Assign_SocketFD_to_SocketIO_TextChannels( param_SocketFD: Longint;
  47.                                                                                                 Var param_var_Input_TextFile: Text;
  48.                                                                                                 Var param_var_Output_TextFile: Text );
  49.  
  50. {*****************************************************************************}
  51. IMPLEMENTATION
  52. Uses SysUtils;
  53. {*****************************************************************************}
  54.  
  55. Function Read_SocketInput_TextChannel( var param_var_TextRecord: TextRec ): Longint;
  56.         var loc_var_CharCount : longint;
  57.         Begin
  58.         { System Read from Socket FileDescriptor into Socket Text Buffer. }
  59.                 loc_var_CharCount := FileRead( param_var_TextRecord.handle,
  60.                                                                                 param_var_TextRecord.bufptr^,
  61.                                                                                 param_var_TextRecord.bufsize ) ;
  62.  
  63.                 If (loc_var_CharCount < 0) then Exit(-1) ;
  64.  
  65.         { Returns number of bytes read to TextRecord.BufEnd. }
  66.                 param_var_TextRecord.BufEnd := loc_var_CharCount ;
  67.  
  68.         { ReSet Read TextRecord buffer position to 0 when done. }
  69.                 param_var_TextRecord.bufpos := 0 ;
  70.  
  71.         { Done }
  72.                 Read_SocketInput_TextChannel := 0 ;
  73.         End; { Function Read_SocketInput_TextChannel }
  74.  
  75. { ============================================================================ }
  76. Function Write_SocketOutput_TextChannel( var param_var_TextRecord: TextRec ): Longint;
  77.         var loc_var_CharCount : longint;
  78.         Begin
  79.         { System Write from Socket Text Buffer into Socket FileDescriptor. }
  80.         { Writes bufpos number of bytes. }
  81.                 loc_var_CharCount := FileWrite( param_var_TextRecord.handle,
  82.                                                                                 param_var_TextRecord.bufptr^,
  83.                                                                                 param_var_TextRecord.bufpos );
  84.  
  85.                 If (loc_var_CharCount < 0) then Exit(-1) ;
  86.  
  87.         { ReSet buffer position to 0 when done. }
  88.                 param_var_TextRecord.bufpos := 0;
  89.  
  90.         { Done }
  91.                 Write_SocketOutput_TextChannel := 0;
  92.         End; { Function Write_SocketOutput_TextChannel }
  93.  
  94. { ============================================================================ }
  95. Function Flush_SocketOutput_TextChannel( var param_var_TextRecord: TextRec ): Longint;
  96.         Begin
  97.         { If needs flush. }
  98.                 If ( param_var_TextRecord.bufpos > 0 )
  99.                         Then Write_SocketOutput_TextChannel( param_var_TextRecord );
  100.  
  101.         { Done }
  102.                 Flush_SocketOutput_TextChannel := 0;
  103.         End; { Function Flush_SocketOutput_TextChannel }
  104.  
  105. { ============================================================================ }
  106. { Assign two FreePascal Text FileTypes to Socket FileDescriptor for Read, Write calls. }
  107.  
  108. Procedure Assign_SocketInput_TextChannel( param_SocketFD: Longint;
  109.                                                                                         Var param_var_Input_TextFile: Text );
  110.         Begin
  111.                 FillChar(TextRec( param_var_Input_TextFile), SizeOf(TextRec), 0 );
  112.                 TextRec(param_var_Input_TextFile).Mode                  := fmInput;
  113.                 TextRec(param_var_Input_TextFile).Handle                := param_SocketFD ;
  114.                 TextRec(param_var_Input_TextFile).InOutFunc             := @Read_SocketInput_TextChannel ;
  115.                 TextRec(param_var_Input_TextFile).FlushFunc             := nil ;
  116.                 TextRec(param_var_Input_TextFile).OpenFunc              := nil ;
  117.                 TextRec(param_var_Input_TextFile).CloseFunc             := nil ;
  118.                 TextRec(param_var_Input_TextFile).BufSize               := TextRecBufSize ;
  119.                 TextRec(param_var_Input_TextFile).Bufptr                := @TextRec(param_var_Input_TextFile).Buffer;
  120.                 TextRec(param_var_Input_TextFile).LineEnd               := #10;
  121.         End; { Procedure Assign_SocketInput_TextChannel }
  122.  
  123. Procedure Assign_SocketOutput_TextChannel( param_SocketFD: Longint;
  124.                                                                                         Var param_var_Output_TextFile: Text );
  125.         Begin
  126.                 FillChar( TextRec(param_var_Output_TextFile), SizeOf(TextRec), 0 );
  127.                 TextRec(param_var_Output_TextFile).Mode                 := fmOutput;
  128.                 TextRec(param_var_Output_TextFile).Handle               := param_SocketFD;
  129.                 TextRec(param_var_Output_TextFile).InOutFunc    := @Write_SocketOutput_TextChannel;
  130.                 TextRec(param_var_Output_TextFile).FlushFunc    := @Flush_SocketOutput_TextChannel;
  131.                 TextRec(param_var_Output_TextFile).OpenFunc             := nil ;
  132.                 TextRec(param_var_Output_TextFile).CloseFunc    := nil ;
  133.                 TextRec(param_var_Output_TextFile).BufSize              := TextRecBufSize;
  134.                 TextRec(param_var_Output_TextFile).Bufptr               := @TextRec(param_var_Output_TextFile).Buffer;
  135.                 TextRec(param_var_Output_TextFile).LineEnd              := #10;
  136.         End; { Procedure Assign_SocketOutput_TextChannel }
  137.  
  138. { ============================================================================ }
  139. { Interface Procedure }
  140. Procedure Assign_SocketFD_to_SocketIO_TextChannels( param_SocketFD: Longint;
  141.                                                                                                 Var param_var_Input_TextFile: Text;
  142.                                                                                                 Var param_var_Output_TextFile: Text );
  143.         Begin
  144.         { First, Assign the reading channel.}
  145.                 Assign_SocketInput_TextChannel( param_SocketFD, param_var_Input_TextFile );
  146.  
  147.         { Now, Assign the writing channel. }
  148.                 Assign_SocketOutput_TextChannel( param_SocketFD, param_var_Output_TextFile );
  149.         End; { Procedure Assign_SocketFD_to_SocketIO_TextChannels }
  150.  
  151. {******************************************************************************
  152. Initialize Unit
  153. ******************************************************************************}
  154. Begin
  155. { nothing }
  156. End.
  157.  
  158.  
  159.  
  160.  


I could also load sample server and client programs. Rather nice is how the client can also bind to a filename.

Sincere thanks. Rick.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Textfiles and unix-domain sockets
« Reply #12 on: March 07, 2022, 09:57:30 pm »
Rather nice is how the client can also bind to a filename.

Yes, but in the case of UD sockets watch out for the case where a filesystem name has been created but there is no longer an associated process. Particularly if deletion requires elevated privilege.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

RickE

  • New Member
  • *
  • Posts: 13
Re: Textfiles and unix-domain sockets
« Reply #13 on: March 08, 2022, 05:06:25 pm »
I hope you find these two little test programs, UD server and client, fun to try compile and run.

Code: Pascal  [Select][+][-]
  1. Program LocalPacketSocket_Server;
  2. {$PACKRECORDS C}
  3.  
  4. Uses BaseUnix, LocalPacketSockets_Text_Unit;
  5.  
  6. Const
  7.         pgm_const_ServerName = 'Socket_LocalServer.soc' ;
  8.         pgm_const_ConnectQueueLimit = 3 ;
  9.  
  10. Var
  11.         pgm_var_SocketFD,
  12.         pgm_var_ConnectFD: Longint;
  13.  
  14.         pgm_var_Connect_ClientName: String[107];
  15.         pgm_var_ConnectCount: Longint;
  16.         pgm_var_Accept_New_Connections: Boolean;
  17.  
  18.         pgm_var_Socket_TextInput,
  19.         pgm_var_Socket_TextOutput: Text;
  20.         pgm_var_Message_String: String[255];
  21.  
  22. { ============================================================================ }
  23. Procedure Handle_Socket_Error( const socket_error_message: string );
  24.         Begin
  25.                 writeln( socket_error_message );
  26.                 halt;
  27.         End;
  28.  
  29. { ============================================================================ }
  30. Begin { Program LocalPacketSocket_Server }
  31. {*** Initialize variables. ***}
  32.         pgm_var_Connect_ClientName := '' ;
  33.         pgm_var_ConnectCount := 0 ;
  34.         pgm_var_Accept_New_Connections := True ;
  35.         pgm_var_Message_String := '' ;
  36.  
  37. {*** Initialize file system. ***}
  38.         FpUnlink( pgm_const_ServerName );
  39.  
  40. { CreateNew Server Socket FileDescriptor. }
  41.         If ( CreateNew_LocalPacketSocket_OK( pgm_var_SocketFD ) = False )
  42.                 Then Handle_Socket_Error( 'CreateNew Socket Error ==>> Quit !! ' );
  43.  
  44. { Bind; Assign Local FileName to Server Socket FileDescriptor. }
  45.         If (Bind_LocalPacketSocket_PathName_OK( pgm_var_SocketFD, pgm_const_ServerName ) = False )
  46.                 Then Handle_Socket_Error( 'Bind Socket Error ==>> Quit !! ' );
  47.  
  48. { Run Unix Local Server }
  49. { Listen; Enable and Wait For New Client ConnectRequests. }
  50.         If ( Listen_LocalPacketSocket_OK( pgm_var_SocketFD, pgm_const_ConnectQueueLimit ) = False )
  51.                 Then Handle_Socket_Error( 'Listen Socket Error ==>> Quit !! ' );
  52.  
  53. { Notify User that Server Program is Waiting for Client ConnectRequests. }
  54.         Writeln;
  55.         Writeln( 'Starting Unix Local Packet Server using FileName = ', pgm_const_ServerName );
  56.         Writeln( 'With Server Socket FileDescriptor = ', pgm_var_SocketFD );
  57.         Writeln( 'Server is now Listening for Connect Requests from Clients.' );
  58.         Writeln( 'Next, run a Unix Local Packet Socket Client connect program in a separate xterm.' );
  59.         Writeln;
  60.  
  61.  
  62. {
  63. The  accept()  system  call  is used with connection-based socket types
  64.     (SOCK_STREAM,  SOCK_SEQPACKET).   It  extracts  the  first   connection
  65.     request  on  the queue of pending connections for the listening socket,
  66.     sockfd, creates a new connected socket, and returns a new file descrip-
  67.     tor  referring  to that socket.  The newly created socket is not in the
  68.     listening state.  The original socket  sockfd  is  unaffected  by  this
  69.     call.
  70.  
  71. If no pending connections are present on the queue, and the  socket  is
  72.     not  marked  as nonblocking, accept() blocks the caller until a connec-
  73.     tion is present.  If the socket is marked nonblocking  and  no  pending
  74.     connections  are  present  on  the queue, accept() fails with the error
  75.     EAGAIN or EWOULDBLOCK.
  76. }
  77.         While pgm_var_Accept_New_Connections
  78.                 Do
  79.                 Begin
  80.                 { Accept Function Waits (default) while Listening for New Client ConnectRequest. }
  81.                 { Accept; Create New Socket Interface and Assign Server New TextStream Socket I/O Channels to New Client. }
  82.  
  83.                         If ( AcceptRequest_NewTextPacketSocket_OK( pgm_var_SocketFD,
  84.                                                                                                                 pgm_var_ConnectFD,
  85.                                                                                                                 pgm_var_Connect_ClientName,
  86.                                                                                                                 pgm_var_Socket_TextInput,
  87.                                                                                                                 pgm_var_Socket_TextOutput ) = True )
  88.  
  89.                         Then    { Use Unix Text Stream Socket Connection }
  90.                                 Begin
  91.                                 { Notify User of Begin Client Connection. }
  92.                                         Writeln ;
  93.                                         Writeln( 'Begin Connect to ClientName = ', pgm_var_Connect_ClientName );
  94.                                         Writeln( 'on Server AcceptConnect FileDescriptor = ', pgm_var_ConnectFD );
  95.                                         Writeln ;
  96.  
  97.                                 { Notify Client program of New Connection. }
  98.                                         Writeln( pgm_var_Socket_TextOutput,
  99.                                                                 pgm_var_Connect_ClientName,
  100.                                                                 ', this is a message from ', pgm_const_ServerName );
  101.  
  102.                                 { Serve the connection until it is finished. }
  103.                                         Repeat  
  104.                                                 Readln( pgm_var_Socket_TextInput, pgm_var_Message_String );
  105.                                                 Writeln( 'Received message "', pgm_var_Message_String, '"' );
  106.                                                 Writeln ;
  107.                                         Until ( pgm_var_Message_String = 'quit' );
  108.  
  109.                                 { Notify Server program of End Client Connection. }
  110.                                         Writeln ;
  111.                                         Writeln( 'End Connect to ClientName = ', pgm_var_Connect_ClientName );
  112.                                         Writeln( 'Close Connection FileDescriptor = ', pgm_var_ConnectFD );
  113.                                         Writeln ;
  114.  
  115.                                 { Close Server AcceptConnect FileDescriptor.}
  116.                                         FpClose( pgm_var_ConnectFD );
  117.  
  118.                                 { Clean up Server Variables. }
  119.                                         pgm_var_Connect_ClientName := '' ;
  120.                                         pgm_var_Accept_New_Connections := False ;
  121.                                 End
  122.  
  123.                         Else
  124.                                 Begin
  125.                                         pgm_var_Accept_New_Connections := False ;
  126.                                         FpUnlink( pgm_const_ServerName );
  127.                                         Handle_Socket_Error( 'Server : Accept : ' );
  128.                                 End;
  129.  
  130.                 End ; { While..Do }
  131.  
  132. { Notify User that Server program is Quitting. }
  133.         Writeln ;
  134.         Writeln( 'Quitting Server using Socket FileDescriptor = ', pgm_var_SocketFD ) ;
  135.         Writeln ;
  136.  
  137. { Remove Named File }
  138.         FpClose( pgm_var_SocketFD ) ;
  139.         FpUnlink( pgm_const_ServerName );
  140.  
  141. End. { Program LocalPacketSocket_Server }
  142.  
  143.  
  144.  
  145.  
  146. Program LocalPacketSocket_ClientLoop;
  147.  
  148. Uses BaseUnix, LocalPacketSockets_Text_Unit;
  149.  
  150. Const
  151.         pgm_const_ServerName = 'Socket_LocalServer.soc' ;
  152.  
  153. Var
  154.         pgm_var_ClientName : String[100] ;
  155.         pgm_var_ClientSocketFD  : Longint;
  156.         pgm_var_Socket_TextInput,
  157.         pgm_var_Socket_TextOutput       : Text;
  158.         pgm_var_Message_String  : String[255];
  159.  
  160. { ============================================================================ }
  161. Procedure Handle_Socket_Error( const socket_error_message : string );
  162.         Begin
  163.                 writeln ( socket_error_message );
  164.                 halt;
  165.         End;
  166.  
  167. { ============================================================================ }
  168. Begin
  169. { Initialize program variables }
  170.         pgm_var_ClientName      := 'Socket_LocalClient.soc' ;
  171.         FpUnlink( pgm_var_ClientName );
  172.  
  173. { CreateNew Socket FileDescriptor. }
  174.         If ( CreateNew_LocalPacketSocket_OK( pgm_var_ClientSocketFD ) = False )
  175.                 Then Handle_Socket_Error( 'CreateNew Socket Error ==>> Quit !! ' );
  176.  
  177. { Bind; Assign Named File to Socket FileDescriptor. }
  178.         If (Bind_LocalPacketSocket_PathName_OK( pgm_var_ClientSocketFD, pgm_var_ClientName ) = False )
  179.                 Then Handle_Socket_Error( 'Bind Socket Error ==>> Quit !! ' );
  180.  
  181. { ConnectRequest; Create New Socket Interface and Assign Client Socket Channels to Named Server. }
  182.         If (ConnectRequest_NewTextPacketSocket_OK( pgm_var_ClientSocketFD,
  183.                                                                                                 pgm_const_ServerName,
  184.                                                                                                 pgm_var_Socket_TextInput,
  185.                                                                                                 pgm_var_Socket_TextOutput) = False)
  186.                 Then Handle_Socket_Error( 'ConnectRequest Socket Error ==>> Quit !! ' );
  187.  
  188. {*** Use New Connection to Server. ***}
  189. { Notify Client program Begin Connection. }
  190.         Writeln ;
  191.         Writeln( pgm_var_ClientName, ' Begin Connect to ServerName = ', pgm_const_ServerName );
  192.         Writeln( 'Client FileDescriptor = ',  pgm_var_ClientSocketFD );
  193.         Writeln ;
  194.  
  195. { Read messages from connected input channel. }
  196.         Readln( pgm_var_Socket_TextInput, pgm_var_Message_String );
  197.         WriteLn( pgm_var_Message_String );
  198.         Writeln ;
  199.  
  200. { Write messages to connected output channel. }
  201.         Writeln( pgm_var_Socket_TextOutput, 'just a quickie' );
  202.         Writeln ;
  203.  
  204. Write( 'Hit return to continue.' );
  205. Readln();
  206.         Writeln( pgm_var_Socket_TextOutput, 'just a longsecond quickie' ) ;
  207.  
  208.         Writeln ;
  209. Write( 'Hit return to continue.' );
  210. Readln();
  211.         Writeln( pgm_var_Socket_TextOutput, 'third quickie' ) ;
  212.  
  213.         Writeln ;
  214. Write( 'Hit return to quit.' );
  215. Readln();
  216.         Writeln( pgm_var_Socket_TextOutput, 'quit' );
  217.  
  218. { Notify Client program End Connection. }
  219.         Writeln ;
  220.         Writeln( pgm_var_ClientName, ' Ending Connect to ServerName = ', pgm_const_ServerName );
  221.         Writeln( 'Close Client FileDescriptor = ',  pgm_var_ClientSocketFD );
  222.         Writeln ;
  223.  
  224. { Close and Remove Client File. }
  225.         FpClose( pgm_var_ClientSocketFD ) ;
  226.         FpUnlink( pgm_var_ClientName );
  227.  
  228. End. { Program LocalPacketSocket_ClientLoop }
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  


It works on my 32 bit opensuse 13.2.

Best of luck with all your ambitions. Most important is to remember the suse motto, "have a lot of fun." I will always be grateful to the suse linux community, the tcl/tk community, and the freepascal community for keeping me what I regard as sane. Amazing treasures.
« Last Edit: March 08, 2022, 05:08:51 pm by RickE »

 

TinyPortal © 2005-2018