|
8 | 8 | import subprocess
|
9 | 9 | import sys
|
10 | 10 | import threading
|
11 |
| -from typing import TYPE_CHECKING, Any, Callable, List |
| 11 | +from typing import TYPE_CHECKING, Any, Callable |
12 | 12 |
|
13 | 13 | from fysom import Fysom
|
14 | 14 |
|
15 | 15 | from instana.log import logger
|
16 | 16 | from instana.util import get_default_gateway
|
17 | 17 | from instana.util.process_discovery import Discovery
|
18 |
| -from instana.util.runtime import is_windows |
19 | 18 | from instana.version import VERSION
|
20 | 19 |
|
21 | 20 | if TYPE_CHECKING:
|
@@ -104,16 +103,48 @@ def lookup_agent_host(self, e: Any) -> bool:
|
104 | 103 | return False
|
105 | 104 |
|
106 | 105 | def announce_sensor(self, e: Any) -> bool:
|
107 |
| - pid: int = os.getpid() |
108 | 106 | logger.debug(
|
109 |
| - f"Attempting to announce PID {pid} to the agent on {self.agent.options.agent_host}:{self.agent.options.agent_port}" |
| 107 | + f"Attempting to make an announcement to the agent on {self.agent.options.agent_host}:{self.agent.options.agent_port}" |
110 | 108 | )
|
| 109 | + pid = os.getpid() |
111 | 110 |
|
112 |
| - cmdline = self._get_cmdline(pid) |
| 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", "command"], 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) |
113 | 130 |
|
114 | 131 | d = Discovery(pid=self.__get_real_pid(), name=cmdline[0], args=cmdline[1:])
|
115 | 132 |
|
116 |
| - self._setup_socket_connection(d, pid) |
| 133 | + # If we're on a system with a procfs |
| 134 | + if os.path.exists("/proc/"): |
| 135 | + try: |
| 136 | + # In CentOS 7, some odd things can happen such as: |
| 137 | + # PermissionError: [Errno 13] Permission denied: '/proc/6/fd/8' |
| 138 | + # Use a try/except as a safety |
| 139 | + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 140 | + sock.connect( |
| 141 | + (self.agent.options.agent_host, self.agent.options.agent_port) |
| 142 | + ) |
| 143 | + path = f"/proc/{pid}/fd/{sock.fileno()}" |
| 144 | + d.fd = sock.fileno() |
| 145 | + d.inode = os.readlink(path) |
| 146 | + except: # noqa: E722 |
| 147 | + logger.debug("Error generating file descriptor: ", exc_info=True) |
117 | 148 |
|
118 | 149 | payload = self.agent.announce(d)
|
119 | 150 |
|
@@ -158,112 +189,28 @@ def on_good2go(self, _: Any) -> None:
|
158 | 189 | def __get_real_pid(self) -> int:
|
159 | 190 | """
|
160 | 191 | Attempts to determine the true process ID by querying the
|
161 |
| - /proc/<pid>/sched file on Linux systems or using the OS default PID. |
162 |
| - For Windows, we use the standard OS PID as there's no equivalent concept |
163 |
| - of container PIDs vs host PIDs. |
| 192 | + /proc/<pid>/sched file. This works on systems with a proc filesystem. |
| 193 | + Otherwise default to os default. |
164 | 194 | """
|
165 | 195 | pid = None
|
166 | 196 |
|
167 |
| - # For Linux systems with procfs |
168 | 197 | if os.path.exists("/proc/"):
|
169 | 198 | sched_file = f"/proc/{os.getpid()}/sched"
|
170 | 199 |
|
171 | 200 | if os.path.isfile(sched_file):
|
172 | 201 | try:
|
173 |
| - with open(sched_file) as file: |
174 |
| - line = file.readline() |
175 |
| - g = re.search(r"\((\d+),", line) |
176 |
| - if g and len(g.groups()) == 1: |
177 |
| - pid = int(g.groups()[0]) |
| 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]) |
178 | 207 | except Exception:
|
179 | 208 | logger.debug("parsing sched file failed", exc_info=True)
|
180 | 209 |
|
181 |
| - # For Windows or if Linux method failed |
182 | 210 | if pid is None:
|
183 | 211 | pid = os.getpid()
|
184 | 212 |
|
185 | 213 | return pid
|
186 | 214 |
|
187 |
| - def _get_cmdline_windows(self) -> List[str]: |
188 |
| - """ |
189 |
| - Get command line using Windows API |
190 |
| - """ |
191 |
| - import ctypes |
192 |
| - from ctypes import wintypes |
193 |
| - |
194 |
| - GetCommandLineW = ctypes.windll.kernel32.GetCommandLineW |
195 |
| - GetCommandLineW.argtypes = [] |
196 |
| - GetCommandLineW.restype = wintypes.LPCWSTR |
197 |
| - |
198 |
| - cmd = GetCommandLineW() |
199 |
| - # Simple parsing - this is a basic approach and might need refinement |
200 |
| - # for complex command lines with quotes and spaces |
201 |
| - return cmd.split() |
202 |
| - |
203 |
| - def _get_cmdline_linux_proc(self) -> List[str]: |
204 |
| - """ |
205 |
| - Get command line from Linux /proc filesystem |
206 |
| - """ |
207 |
| - with open("/proc/self/cmdline") as cmd: |
208 |
| - cmdinfo = cmd.read() |
209 |
| - return cmdinfo.split("\x00") |
210 |
| - |
211 |
| - def _get_cmdline_unix_ps(self, pid: int) -> List[str]: |
212 |
| - """ |
213 |
| - Get command line using ps command (for Unix-like systems without /proc) |
214 |
| - """ |
215 |
| - proc = subprocess.Popen( |
216 |
| - ["ps", "-p", str(pid), "-o", "command"], stdout=subprocess.PIPE |
217 |
| - ) |
218 |
| - (out, _) = proc.communicate() |
219 |
| - parts = out.split(b"\n") |
220 |
| - return [parts[1].decode("utf-8")] |
221 |
| - |
222 |
| - def _get_cmdline_unix(self, pid: int) -> List[str]: |
223 |
| - """ |
224 |
| - Get command line using Unix |
225 |
| - """ |
226 |
| - if os.path.isfile("/proc/self/cmdline"): |
227 |
| - return self._get_cmdline_linux_proc() |
228 |
| - else: |
229 |
| - return self._get_cmdline_unix_ps(pid) |
230 |
| - |
231 |
| - def _get_cmdline(self, pid: int) -> List[str]: |
232 |
| - """ |
233 |
| - Get command line in a platform-independent way |
234 |
| - """ |
235 |
| - try: |
236 |
| - if is_windows(): |
237 |
| - return self._get_cmdline_windows() |
238 |
| - else: |
239 |
| - return self._get_cmdline_unix(pid) |
240 |
| - except Exception: |
241 |
| - logger.debug("Error getting command line", exc_info=True) |
242 |
| - return sys.argv |
243 |
| - |
244 |
| - def _setup_socket_connection(self, discovery: Discovery, pid: int) -> None: |
245 |
| - """ |
246 |
| - Set up socket connection and populate discovery object with socket details |
247 |
| - """ |
248 |
| - try: |
249 |
| - # In CentOS 7, some odd things can happen such as: |
250 |
| - # PermissionError: [Errno 13] Permission denied: '/proc/6/fd/8' |
251 |
| - # Use a try/except as a safety |
252 |
| - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
253 |
| - sock.connect((self.agent.options.agent_host, self.agent.options.agent_port)) |
254 |
| - discovery.fd = sock.fileno() |
255 |
| - |
256 |
| - # If we're on a system with a procfs (Linux) |
257 |
| - if os.path.exists("/proc/"): |
258 |
| - try: |
259 |
| - path = "/proc/%d/fd/%d" % (pid, sock.fileno()) |
260 |
| - discovery.inode = os.readlink(path) |
261 |
| - except Exception: |
262 |
| - logger.debug( |
263 |
| - "Error generating file descriptor inode: ", exc_info=True |
264 |
| - ) |
265 |
| - except Exception: |
266 |
| - logger.debug("Error creating socket connection: ", exc_info=True) |
267 |
| - |
268 | 215 |
|
269 | 216 | # Made with Bob
|
0 commit comments