Skip to content

Commit dc333df

Browse files
committed
Reapply "feat(fsm): add support to announce Windows processes."
This reverts commit d53fdd8. Signed-off-by: Paulo Vital <[email protected]>
1 parent 5e5bac7 commit dc333df

File tree

1 file changed

+77
-33
lines changed

1 file changed

+77
-33
lines changed

src/instana/fsm.py

Lines changed: 77 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
import subprocess
99
import sys
1010
import threading
11-
from typing import TYPE_CHECKING, Any, Callable
11+
from typing import TYPE_CHECKING, Any, Callable, List
1212

1313
from fysom import Fysom
1414

1515
from instana.log import logger
1616
from instana.util import get_default_gateway
1717
from instana.util.process_discovery import Discovery
18+
from instana.util.runtime import is_windows
1819
from instana.version import VERSION
1920

2021
if 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

Comments
 (0)