|
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