0

spawning a linux command line from an ada program

HI, I need to spawn a linux command line from an ada program the command line is chmod 777 mem.bat (it makes mem.bat executable) from the same ada program where mem.bat is the batch file i need to run just after the creation of mem.bat Just created by the said ada program. here are the main code lines to achieve that goal

-- I use something like  these
 
with Gnat.Os_Lib;
use Gnat.Os_Lib;
Command_1 : constant String := "chmod 777 mem.bat";
Args := Argument_String_To_List(Command_1);
Spawn(Program_Name => Args(Args'First).all,
Args         => Args(Args'First + 1 .. Args'Last),
Success      => Success);
-- but it does not do the job
-- help will be appreciated

-- thanks

1 Answers1

4

I'm not sure if using Spawn directly is the best way forward here. I would choose one of the following three alternatives for achieving your goal:

1. Use GNAT.OS_Lib.Set_Executable

If all you want is to make the file executable, then use GNAT.OS_Lib.Set_Executable:

main_v1.adb

with GNAT.OS_Lib; use GNAT.OS_Lib;

procedure Main_v1 is
   Name : constant String := "mem.bat";
   
begin   
   Set_Executable (Name, Mode => S_Owner);
   Set_Executable (Name, Mode => S_Group);
   Set_Executable (Name, Mode => S_Others);   
   
end Main_v1;

output

$ touch mem.bat; ll mem.bat
-rw-rw-r--. 1 deedee deedee 0 May  9 21:44 mem.bat
$ ./main_v1; ll mem.bat
-rwxrwxr-x. 1 deedee deedee 0 May  9 21:44 mem.bat

2. Use GNAT.Expect

GNAT.Expect is included in the GNAT standard library. An example on its use was already shown in this SO answer. You can rework the example for your particular problem.

main_v2.adb

with Ada.Text_IO;
with GNAT.Expect;

procedure Main_v2 is

   Command    : constant String := "chmod";
   Argument_1 : aliased String  := "777";
   Argument_2 : aliased String  := "mem.bat";
   Input      : constant String := "";
   Status     : aliased Integer := 0;

   --  Execute the command and retrieve the output.

   Output : String :=
              GNAT.Expect.Get_Command_Output
                (Command    => Command,
                 Arguments  => (1 => Argument_1'Unchecked_Access,
                                2 => Argument_2'Unchecked_Access),
                 Input      => Input,
                 Status     => Status'Access,
                 Err_To_Out => True);

   --  NOTE: Cheating with Unchecked_Access, OK for demo. You may want
   --        to properly new and Free these strings (see Argument_List
   --        type in package GNAT.OS_Lib).

begin
   if Status /= 0 then
      Ada.Text_IO.Put_Line ("chmod failed: " & Output);
   end if;
   
end Main_v2;

output

$ touch mem.bat && ll mem.bat 
-rw-rw-r--. 1 deedee deedee 0 May  9 21:15 mem.bat
$ ./main_v2 && ll mem.bat
-rwxrwxrwx. 1 deedee deedee 0 May  9 21:15 mem.bat

3. Use Florist

As chmod is a POSIX function, you could also consider to use the Florist library. The Florist library is readily available in distributions like Debian (libflorist), in Alire (only when running on Debian or Ubuntu), or otherwise as source from this repository on GitHub provided by AdaCore.

main_v3.adb

with POSIX.Files;
with POSIX.Permissions;

procedure Main_v3 is
   use POSIX.Permissions;
   
   New_Permissions : constant POSIX.Permissions.Permission_Set :=
     (Owner_Read     => True,
      Owner_Write    => True,
      Owner_Execute  => True,
      Group_Read     => True,
      Group_Write    => True,
      Group_Execute  => True,
      Others_Read    => True,
      Others_Write   => True,
      Others_Execute => True,
      others         => False);
   
begin
   POSIX.Files.Change_Permissions
     (Pathname   => "mem.bat",
      Permission => New_Permissions);
   
end Main_v3;

output

$ touch mem.bat && ll mem.bat 
-rw-rw-r--. 1 deedee deedee 0 May  9 21:16 mem.bat
$ ./main_v3 && ll mem.bat
-rwxrwxrwx. 1 deedee deedee 0 May  9 21:16 mem.bat

4. Importing chmod as system call

You can also just import the chmod directly as a system call. See also man 3p chmod (or here) for its C signature. Ada has excellent facilities to import C programs as shown in the example below.

main_v4.adb

with Ada.Text_IO;
with Interfaces.C;
with GNAT.OS_Lib;

procedure Main_v4 is

   package C renames Interfaces.C;
   use type C.int;  --  Make operators of C.int (like "<") directly visible.
   
   subtype mode_t is C.unsigned;
      
   function chmod (path : C.char_array; mode : mode_t) return C.int
     with Import, Convention => C;
   
   path : aliased C.char_array := C.To_C ("mem.bat");
   
   result : C.int;
   
begin  
   result := chmod (path, 8#777#);   --  777 as octal number (base-8)
   if result < 0 then
      Ada.Text_IO.Put_Line ("chmod failed: " & GNAT.OS_Lib.Errno_Message);
   end if;
   
end Main_v4;

output

$ touch mem.bat && ll mem.bat 
-rw-rw-r--. 1 deedee deedee 0 May  9 21:17 mem.bat
$ ./main_v4 && ll mem.bat
-rwxrwxrwx. 1 deedee deedee 0 May  9 21:17 mem.bat
DeeDee
  • 5,654
  • 7
  • 14