Skip to content

Commit df93413

Browse files
committed
Implement handling of process redirection to Null_FD file descriptor
Part V114-021 Change-Id: I16631e549e591f3676abf292b070f63e222b6b0c
1 parent 7c3abef commit df93413

File tree

3 files changed

+132
-15
lines changed

3 files changed

+132
-15
lines changed

src/os/gnatcoll-os-process.adb

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -228,26 +228,20 @@ package body GNATCOLL.OS.Process is
228228
Status : out Integer)
229229
return Ada.Strings.Unbounded.Unbounded_String
230230
is
231-
use type FS.File_Descriptor;
232231
use Ada.Strings.Unbounded;
233232

234233
Pipe_Read, Pipe_Write : OS.FS.File_Descriptor;
235234
Output : Unbounded_String;
236-
Real_Stderr_Fd : OS.FS.File_Descriptor := Stderr;
237235
Buffer : String (1 .. 4096);
238236
Pid : Process_Handle;
239237
N : Integer;
240238
begin
241239
-- Allocate a PIPE to retrieve the output
242240
FS.Open_Pipe (Pipe_Read, Pipe_Write);
243241

244-
if Stderr = FS.To_Stdout then
245-
Real_Stderr_Fd := Pipe_Write;
246-
end if;
247-
248242
-- Start the process.
249243
Pid := Start
250-
(Args, Env, Cwd, Stdin, Pipe_Write, Real_Stderr_Fd, Priority);
244+
(Args, Env, Cwd, Stdin, Pipe_Write, Stderr, Priority);
251245

252246
-- Be sure to close Pipe_Write otherwise following reads on Pipe_Read
253247
-- will block even if the child process ends.
@@ -461,24 +455,75 @@ package body GNATCOLL.OS.Process is
461455
Priority : Priority_Class := INHERIT)
462456
return Process_Handle
463457
is
464-
Result : Process_Handle;
458+
Result : Process_Handle;
459+
460+
-- By default final file descriptors are the one passed by the user
461+
Real_Stdin : OS.FS.File_Descriptor := Stdin;
462+
Real_Stdout : OS.FS.File_Descriptor := Stdout;
463+
Real_Stderr : OS.FS.File_Descriptor := Stderr;
464+
Close_Stdin : Boolean := False;
465+
Close_Stdout : Boolean := False;
466+
Close_Stderr : Boolean := False;
467+
468+
use type OS.FS.File_Descriptor;
465469
begin
466470
GNAT.Task_Lock.Lock;
467471

468-
FS.Set_Close_On_Exec (Stdin, False);
469-
FS.Set_Close_On_Exec (Stdout, False);
470-
FS.Set_Close_On_Exec (Stderr, False);
472+
-- Handle special cases for file descriptors
473+
-- First ensure all file descriptors are valid
474+
if Stdout = OS.FS.Invalid_FD then
475+
raise OS_Error with "invalid fd for process stdout";
476+
elsif Stdout = OS.FS.Null_FD then
477+
Real_Stdout := OS.FS.Open (OS.FS.Null_File, OS.FS.Write_Mode);
478+
Close_Stdout := True;
479+
elsif Stdout = OS.FS.To_Stdout then
480+
raise OS_Error with "cannot redirect stdout to stdout";
481+
end if;
482+
483+
if Stdin = OS.FS.Invalid_FD then
484+
raise OS_Error with "invalid fd for process stdin";
485+
elsif Stdin = OS.FS.Null_FD then
486+
Real_Stdin := OS.FS.Open (OS.FS.Null_File, OS.FS.Read_Mode);
487+
Close_Stdin := True;
488+
elsif Stdin = OS.FS.To_Stdout then
489+
raise OS_Error with "cannot redirect stdin to stdout";
490+
end if;
491+
492+
if Stderr = OS.FS.Invalid_FD then
493+
raise OS_Error with "invalid fd for process stderr";
494+
elsif Stderr = OS.FS.Null_FD then
495+
Real_Stderr := OS.FS.Open (OS.FS.Null_File, OS.FS.Write_Mode);
496+
Close_Stderr := True;
497+
elsif Stderr = OS.FS.To_Stdout then
498+
Real_Stderr := Real_Stdout;
499+
end if;
500+
501+
FS.Set_Close_On_Exec (Real_Stdin, False);
502+
FS.Set_Close_On_Exec (Real_Stdout, False);
503+
FS.Set_Close_On_Exec (Real_Stderr, False);
471504

472505
Result := Internal_Spawn
473506
(Args,
474507
Cwd,
475508
Env,
476-
Stdin, Stdout, Stderr,
509+
Real_Stdin, Real_Stdout, Real_Stderr,
477510
Priority);
478511

479-
FS.Set_Close_On_Exec (Stdin, True);
480-
FS.Set_Close_On_Exec (Stdout, True);
481-
FS.Set_Close_On_Exec (Stderr, True);
512+
FS.Set_Close_On_Exec (Real_Stdin, True);
513+
FS.Set_Close_On_Exec (Real_Stdout, True);
514+
FS.Set_Close_On_Exec (Real_Stderr, True);
515+
516+
if Close_Stdout then
517+
OS.FS.Close (Real_Stdout);
518+
end if;
519+
520+
if Close_Stderr then
521+
OS.FS.Close (Real_Stderr);
522+
end if;
523+
524+
if Close_Stdin then
525+
OS.FS.Close (Real_Stdin);
526+
end if;
482527

483528
GNAT.Task_Lock.Unlock;
484529
return Result;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
with GNATCOLL.OS.Process;
2+
with GNATCOLL.OS.Process_Types;
3+
with GNATCOLL.OS.FS;
4+
with Test_Assert;
5+
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
6+
with Ada.Command_Line;
7+
with GNAT.IO;
8+
9+
function Test return Integer is
10+
package A renames Test_Assert;
11+
package IO renames GNAT.IO;
12+
package OS renames GNATCOLL.OS;
13+
package PT renames GNATCOLL.OS.Process_Types;
14+
package FS renames GNATCOLL.OS.FS;
15+
16+
Args : PT.Arguments;
17+
Env : PT.Environ;
18+
Status : Integer;
19+
Output : Unbounded_String;
20+
begin
21+
PT.Inherit (Env);
22+
23+
if Ada.Command_Line.Argument_Count = 0 then
24+
-- This the topelevel program that check redirections
25+
IO.Put_Line ("Test redirection to null file");
26+
PT.Add_Argument (Args, Ada.Command_Line.Command_Name);
27+
PT.Add_Argument (Args, "to_dev_null");
28+
Output := OS.Process.Run
29+
(Args, Env, Status => Status, Stderr => FS.To_Stdout);
30+
A.Assert (Status, 0, Msg => "program should return 0 as status");
31+
-- This ensure that redirection of stdout and stderr to /dev/null by the
32+
-- child process is not leaked on stdout and or stderr
33+
A.Assert (To_String (Output), "");
34+
PT.Deallocate (Args);
35+
36+
IO.Put_Line ("Test redirection of stderr to stdout");
37+
PT.Add_Argument (Args, Ada.Command_Line.Command_Name);
38+
PT.Add_Argument (Args, "to_stdout");
39+
Output := OS.Process.Run
40+
(Args, Env,
41+
Status => Status,
42+
Stderr => FS.Null_FD,
43+
Strip => True,
44+
Universal_Newline => True);
45+
A.Assert (Status, 0, Msg => "program should return 0 as status");
46+
A.Assert
47+
(To_String (Output), "hello stdout" & ASCII.LF & "hello stderr");
48+
49+
PT.Deallocate (Args);
50+
51+
return A.Report;
52+
53+
elsif Ada.Command_Line.Argument (1) = "to_dev_null" then
54+
-- This the version of the program spawned by itself
55+
PT.Add_Argument (Args, Ada.Command_Line.Command_Name);
56+
PT.Add_Argument (Args, "write_to_stdout_and_stderr");
57+
Status := OS.Process.Run
58+
(Args, Env, Stdout => FS.Null_FD, Stderr => FS.To_Stdout);
59+
60+
elsif Ada.Command_Line.Argument (1) = "to_stdout" then
61+
PT.Add_Argument (Args, Ada.Command_Line.Command_Name);
62+
PT.Add_Argument (Args, "write_to_stdout_and_stderr");
63+
Status := OS.Process.Run (Args, Env, Stderr => FS.To_Stdout);
64+
65+
elsif Ada.Command_Line.Argument (1) = "write_to_stdout_and_stderr" then
66+
IO.Put_Line (IO.Standard_Output, "hello stdout");
67+
IO.Put_Line (IO.Standard_Error, "hello stderr");
68+
end if;
69+
70+
return 0;
71+
end Test;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
title: GNATCOLL.OS.Process Redirections test

0 commit comments

Comments
 (0)