Finding Running
Processes by Executable Name using Linux
Overview:
This post will illustrate a way for an application process
to check if any particular known application process is currently running in a
Linux operating environment. Variations
of the method could be used to find all running processes or all currently
running executables.
This came up since I was completing the move of my
Exploratory Project (EP) from running under Windows to running under
Linux. Recent posts have provided some
of the Linux interfaces that I needed to replace Win32 interfaces. These allowed me to almost complete the
Linux version of the EP application.
The EP applications communicate with each other via named
pipes – Microsoft Pipes for Windows – which will become fifo named pipes for
Linux. In finishing up I came to a
Win32 interface that I used to check if a remote application process in the
configuration of applications is running prior to attempting to connect to the
application.
In my Linux named pipe development I found that I could have
the applications connect (that is, open their common named pipe and
communicate) without first determining whether the other (that is, remote)
application of the a configuration pair was running – just continue to try until
it was running and had opened its end of the pipe. However, since I want to be able to have essentially the same
code usable by Windows and Linux – other than switching the routines of an
operating system/executive layer – I wanted to be able to detect if a specific
remote application was running prior to attempting to connect to it.
Finding an appropriate Linux function proved to be a little
more difficult than replacing the Win32 routines to create threads, enter a
critical region, do disk file opens, reads, writes, etc or obtain the current
time. That is, my internet searches for
how to accomplish this just kept returning how to do it from the console
terminal rather than via an application.
There was even one set of blog postings in response to an individual's
question on the subject that pretty much ridiculed him for wanting to while
telling him it wasn't possible.
However, by using various hints that I came across and Linux
man pages that some of them referenced in one way or another, I was able to
work out an approach that was successful.
The resulting function will be presented below.
Design:
This particular function (Is_Running) is implemented in the
way that it is because the EP has a configuration file that specified the
applications that can make up the complete configuration. One item of information in each
application's record in the file is the directory/folder path of the
application executable including the application's name. This information can be used for two
purposes; 1) for a running application to check whether the remote application
of a potential application pair is running, and 2) with later versions of the
Windows interface, to launch the applications that aren't running. [So much for the question raiser's hecklers
who couldn't figure out how such functions could be useful.]
In any case the Is_Running function is provided with an
executable path that, in the EP case would come from the configuration file,
that the function checks against the running processes to determine if it is
one of them. If so, it stops checking
and returns true. If not it returns
false after checking all the running processes. This, of course, allows a window where an application could be
either launched just the check was completed or aborted just afterwards but
which isn't important to the way I use it.
Thus, for instance, in the tests of the function, the test2
application makes the call
if
Exec_Itf.Is_Running( Path => "/home/clayton/Source/Test/test1.exe"
) then
Console.Write( "Other app, test1, is running" );
else
Console.Write( "test1 is NOT running" );
end if;
to check whether the test1 application is running. If test1 has been started before test2 than
the "test1, is running" appears on the terminal. Otherwise, the "test1 is NOT
running" text appears.
Implementation:
The Is_Running function was placed in the exec_itf Ada files
(spec and body) as part of the operating system interface functions.
It uses the following Linux interfaces:
1) opendir to open the "/proc" process directory,
2) readdir to return each entry in succession,
3) readlink to get the executable path name associated with
the process, if any.
The /proc directory is, of course, a special use directory
that is part of the Linux design and used to store information about the
running processes. The readdir returns
a different directory entry each time it is called after the open until a null
pointer is returned.
I found out that readlink uses various lookup name suffixes
to return different kinds of information.
With a /exe suffix the executable path is returned (if the process has
an executable). That is, the process
name is passed as /proc/pid/exe where pid is the directory entity process
identifier (that is, name) of the structure pointed to by the directory entry
pointer returned by readdir.
So, voila, just compare the returned executable path name
(when its length matches that of the path being searched and hence is greater
than 0 indicating that there is such a path) to the path supplied in the call to
Is_Running. If a match is found, the
process is running.
function Is_Running
( Path : String
--| Full path of executable
) return Boolean;
--| Return True if executable is currently running
-- ++
--| Overview:
--| Determine if the executable named by the Path is currently running.
--
--| Notes:
--| o It is expected that Path will be the full path including the
--| trailing ".exe" of the executable.
--| o The function iterates through the /proc directory to obtain the
--| process identifiers of the running processes and, for each one
--| for which ReadLink returns a value for an executable path, it
--| compares that value with the supplied Path. If it matches, True
--| is returned.
-- --
is_running code:
--| Linux note:
--| The only fields in the dirent structure that are mandated by POSIX.1 are:
--| o d_name[], of unspecified size, with at most NAME_MAX characters
--| preceding the terminating null byte; and
--| o (as an XSI extension) d_ino.
--| The other fields are unstandardized, and not present on all systems.
is record
d_ino : Machine.Unsigned_Longword; -- File system i-node number
-- The file serial number, which distinguishes this file from all other
-- files on the same device.
d_off : Machine.Unsigned_Longword; -- i.e., of type off_t
-- File offset, measured in bytes from the beginning of a file or device.
-- off_t is normally defined as a signed, 32-bit integer. In the
-- programming environment which enables large files, off_t is defined
-- to be a signed, 64-bit integer.
d_reclen : Machine.Unsigned_Word;
d_type : Machine.Unsigned_Byte;
d_name : String(1..256); -- null terminated
end record;
type Dir_Entity_Ptr_Type is access Dir_Entity_Struct_Type;
function OpenDir
( File_Name : String
--| Name of directory; null terminated
) return System.Address;
--| Pointer to opened directory
pragma Import (C, OpenDir, "__gnat_opendir");
function ReadDir
( Directory : System.Address
--| Pointer to opened directory
) return Dir_Entity_Ptr_Type;
--| Return pointer to directory entity
pragma Import( C, ReadDir, "readdir" );
function ReadLink
( Proc : System.Address;
--| Pointer to null terminated string
Buffer : System.Address;
--| Pointer to buffer for path
Length : Integer
--| Length of buffer
) return Integer;
--| Return number of characters output to Buffer
pragma Import( C, ReadLink, "readlink" );
function Is_Running
( Path : String
--| Full path of executable
) return Boolean is
Dir_Entity
--| Pointer to process directory entity
: Dir_Entity_Ptr_Type := null;
Dir_Proc
--| Pointer to process directory
: System.Address := System.Null_Address;
Lookup_Name
--| Process id for /proc lookup
: String(1..50);
Name_Len
--| Number of characters in name
: Integer;
Proc_Directory
--| Null-terminated name of directory containing running processes
: String(1..10) := ( others => ASCII.NUL );
Running_Process
--| Path of running process
: String(1..256);
use type String_Tools.Comparison_Type;
use type System.Address;
begin -- Is_Running
--| Logic_Step:
--| Open the /proc directory.
Proc_Directory(1..6) := "/proc/"; -- nul terminated
Dir_Proc := OpenDir( File_Name => Proc_Directory );
if Dir_Proc = System.Null_Address then
Console.Write_Error( "Couldn't open the /proc/ directory" );
return False;
end if;
--| Logic_Step:
--| Search through the processes looking for the one with the
--| specified path and executable name.
loop
-- Read next item
Dir_Entity := ReadDir(Dir_Proc);
exit when Dir_Entity = null;
-- Form lookup name
Name_Len := 0;
for I in 1..Dir_Entity.d_name'length loop
exit when Dir_Entity.d_name(I) = ASCII.NUL; -- trailing NUL
Name_Len := I; -- set length of name to last non-NUL character
end loop;
if Name_Len > 0 then
Lookup_Name(1..6) := "/proc/";
Lookup_Name(7..6+Name_Len) := Dir_Entity.d_name(1..Name_Len);
Lookup_Name(7+Name_Len..10+Name_Len) := "/exe";
Lookup_Name(11+Name_Len) := ASCII.NUL; -- trailing NUL
-- Get path
Name_Len := ReadLink( Lookup_Name'address,
Running_Process'address,
Running_Process'length );
-- Compare path of running executable with that input
if Name_Len = Path'length and then
String_Tools.Blind_Compare
( Left => Running_Process(1..Name_Len),
Right => Path ) = String_Tools.Equal
then
return True; -- process is running
end if;
end if; -- Name_Len > 0
end loop;
return False; -- Path not found in running processes
end Is_Running;
No comments:
Post a Comment