Parcourir la source

Restrict output to 10 million bytes or 1 million lines

John Stevenson il y a 1 an
Parent
commit
a2f8d6bfca
2 fichiers modifiés avec 34 ajouts et 42 suppressions
  1. 4 2
      ISHelp/isxfunc.xml
  2. 30 40
      Projects/Src/CmnFunc2.pas

+ 4 - 2
ISHelp/isxfunc.xml

@@ -2885,7 +2885,8 @@ end;</pre></example>
     Error: Boolean;
     Error: Boolean;
   end;
   end;
 </pre>
 </pre>
-<p>Error will be True if there was an error reading the output (which should be very rare) or if the total output size exceeded 10mb. The error is logged in Setup's or Uninstall's log file and/or in the Compiler IDE's "Debug Output" view. There is no further output after an error.</p></remarks>
+<p>Error will be True if there was an error reading the output (which should be very rare) or if the output is too big. The error is logged in Setup's or Uninstall's log file and/or in the Compiler IDE's "Debug Output" view. There is no further output after an error.</p>
+<p>Output is limited to a total size of 10 million bytes or a maximum of 1 million lines.</p></remarks>
         <example><pre>var
         <example><pre>var
   ResultCode: Integer;
   ResultCode: Integer;
   Output: TExecOutput;
   Output: TExecOutput;
@@ -2918,7 +2919,8 @@ end;</pre></example>
 <p>TOnLog is defined as:</p>
 <p>TOnLog is defined as:</p>
 <p><tt>TOnLog = procedure(const S: String; const Error, FirstLine: Boolean);</tt></p>
 <p><tt>TOnLog = procedure(const S: String; const Error, FirstLine: Boolean);</tt></p>
 <p>Parameter S is the output line when Error is False, otherwise an error message. FirstLine is True if this is the first line of output from the program, otherwise False.</p>
 <p>Parameter S is the output line when Error is False, otherwise an error message. FirstLine is True if this is the first line of output from the program, otherwise False.</p>
-<p>Error will be True if reading the output failed (which should be very rare), or if the output size exceeded 10mb. There is no further output after an error.</p></remarks>
+<p>Error will be True if reading the output failed (which should be very rare), or if the output is too big. There is no further output after an error.</p>
+<p>Output is limited to a total size of 10 million bytes or a maximum of 1 million lines.</p></remarks>
         <example><pre>var
         <example><pre>var
   Line: String;
   Line: String;
 
 

+ 30 - 40
Projects/Src/CmnFunc2.pas

@@ -40,7 +40,9 @@ type
   private
   private
     FOKToRead: Boolean;
     FOKToRead: Boolean;
     FMaxTotalBytesToRead: Cardinal;
     FMaxTotalBytesToRead: Cardinal;
+    FMaxTotalLinesToRead: Cardinal;
     FTotalBytesRead: Cardinal;
     FTotalBytesRead: Cardinal;
+    FTotalLinesRead: Cardinal;
     FStdInNulDevice: THandle;
     FStdInNulDevice: THandle;
     FStdOutPipeRead: THandle;
     FStdOutPipeRead: THandle;
     FStdOutPipeWrite: THandle;
     FStdOutPipeWrite: THandle;
@@ -51,7 +53,6 @@ type
     FReadOutBuffer: AnsiString;
     FReadOutBuffer: AnsiString;
     FReadErrBuffer: AnsiString;
     FReadErrBuffer: AnsiString;
     FNextLineIsFirstLine: Boolean;
     FNextLineIsFirstLine: Boolean;
-    FRedirectionError: String;
     FMode: TOutputMode;
     FMode: TOutputMode;
     FCaptureOutList: TStringList;
     FCaptureOutList: TStringList;
     FCaptureErrList: TStringList;
     FCaptureErrList: TStringList;
@@ -67,7 +68,6 @@ type
     procedure UpdateStartupInfo(var StartupInfo: TStartupInfo);
     procedure UpdateStartupInfo(var StartupInfo: TStartupInfo);
     procedure NotifyCreateProcessDone;
     procedure NotifyCreateProcessDone;
     procedure Read(const LastRead: Boolean);
     procedure Read(const LastRead: Boolean);
-    property MaxTotalBytesToRead: Cardinal read FMaxTotalBytesToRead write FMaxTotalBytesToRead;
     property CaptureOutList: TStringList read GetCaptureOutList;
     property CaptureOutList: TStringList read GetCaptureOutList;
     property CaptureErrList: TStringList read GetCaptureErrList;
     property CaptureErrList: TStringList read GetCaptureErrList;
     property CaptureError: Boolean read FCaptureError;
     property CaptureError: Boolean read FCaptureError;
@@ -1613,20 +1613,17 @@ end;
 constructor TCreateProcessOutputReader.Create(const ALogProc: TLogProc;
 constructor TCreateProcessOutputReader.Create(const ALogProc: TLogProc;
   const ALogProcData: NativeInt; AMode: TOutputMode = omLog);
   const ALogProcData: NativeInt; AMode: TOutputMode = omLog);
 
 
-  procedure SetRedirectionError(const S: String; const Args: array of const);
+  procedure PipeCreate(var Read, Write: THandle; SecurityAttr: TSecurityAttributes);
   begin
   begin
-    FRedirectionError := Format(S, Args);
-  end;
+    var TempReadPipe, TempWritePipe: THandle;
+    if not CreatePipe(TempReadPipe, TempWritePipe, @SecurityAttr, 0) then
+      raise Exception.CreateFmt('Output redirection error: CreatePipe failed (%d)', [GetLastError]);
 
 
-  function PipeCreate(var Read, Write: THandle; SecurityAttr: TSecurityAttributes): Boolean;
-  begin
-    Result := False;
-    if not CreatePipe(Read, Write, @SecurityAttr, 0) then
-      SetRedirectionError('CreatePipe failed (%d).', [GetLastError])
-    else if not SetHandleInformation(Read, HANDLE_FLAG_INHERIT, 0) then
-      SetRedirectionError('SetHandleInformation failed (%d).', [GetLastError])
-    else
-      Result := True;
+    if not SetHandleInformation(TempReadPipe, HANDLE_FLAG_INHERIT, 0) then
+      raise Exception.CreateFmt('Output redirection error: SetHandleInformation failed (%d)', [GetLastError]);
+
+    Read := TempReadPipe;
+    Write := TempWritePipe;
   end;
   end;
 
 
 begin
 begin
@@ -1652,30 +1649,17 @@ begin
     FILE_SHARE_READ or FILE_SHARE_WRITE, @SecurityAttributes,
     FILE_SHARE_READ or FILE_SHARE_WRITE, @SecurityAttributes,
     OPEN_EXISTING, 0, 0);
     OPEN_EXISTING, 0, 0);
   if NulDevice = INVALID_HANDLE_VALUE then
   if NulDevice = INVALID_HANDLE_VALUE then
-    SetRedirectionError('CreateFile failed (%d).', [GetLastError])
-  else begin
-    FStdInNulDevice := NulDevice;
-    var PipeRead, PipeWrite: THandle;
-    if PipeCreate(PipeRead, PipeWrite, SecurityAttributes) then
-    begin
-      FStdOutPipeRead := PipeRead;
-      FStdOutPipeWrite := PipeWrite;
-
-      if FMode = omLog then begin
-        FOkToRead := True;
-        FMaxTotalBytesToRead := 10*1024*1024;
-      end
-      else if PipeCreate(PipeRead, PipeWrite, SecurityAttributes) then begin
-        FStdErrPipeRead := PipeRead;
-        FStdErrPipeWrite := PipeWrite;
-        FOkToRead := True;
-        FMaxTotalBytesToRead := 10*1024*1024;
-      end;
-    end;
-  end;
+    raise Exception.CreateFmt('Output redirection error: CreateFile failed (%d)', [GetLastError]);
+
+  FStdInNulDevice := NulDevice;
+  PipeCreate(FStdOutPipeRead, FStdOutPipeWrite, SecurityAttributes);
 
 
-  if not FOKToRead then
-    raise Exception.Create(Format('Output redirection error: %s', [FRedirectionError]));
+  if FMode = omCapture then
+    PipeCreate(FStdErrPipeRead, FStdErrPipeWrite, SecurityAttributes);
+
+  FOkToRead := True;
+  FMaxTotalBytesToRead := 10*1000*1000;
+  FMaxTotalLinesToRead := 1000*1000;
 end;
 end;
 
 
 destructor TCreateProcessOutputReader.Destroy;
 destructor TCreateProcessOutputReader.Destroy;
@@ -1797,7 +1781,7 @@ begin
         LogErrorFmt('ReadFile failed (%d).', [GetLastError]);
         LogErrorFmt('ReadFile failed (%d).', [GetLastError]);
         { Restore back to original size }
         { Restore back to original size }
         SetLength(Buffer, TotalBytesHave);
         SetLength(Buffer, TotalBytesHave);
-      end else if BytesRead > 0 then begin
+      end else begin
         { Correct length if less bytes were read than requested }
         { Correct length if less bytes were read than requested }
         SetLength(Buffer, TotalBytesHave+BytesRead);
         SetLength(Buffer, TotalBytesHave+BytesRead);
 
 
@@ -1805,6 +1789,7 @@ begin
         var P := FindNewLine(Buffer, LastRead);
         var P := FindNewLine(Buffer, LastRead);
         while P <> 0 do begin
         while P <> 0 do begin
           LogLine(Copy(Buffer, 1, P-1));
           LogLine(Copy(Buffer, 1, P-1));
+          Inc(FTotalLinesRead);
           if (Buffer[P] = #13) and (P < Length(Buffer)) and (Buffer[P+1] = #10) then
           if (Buffer[P] = #13) and (P < Length(Buffer)) and (Buffer[P+1] = #10) then
             Inc(P);
             Inc(P);
           Delete(Buffer, 1, P);
           Delete(Buffer, 1, P);
@@ -1812,7 +1797,8 @@ begin
         end;
         end;
 
 
         Inc(FTotalBytesRead, BytesRead);
         Inc(FTotalBytesRead, BytesRead);
-        if FTotalBytesRead >= FMaxTotalBytesToRead then begin
+        if (FTotalBytesRead >= FMaxTotalBytesToRead) or
+           (FTotalLinesRead >= FMaxTotalLinesToRead) then begin
           { Read limit reached: break the pipe, throw away the incomplete line, and log an error }
           { Read limit reached: break the pipe, throw away the incomplete line, and log an error }
           FOKToRead := False;
           FOKToRead := False;
           if FMode = omLog then
           if FMode = omLog then
@@ -1821,7 +1807,11 @@ begin
             FReadOutBuffer := '';
             FReadOutBuffer := '';
             FReadErrBuffer := '';
             FReadErrBuffer := '';
           end;
           end;
-          LogErrorFmt('Maximum output length (%d) reached, ignoring remainder.', [FMaxTotalBytesToRead]);
+
+          if FTotalBytesRead >= FMaxTotalBytesToRead then
+            LogErrorFmt('Maximum output length (%d) reached, ignoring remainder.', [FMaxTotalBytesToRead])
+          else
+            LogErrorFmt('Maximum output lines (%d) reached, ignoring remainder.', [FMaxTotalLinesToRead]);
         end;
         end;
       end;
       end;
     end;
     end;