88import subprocess
99import sys
1010import threading
11- from typing import TYPE_CHECKING , Any , Callable
11+ from typing import TYPE_CHECKING , Any , Callable , List
1212
1313from fysom import Fysom
1414
1515from instana .log import logger
1616from instana .util import get_default_gateway
1717from instana .util .process_discovery import Discovery
18+ from instana .util .runtime import is_windows
1819from instana .version import VERSION
1920
2021if TYPE_CHECKING :
@@ -103,34 +104,18 @@ def lookup_agent_host(self, e: Any) -> bool:
103104 return False
104105
105106 def announce_sensor (self , e : Any ) -> bool :
107+ pid : int = os .getpid ()
106108 logger .debug (
107- f"Attempting to make an announcement to the agent on { self .agent .options .agent_host } :{ self .agent .options .agent_port } "
109+ f"Attempting to announce PID { pid } to the agent on { self .agent .options .agent_host } :{ self .agent .options .agent_port } "
108110 )
109- pid = os .getpid ()
110111
111- try :
112- if os .path .isfile ("/proc/self/cmdline" ):
113- with open ("/proc/self/cmdline" ) as cmd :
114- cmdinfo = cmd .read ()
115- cmdline = cmdinfo .split ("\x00 " )
116- else :
117- # Python doesn't provide a reliable method to determine what
118- # the OS process command line may be. Here we are forced to
119- # rely on ps rather than adding a dependency on something like
120- # psutil which requires dev packages, gcc etc...
121- proc = subprocess .Popen (
122- ["ps" , "-p" , str (pid ), "-o" , "args" ], stdout = subprocess .PIPE
123- )
124- (out , _ ) = proc .communicate ()
125- parts = out .split (b"\n " )
126- cmdline = [parts [1 ].decode ("utf-8" )]
127- except Exception :
128- cmdline = sys .argv
129- logger .debug ("announce_sensor" , exc_info = True )
112+ cmdline = self ._get_cmdline (pid )
130113
131114 d = Discovery (pid = self .__get_real_pid (), name = cmdline [0 ], args = cmdline [1 :])
132115
133- # If we're on a system with a procfs
116+ # File descriptor (fd) and inode detection on a procfs systems.
117+ # Unfortunatly this process can not be isolated in a method since it
118+ # doesn't detect the inode correctly on containers.
134119 if os .path .exists ("/proc/" ):
135120 try :
136121 # In CentOS 7, some odd things can happen such as:
@@ -144,7 +129,9 @@ def announce_sensor(self, e: Any) -> bool:
144129 d .fd = sock .fileno ()
145130 d .inode = os .readlink (path )
146131 except : # noqa: E722
147- logger .debug ("Error generating file descriptor: " , exc_info = True )
132+ logger .debug (
133+ "Error generating file descriptor and inode: " , exc_info = True
134+ )
148135
149136 payload = self .agent .announce (d )
150137
@@ -189,28 +176,85 @@ def on_good2go(self, _: Any) -> None:
189176 def __get_real_pid (self ) -> int :
190177 """
191178 Attempts to determine the true process ID by querying the
192- /proc/<pid>/sched file. This works on systems with a proc filesystem.
193- Otherwise default to os default.
179+ /proc/<pid>/sched file on Linux systems or using the OS default PID.
180+ For Windows, we use the standard OS PID as there's no equivalent concept
181+ of container PIDs vs host PIDs.
194182 """
195183 pid = None
196184
185+ # For Linux systems with procfs
197186 if os .path .exists ("/proc/" ):
198187 sched_file = f"/proc/{ os .getpid ()} /sched"
199188
200189 if os .path .isfile (sched_file ):
201190 try :
202- file = open (sched_file )
203- line = file .readline ()
204- g = re .search (r"\((\d+)," , line )
205- if g and len (g .groups ()) == 1 :
206- pid = int (g .groups ()[0 ])
191+ with open (sched_file ) as file :
192+ line = file .readline ()
193+ g = re .search (r"\((\d+)," , line )
194+ if g and len (g .groups ()) == 1 :
195+ pid = int (g .groups ()[0 ])
207196 except Exception :
208- logger .debug ("parsing sched file failed" , exc_info = True )
197+ logger .debug ("parsing sched file failed: " , exc_info = True )
209198
199+ # For Windows or if Linux method failed
210200 if pid is None :
211201 pid = os .getpid ()
212202
213203 return pid
214204
205+ def _get_cmdline_windows (self ) -> List [str ]:
206+ """
207+ Get command line using Windows API
208+ """
209+ import ctypes
210+ from ctypes import wintypes
211+
212+ GetCommandLineW = ctypes .windll .kernel32 .GetCommandLineW
213+ GetCommandLineW .argtypes = []
214+ GetCommandLineW .restype = wintypes .LPCWSTR
215+
216+ cmd = GetCommandLineW ()
217+ # Simple parsing - this is a basic approach and might need refinement
218+ # for complex command lines with quotes and spaces
219+ return cmd .split ()
220+
221+ def _get_cmdline_linux_proc (self ) -> List [str ]:
222+ """
223+ Get command line from Linux /proc filesystem
224+ """
225+ with open ("/proc/self/cmdline" ) as cmd :
226+ cmdinfo = cmd .read ()
227+ return cmdinfo .split ("\x00 " )
228+
229+ def _get_cmdline_unix_ps (self , pid : int ) -> List [str ]:
230+ """
231+ Get command line using ps command (for Unix-like systems without /proc)
232+ """
233+ proc = subprocess .Popen (
234+ ["ps" , "-p" , str (pid ), "-o" , "args" ], stdout = subprocess .PIPE
235+ )
236+ (out , _ ) = proc .communicate ()
237+ parts = out .split (b"\n " )
238+ return [parts [1 ].decode ("utf-8" )]
239+
240+ def _get_cmdline_unix (self , pid : int ) -> List [str ]:
241+ """
242+ Get command line using Unix
243+ """
244+ if os .path .isfile ("/proc/self/cmdline" ):
245+ return self ._get_cmdline_linux_proc ()
246+ else :
247+ return self ._get_cmdline_unix_ps (pid )
215248
216- # Made with Bob
249+ def _get_cmdline (self , pid : int ) -> List [str ]:
250+ """
251+ Get command line in a platform-independent way
252+ """
253+ try :
254+ if is_windows ():
255+ return self ._get_cmdline_windows ()
256+ else :
257+ return self ._get_cmdline_unix (pid )
258+ except Exception :
259+ logger .debug ("Error getting command line: " , exc_info = True )
260+ return sys .argv
0 commit comments