Learn How To Execute External Commands On Linux From An Auto Tables For RAD Server API Endpoint

Posted by on in Blogs

Auto Tables for RAD Server is an automated solution for generating a REST API for your database. However, in addition to that it supports defining custom methods for an endpoint. Inside of this custom method you can call external commands on Windows. You can also call external commands when the RAD Server instance is hosted on Linux using the code I will show here from StackOverFlow.

Once you create your Endpoint in the Auto Tables Editor and the Endpoint is selected you can go to the Endpoint Details tab. Change the Action field to Method. Enter the name of your method that you want to call in the Method field. In this case you can use customMethod which is a built in method already in the source code you can use as a sample. At runtime the customMethod() function will be called inside RAD Server. The sample customMethod looks like this:

//
//
function TAutoTablesResource.customMethod(AContext: TEndpointContext; ARequest: TEndpointRequest; AResponse: TEndpointResponse): TMemoryStream;
var
StatusResult, StatusMessage: String;
begin
  try
{$IFDEF MSWINDOWS}
    StatusMessage := GetDosOutput('schtasks','');
{$ENDIF}
  except
    on E: Exception do
     begin
       StatusMessage := E.Message;
     end;
  end;
  Result := TMemoryStream.Create;
  StatusResult := 'ok';
  SetResponseStatus(StatusResult,StatusMessage,Result);
end;

As you can see using the IFDEF on Windows it will call the GetDosOutput() function which will call a command line on Windows. Be aware that permissions apply when calling the command line so if you are running as an ISAPI or Apache Module make sure you permissions are setup correctly for this to work otherwise it will be ignored. If you want to call a command line on Linux you'll have to add your helper own function to do that and we have that function right here. This is one version of the code from the StackOverflow entry (an original version of the code was created by Craig Chapman):

//
//
uses
  System.SysUtils,
  System.Classes,
  Posix.Base,
  Posix.Fcntl;

type
  TStreamHandle = pointer;

  TLinuxUtils = class
  public
    class function RunCommandLine(ACommand : string) : TStringList;overload;
    class function RunCommandLine(Acommand : string; Return : TProc<String>) : boolean; overload;
    class function findParameter(AParameter : string) : boolean;
  end;



  function popen(const command: MarshaledAString; const _type: MarshaledAString): TStreamHandle; cdecl; external libc name _PU + 'popen';
  function pclose(filehandle: TStreamHandle): int32; cdecl; external libc name _PU + 'pclose';
  function fgets(buffer: pointer; size: int32; Stream: TStreamHAndle): pointer; cdecl; external libc name _PU + 'fgets';


implementation

class function TLinuxUtils.RunCommandLine(ACommand : string) : TStringList;
var
  Handle: TStreamHandle;
  Data: array[0..511] of uint8;
  M : TMarshaller;

begin
  Result := TStringList.Create;
  try
    Handle := popen(M.AsAnsi(PWideChar(ACommand)).ToPointer,'r');
    try
      while fgets(@data[0],Sizeof(Data),Handle)<>nil do begin
        Result.Add(Copy(UTF8ToString(@Data[0]),1,UTF8ToString(@Data[0]).Length -1));//,sizeof(Data)));
      end;
    finally
      pclose(Handle);
    end;
  except
    on E: Exception do
      Result.Add(E.ClassName + ': ' + E.Message);
  end;
end;

class function TLinuxUtils.RunCommandLine(Acommand : string; Return : TProc<string>) : boolean;
var
  Handle: TStreamHandle;
  Data: array[0..511] of uint8;
  M : TMarshaller;

begin
  Result := false;
  try
    Handle := popen(M.AsAnsi(PWideChar(ACommand)).ToPointer,'r');
    try
      while fgets(@data[0],Sizeof(Data),Handle)<>nil do begin
        Return(Copy(UTF8ToString(@Data[0]),1,UTF8ToString(@Data[0]).Length -1));//,sizeof(Data)));
      end;
    finally
      pclose(Handle);
    end;
  except
    on E: Exception do
      Return(E.ClassName + ': ' + E.Message);
  end;
end;

class function TLinuxUtils.findParameter(AParameter : string) : boolean;
var
  I : Integer;
begin
  Result := false;
  for I := 0 to Pred(ParamCount) do
  begin
    Result := AParameter.ToUpper = ParamStr(i).ToUpper;
    if Result then
      Break;
  end;
end;


And that is all there is to it. You can easily expose database tables through Auto Tables for RAD Server or run your own custom code.

Find out more about building your REST APIs automatically using Auto Tables for RAD Server which generates client/server/OpenAPI.

 



Comments

Check out more tips and tricks in this development video: