Loading...
Searching...
No Matches
process_posix.f90
Go to the documentation of this file.
1!> @file
2!! @defgroup group_process_posix POSIX process utilities
3!! Low-level POSIX implementation of process inspection routines.
4!!
5!! This module provides helper procedures used internally by
6!! `fpx_process` to inspect running processes on Unix-like systems.
7!!
8!! Detection strategy:
9!!
10!! 1. `/proc/<pid>/comm` (Linux only, fastest)
11!! 2. `ps`
12!!
13!! `/proc` is preferred because it requires no subprocess creation and
14!! returns only the executable name.
15!!
16!! The `ps` fallback works on Linux, macOS, FreeBSD, OpenBSD,
17!! Solaris and virtually every POSIX implementation.
18!!
19!! @ingroup group_process
20module fpx_process_posix
21 use, intrinsic :: iso_fortran_env
22
23 implicit none; private
24
25 public :: get_process_name_posix, &
27
28contains
29
30 !> @brief Return parent PID (PPID) on POSIX systems
31 !!
32 !! Minimal, deterministic implementation based on /proc/self/status.
33 !! No shell, no parsing hacks, no external dependencies.
34 !! @ingroup group_process_posix
35 integer function get_parent_pid_posix() result(ppid)
36 !private
37 integer :: u, ios
38 character(256) :: line
39
40 ppid = -1
41
42 open(newunit=u, file='/proc/self/status', status='old', action='read', iostat=ios)
43 if (ios /= 0) return
44 do
45 read(u, '(A)', iostat=ios) line
46 if (ios /= 0) exit
47
48 ! PPid line format:
49 ! PPid:\t1234
50 if (line(1:5) == 'PPid:') then
51 read(line(6:), *) ppid
52 exit
53 end if
54 end do
55
56 close(u)
57 end function
58
59 !> Return executable name of a process.
60 !!
61 !! Attempts Linux `/proc/<pid>/comm` first and falls back
62 !! to invoking `ps`.
63 !!
64 !! @param[in] pid process identifier
65 !!
66 !! @return executable name or empty string
67 !!
68 !! @ingroup group_process
69 function get_process_name_posix(pid) result(name)
70 integer(int64), intent(in) :: pid
71 character(:), allocatable :: name
72
73 name = process_name_proc(pid)
74
75 if (len(name) == 0) then
76 name = process_name_ps(pid)
77 endif
78 end function
79
80 !> Linux implementation using `/proc/<pid>/comm`
81 !!
82 !! Returns an empty string if `/proc` is unavailable.
83 !!
84 !! @ingroup group_process
85 function process_name_proc(pid) result(name)
86 integer(int64), intent(in) :: pid
87 character(:), allocatable :: name
88 !private
89 character(64) :: filename
90 character(256) :: line
91 integer :: ios, unit
92
93 write(filename,'("/proc/",I0,"/comm")') pid
94
95 open(newunit = unit, file = trim(filename), status = 'old', action = 'read', iostat = ios)
96
97 if (ios /= 0) then
98 name = ''
99 return
100 endif
101
102 read(unit,'(A)',iostat=ios) line
103 close(unit)
104
105 if (ios /= 0) then
106 name = ''
107 else
108 name = trim(line)
109 endif
110 end function
111
112 !> Portable POSIX fallback using `ps`.
113 !!
114 !! Works on Linux, macOS, FreeBSD, OpenBSD and Solaris.
115 !!
116 !! @ingroup group_process
117 function process_name_ps(pid) result(name)
118 integer(int64), intent(in) :: pid
119 character(:), allocatable :: name
120 !private
121 character(64) :: command, tmpfile
122 character(256) :: line
123 integer :: ios, unit
124
125 tmpfile = '__fpx_process.tmp'
126
127 write(command,'("ps -p ",I0," -o comm= > ",A)') pid, trim(tmpfile)
128
129 call execute_command_line(command)
130
131 open(newunit = unit, file = trim(tmpfile), status = 'old', action = 'read', iostat = ios)
132 if (ios /= 0) then
133 name = ''
134 close(unit, status='delete')
135 return
136 endif
137
138 read(unit,'(A)', iostat=ios) line
139 if (ios == 0) then
140 name = trim(adjustl(line))
141 else
142 name = ''
143 endif
144
145 close(unit, status='delete')
146 end function
147
148end module
integer function, public get_parent_pid_posix()
Return parent PID (PPID) on POSIX systems.