Kill any llama-server bound to the embed port. Uses /proc scan to find the exact PID holding the port, avoiding unreliable pkill -f regex matching on Termux/Android.
(self)
| 166 | # ── Health helpers ───────────────────────────────────────────────────────── |
| 167 | |
| 168 | def _kill_port_occupant(self): |
| 169 | """Kill any llama-server bound to the embed port. |
| 170 | |
| 171 | Uses /proc scan to find the exact PID holding the port, avoiding |
| 172 | unreliable pkill -f regex matching on Termux/Android. |
| 173 | """ |
| 174 | import subprocess as _sp |
| 175 | |
| 176 | # Method 1: parse /proc/net/tcp to find PID on our port |
| 177 | try: |
| 178 | port_hex = f"{self.port:04X}" |
| 179 | with open("/proc/net/tcp") as f: |
| 180 | for line in f: |
| 181 | parts = line.split() |
| 182 | if len(parts) < 10: |
| 183 | continue |
| 184 | local_addr = parts[1] |
| 185 | if local_addr.endswith(f":{port_hex}"): |
| 186 | inode = parts[9] |
| 187 | # Find PID owning this inode |
| 188 | for pid_dir in Path("/proc").iterdir(): |
| 189 | if not pid_dir.name.isdigit(): |
| 190 | continue |
| 191 | try: |
| 192 | for fd in (pid_dir / "fd").iterdir(): |
| 193 | link = os.readlink(str(fd)) |
| 194 | if f"socket:[{inode}]" in link: |
| 195 | pid = int(pid_dir.name) |
| 196 | info(f"Killing stale embed server PID {pid}") |
| 197 | os.kill(pid, 9) |
| 198 | raise StopIteration |
| 199 | except (PermissionError, StopIteration, OSError): |
| 200 | pass |
| 201 | break |
| 202 | except StopIteration: |
| 203 | pass |
| 204 | except Exception: |
| 205 | pass |
| 206 | |
| 207 | # Method 2: fallback — kill all llama-server processes |
| 208 | # This is aggressive but reliable on Termux where fuser/lsof may not exist |
| 209 | try: |
| 210 | _sp.run(["pkill", "-9", "llama-server"], capture_output=True) |
| 211 | except Exception: |
| 212 | pass |
| 213 | |
| 214 | import time as _time |
| 215 | _time.sleep(2) # give kernel time to release the port |
| 216 | |
| 217 | def _check_health(self) -> bool: |
| 218 | try: |