This is an appendix to Understanding Unix/Linux Programming written for people interested in Ada programming on POSIX systems.

Users, Files, and the Manual: who Is First

Section 2.5.3, page 43

Here is a first draft of who: who1.adb

Compile it like this:

% gnatmake -P who1

Notice that we have switched to using a project file. We do that to keep the extra command line arguments to gnatmake in one file.

Run the program, and compare it with the system version of who:

% ./who1
[...]
% who
[...]
% 

Section 2.5.5, page 48

Here is the finished version of who2.adb.

Compile and test this version:

% gnatmake -P who2
[...]
% ./who2
sparre   pts/1    Jan 15 15:28 (:20.0)
sparre   pts/2    Jan 15 15:29 (:20.0)
sparre   pts/0    Jan 15 15:35 (:20.0)
sparre   pts/3    Jan 15 15:34 (:20.0)
sparre   pts/4    Jan 16 06:51 (:20.0)
sparre   :20      Jan 13 13:25
% who
sparre   pts/1        2007-01-15 15:28 (:20.0)
sparre   pts/2        2007-01-15 15:29 (:20.0)
sparre   pts/0        2007-01-15 15:35 (:20.0)
sparre   pts/3        2007-01-15 15:34 (:20.0)
sparre   pts/4        2007-01-16 06:51 (:20.0)
sparre   :20          2007-01-13 13:25
%

Section 2.6.3, page 53

Finally, here is code that implements the picture: cp1.adb

Compile and test the program:

% gnatmake -P cp1
[...]
% ./cp1 cp1 copy.of.cp1
% ls -l cp1 copy.of.cp1
-rw-r--r--  1 sparre sparre 41061 2007-01-16 09:28 copy.of.cp1
-rwxr-xr-x  1 sparre sparre 41061 2007-01-16 09:25 cp1
%

Section 2.7, pages 55-61

When we want to read from a file, the most efficient way of accessing it, is to ask the operating system to pretend that the file is actually a part of the memory allocated to our program. We do that using one of the POSIX.Memory_Mapping.Map_Memory functions.

What makes this much more efficient is first of all that we leave the job of copying data to and from disk to the operating system. This is something the operating system controls anyway, so we can just as well leave all the control to it.

The second thing that makes this much more efficient is that we don't have to make as many system calls. The reading of the UTMP data has been cut down to four system calls. In the other versions there are two system calls plus one system call per entry in the UTMP file (here on my laptop this adds up to 34 system calls).

It is only the core of procedure Who we have to change. The Show procedures are just the same.

We need an object to handle the access to the UTMP file (UTMP_File, we need to store the size of the file as we map it into memory (UTMP_File_Size) and we need the address of the file data in our memory space (UTMP_Memory):

   UTMP_File      : File_Descriptor;
   UTMP_File_Size : System.Storage_Elements.Storage_Offset;
   UTMP_Memory    : System.Address;

We open the UTML file, read its size, and ask to have it mapped into our process memory:

begin
   UTMP_File := Open (Name => To_POSIX_String (POSIX.UTMP.File_Name),
                      Mode => Read_Only);
   UTMP_File_Size := Storage_Offset (File_Size (UTMP_File));
   UTMP_Memory := Map_Memory (Length     => UTMP_File_Size,
                              Protection => Allow_Read,
                              Mapping    => Map_Shared,
                              File       => UTMP_File,
                              Offset     => 0);

Now we pretend to create an array of UTMP records, and locate it at the same place in memory as the UTMP file:

   declare
      Bit_Count    : constant Natural :=
                       Natural (UTMP_File_Size) * Storage_Element'Size;
      Record_Count : constant Natural :=
                       Bit_Count / POSIX.UTMP.Instance'Size;

      Records : array (1 .. Record_Count) of POSIX.UTMP.Instance;
      for Records'Address use UTMP_Memory;

Finally we loop through the array, and show the records in it:

   begin
      for Index in Records'Range loop
         Show (Item => Records (Index));
      end loop;
   end;

   Unmap_Memory (First  => UTMP_Memory,
                 Length => UTMP_File_Size);
   Close (File => UTMP_File);
end Who;

The complete source code is available as who4.adb. Please build and test it. How does it compare with the system who program?

Compile and test this version:

% gnatmake -P who4
[...]
% ./who4
christin :0       Feb  9 18:21
sparre   :20      Feb  9 19:00
sparre   pts/1    Feb 21 13:17 (:20.0)
sparre   pts/0    Feb 21 12:59 (:20.0)
sparre   pts/2    Feb 21 13:12 (:20.0)
sparre   pts/7    Feb 21 09:05 (:20.0)
sparre   pts/8    Feb 21 09:05 (:20.0)
sparre   pts/6    Feb 21 09:05 (:20.0)
% who
christina :0           2007-02-09 18:21
sparre   :20          2007-02-09 19:00
sparre   pts/1        2007-02-21 13:17 (:20.0)
sparre   pts/0        2007-02-21 12:59 (:20.0)
sparre   pts/2        2007-02-21 13:12 (:20.0)
sparre   pts/7        2007-02-21 09:05 (:20.0)
sparre   pts/8        2007-02-21 09:05 (:20.0)
sparre   pts/6        2007-02-21 09:05 (:20.0)
%
% LANG=C who
christina :0           Feb  9 18:21
sparre   :20          Feb  9 19:00
sparre   pts/1        Feb 21 13:17 (:20.0)
sparre   pts/0        Feb 21 12:59 (:20.0)
sparre   pts/2        Feb 21 13:12 (:20.0)
sparre   pts/7        Feb 21 09:05 (:20.0)
sparre   pts/8        Feb 21 09:05 (:20.0)
sparre   pts/6        Feb 21 09:05 (:20.0)
%

The files who3.adb, utmp_buffer.ads and utmp_buffer.adb are included for direct comparison with the examples in the book. Please remember that buffered I/O is generally less efficient than memory mapping of files.

Section 2.9.3, page 64

package POSIX.IO is
   [...]
   type Position is
     (From_Beginning, From_Current_Position, From_End_Of_File);
   procedure Seek
     (File           : in     File_Descriptor;
      Offset         : in     IO_Offset;
      Result         :    out IO_Offset;
      Starting_Point : in     Position := From_Beginning);
   [...]
end POSIX.IO;

Explorations, pages 68-69

2.9, Jumping off the end of a file

The procedure POSIX.IO.Seek allows you to set the current position to locations after the end of the file. [...]

Programming Exercises, pages 69-70

2.10, Identity crisis

Use who4.adb as a basis for your who am i implementation.

If you want to implement whoami, you may want to look at the specifications for package POSIX.Process_Identification and package POSIX.User_Database.

2.12, Files vs. API's

Ignore this.

2.13, Buffering and seeking

Ignore this.

A Final Puzzler: the tail command, page 70

Consider using the functions POSIX.Memory_Mapping.Map_Memory and Ada.Strings.Fixed.Index for this problem.

Compare the speed of your implementation of tail with that of the system version.

-- 

Go to next chapter.

Collected examples:
posix-in-ada.zip
Based on:
Understanding Unix/Linux Programming, Bruce Molay, ISBN 0-13-008396-8.
Main page:
http://edb.jacob-sparre.dk/Posix_in_Ada/
Written by:
Jacob Sparre Andersen.
Latest update:
19th of April, 2008