Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package killport for openSUSE:Factory 
checked in at 2026-08-09 21:45:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/killport (Old)
 and      /work/SRC/openSUSE:Factory/.killport.new.16738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "killport"

Sun Aug  9 21:45:02 2026 rev:5 rq:1370403 version:2.0.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/killport/killport.changes        2026-07-12 
18:36:27.445417106 +0200
+++ /work/SRC/openSUSE:Factory/.killport.new.16738/killport.changes     
2026-08-09 21:47:02.998234118 +0200
@@ -1,0 +2,16 @@
+Sun Aug  9 14:47:49 UTC 2026 - Andrea Manzini <[email protected]>
+
+- Update to 2.0.2:
+  * bump deps
+  * Minimum supported Rust version corrected from 1.74 to 1.86
+  * Killing multiple ports no longer stops at the first port that errors
+  * port lookup scans /proc once for all matching socket inodes instead of once
+    per inode, and no longer returns duplicate entries when several fds match 
the port
+  * processes with unreadable command lines fall back to the short process name
+    instead of being skipped
+  * Container runtime detection and kills honor DOCKER_HOST (colima, podman, 
+    remote docker)
+  * Container kills reuse the shared tokio runtime instead of building a new 
+    one per container
+
+-------------------------------------------------------------------

Old:
----
  killport-2.0.1.tar.gz

New:
----
  killport-2.0.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ killport.spec ++++++
--- /var/tmp/diff_new_pack.V64jDM/_old  2026-08-09 21:47:04.134272857 +0200
+++ /var/tmp/diff_new_pack.V64jDM/_new  2026-08-09 21:47:04.138272993 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           killport
-Version:        2.0.1
+Version:        2.0.2
 Release:        0
 Summary:        A tool to easily kill processes running on a specified port
 License:        MIT

++++++ _service ++++++
--- /var/tmp/diff_new_pack.V64jDM/_old  2026-08-09 21:47:04.174274221 +0200
+++ /var/tmp/diff_new_pack.V64jDM/_new  2026-08-09 21:47:04.178274357 +0200
@@ -3,7 +3,7 @@
         <param name="url">https://github.com/jkfran/killport</param>
         <param name="scm">git</param>
         <param name="exclude">.git</param>
-        <param name="revision">v2.0.1</param>
+        <param name="revision">v2.0.2</param>
         <param name="versionformat">@PARENT_TAG@</param>
         <param name="versionrewrite-pattern">v(.*)</param>
     </service>

++++++ killport-2.0.1.tar.gz -> killport-2.0.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/CHANGELOG.md 
new/killport-2.0.2/CHANGELOG.md
--- old/killport-2.0.1/CHANGELOG.md     2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/CHANGELOG.md     2026-08-01 10:04:31.000000000 +0200
@@ -7,6 +7,68 @@
 
 ## [Unreleased]
 
+## [2.0.2] - 2026-08-01
+
+### Dependencies
+
+- build(deps): bump the cargo-minor-patch group with 3 updates (#52)
+- build(deps): bump the cargo-minor-patch group with 3 updates (#51)
+- build(deps): bump regex in the cargo-minor-patch group (#50)
+- Updating bitflags v2.13.0 -> v2.13.1
+- Updating bstr v1.12.3 -> v1.13.0
+- Updating bytes v1.12.0 -> v1.12.1
+- Updating cc v1.2.66 -> v1.4.0
+- Updating cfg_aliases v0.2.1 -> v0.2.2
+- Updating clang-sys v1.8.1 -> v1.9.1
+- Updating clap v4.6.4 -> v4.6.5
+- Updating clap_builder v4.6.2 -> v4.6.5
+- Updating clap_complete v4.6.7 -> v4.6.8
+- Updating displaydoc v0.2.6 -> v0.2.7
+- Updating either v1.16.0 -> v1.17.0
+- Updating fastrand v2.4.1 -> v2.5.0
+- Updating futures-channel v0.3.32 -> v0.3.33
+- Updating futures-core v0.3.32 -> v0.3.33
+- Updating futures-macro v0.3.32 -> v0.3.33
+- Updating futures-sink v0.3.32 -> v0.3.33
+- Updating futures-task v0.3.32 -> v0.3.33
+- Updating futures-util v0.3.32 -> v0.3.33
+- Updating glob v0.3.3 -> v0.3.4
+- Updating http v1.4.2 -> v1.5.0
+- Updating http-body v1.0.1 -> v1.1.0
+- Updating http-body-util v0.1.3 -> v0.1.4
+- Updating hyper v1.10.1 -> v1.11.0
+- Updating hyper-named-pipe v0.1.0 -> v0.1.1
+- Updating jiff v0.2.31 -> v0.2.35
+- Adding jiff-core v0.1.0
+- Updating jiff-static v0.2.31 -> v0.2.35
+- Updating memchr v2.8.2 -> v2.8.3
+- Updating mio v1.2.1 -> v1.2.2
+- Updating portable-atomic v1.13.1 -> v1.14.0
+- Updating proc-macro2 v1.0.106 -> v1.0.107
+- Updating quote v1.0.46 -> v1.0.47
+- Updating rustversion v1.0.22 -> v1.0.23
+- Updating serde v1.0.228 -> v1.0.229
+- Updating serde_core v1.0.228 -> v1.0.229
+- Updating serde_derive v1.0.228 -> v1.0.229
+- Updating serde_json v1.0.150 -> v1.0.151
+- Updating serde_repr v0.1.20 -> v0.1.21
+- Updating simd-adler32 v0.3.9 -> v0.3.10
+- Updating socket2 v0.6.4 -> v0.6.5
+- ...and 5 more transitive updates
+
+### Changed
+- Minimum supported Rust version corrected from 1.74 to 1.86 — the actual 
floor of the current dependency tree — and now enforced by a dedicated CI job
+
+### Fixed
+- Killing multiple ports no longer stops at the first port that errors; all 
ports are processed and the exit code reflects the worst outcome (error > not 
found > success)
+- Windows: no longer attempts to kill PID 0 (`[System Process]`, owner of 
TIME_WAIT entries) or PID 4 (System), which failed with error 0x57
+- macOS: processes holding more than 1024 file descriptors could have their 
listening sockets missed; the fd list is now sized to the process's actual fd 
count
+- macOS: a process exiting mid-scan no longer aborts the whole port lookup
+- Linux: port lookup scans `/proc` once for all matching socket inodes instead 
of once per inode, and no longer returns duplicate entries when several fds 
match the port
+- Linux: processes with unreadable command lines fall back to the short 
process name instead of being skipped
+- Container runtime detection and kills honor `DOCKER_HOST` (colima, podman, 
remote docker)
+- Container kills reuse the shared tokio runtime instead of building a new one 
per container
+
 ## [2.0.1] - 2026-07-06
 
 ### Dependencies
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/Cargo.lock 
new/killport-2.0.2/Cargo.lock
--- old/killport-2.0.1/Cargo.lock       2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/Cargo.lock       2026-08-01 10:04:31.000000000 +0200
@@ -115,7 +115,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
 dependencies = [
- "bitflags 2.13.0",
+ "bitflags 2.13.1",
  "cexpr",
  "clang-sys",
  "itertools",
@@ -124,7 +124,7 @@
  "regex",
  "rustc-hash",
  "shlex 1.3.0",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -135,9 +135,9 @@
 
 [[package]]
 name = "bitflags"
-version = "2.13.0"
+version = "2.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
 
 [[package]]
 name = "bollard"
@@ -184,9 +184,9 @@
 
 [[package]]
 name = "bstr"
-version = "1.12.3"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79"
+checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
 dependencies = [
  "memchr",
  "regex-automata",
@@ -201,15 +201,15 @@
 
 [[package]]
 name = "bytes"
-version = "1.12.0"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
 
 [[package]]
 name = "cc"
-version = "1.2.66"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
+checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
 dependencies = [
  "find-msvc-tools",
  "shlex 2.0.1",
@@ -232,9 +232,9 @@
 
 [[package]]
 name = "cfg_aliases"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
 
 [[package]]
 name = "chrono"
@@ -249,9 +249,9 @@
 
 [[package]]
 name = "clang-sys"
-version = "1.8.1"
+version = "1.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
 dependencies = [
  "glob",
  "libc",
@@ -260,9 +260,9 @@
 
 [[package]]
 name = "clap"
-version = "4.6.1"
+version = "4.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
+checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -280,9 +280,9 @@
 
 [[package]]
 name = "clap_builder"
-version = "4.6.0"
+version = "4.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
+checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078"
 dependencies = [
  "anstream",
  "anstyle",
@@ -292,23 +292,23 @@
 
 [[package]]
 name = "clap_complete"
-version = "4.6.7"
+version = "4.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "db8b397918185f0161ff3d6fcaa9e4bfc09b8367caf6e1d4a2848e5477ed027b"
+checksum = "b1f84a88507dbd05c695f2cb5e8558e747179134005e9893882dec964190ed89"
 dependencies = [
  "clap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.6.1"
+version = "4.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
+checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
 dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.3",
 ]
 
 [[package]]
@@ -367,7 +367,7 @@
  "defmt-parser",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -387,20 +387,20 @@
 
 [[package]]
 name = "displaydoc"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
+checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.3",
 ]
 
 [[package]]
 name = "either"
-version = "1.16.0"
+version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
+checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
 
 [[package]]
 name = "env_filter"
@@ -437,9 +437,9 @@
 
 [[package]]
 name = "fastrand"
-version = "2.4.1"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
 
 [[package]]
 name = "find-msvc-tools"
@@ -468,47 +468,47 @@
 
 [[package]]
 name = "futures-channel"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
+checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
 dependencies = [
  "futures-core",
 ]
 
 [[package]]
 name = "futures-core"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
+checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
+checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
 
 [[package]]
 name = "futures-task"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
 
 [[package]]
 name = "futures-util"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
 dependencies = [
  "futures-core",
  "futures-macro",
@@ -530,9 +530,9 @@
 
 [[package]]
 name = "glob"
-version = "0.3.3"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
+checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
 
 [[package]]
 name = "heck"
@@ -548,9 +548,9 @@
 
 [[package]]
 name = "http"
-version = "1.4.2"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
+checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
 dependencies = [
  "bytes",
  "itoa",
@@ -558,9 +558,9 @@
 
 [[package]]
 name = "http-body"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
 dependencies = [
  "bytes",
  "http",
@@ -568,9 +568,9 @@
 
 [[package]]
 name = "http-body-util"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
 dependencies = [
  "bytes",
  "futures-core",
@@ -593,9 +593,9 @@
 
 [[package]]
 name = "hyper"
-version = "1.10.1"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
+checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72"
 dependencies = [
  "atomic-waker",
  "bytes",
@@ -614,9 +614,9 @@
 
 [[package]]
 name = "hyper-named-pipe"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278"
+checksum = "fab3637d6b04a8037af8a266fdf6cf92ea957e8c53981a2bf6136572531025bf"
 dependencies = [
  "hex",
  "hyper",
@@ -624,7 +624,6 @@
  "pin-project-lite",
  "tokio",
  "tower-service",
- "winapi",
 ]
 
 [[package]]
@@ -812,11 +811,12 @@
 
 [[package]]
 name = "jiff"
-version = "0.2.31"
+version = "0.2.35"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ccfe6121cbe750cf81efa362d85c0bde7ea298ec43092d3a193baca59cdbd634"
+checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc"
 dependencies = [
  "defmt",
+ "jiff-core",
  "jiff-static",
  "log",
  "portable-atomic",
@@ -825,14 +825,24 @@
 ]
 
 [[package]]
+name = "jiff-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09"
+dependencies = [
+ "defmt",
+]
+
+[[package]]
 name = "jiff-static"
-version = "0.2.31"
+version = "0.2.35"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2"
+checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204"
 dependencies = [
+ "jiff-core",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -848,7 +858,7 @@
 
 [[package]]
 name = "killport"
-version = "2.0.1"
+version = "2.0.2"
 dependencies = [
  "assert_cmd",
  "bollard",
@@ -870,9 +880,9 @@
 
 [[package]]
 name = "libc"
-version = "0.2.186"
+version = "0.2.189"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
+checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
 
 [[package]]
 name = "libloading"
@@ -915,9 +925,9 @@
 
 [[package]]
 name = "memchr"
-version = "2.8.2"
+version = "2.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
 
 [[package]]
 name = "minimal-lexical"
@@ -937,9 +947,9 @@
 
 [[package]]
 name = "mio"
-version = "1.2.1"
+version = "1.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
+checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
 dependencies = [
  "libc",
  "wasi",
@@ -952,7 +962,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
 dependencies = [
- "bitflags 2.13.0",
+ "bitflags 2.13.1",
  "cfg-if",
  "cfg_aliases",
  "libc",
@@ -1003,9 +1013,9 @@
 
 [[package]]
 name = "portable-atomic"
-version = "1.13.1"
+version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
+checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
 
 [[package]]
 name = "portable-atomic-util"
@@ -1054,9 +1064,9 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.106"
+version = "1.0.107"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
 dependencies = [
  "unicode-ident",
 ]
@@ -1067,7 +1077,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7"
 dependencies = [
- "bitflags 2.13.0",
+ "bitflags 2.13.1",
  "chrono",
  "flate2",
  "procfs-core",
@@ -1080,16 +1090,16 @@
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405"
 dependencies = [
- "bitflags 2.13.0",
+ "bitflags 2.13.1",
  "chrono",
  "hex",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.46"
+version = "1.0.47"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
 dependencies = [
  "proc-macro2",
 ]
@@ -1102,9 +1112,9 @@
 
 [[package]]
 name = "regex"
-version = "1.12.4"
+version = "1.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
+checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1114,9 +1124,9 @@
 
 [[package]]
 name = "regex-automata"
-version = "0.4.14"
+version = "0.4.16"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1147,7 +1157,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
 dependencies = [
- "bitflags 2.13.0",
+ "bitflags 2.13.1",
  "errno",
  "libc",
  "linux-raw-sys",
@@ -1156,9 +1166,9 @@
 
 [[package]]
 name = "rustversion"
-version = "1.0.22"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
 
 [[package]]
 name = "ryu"
@@ -1168,9 +1178,9 @@
 
 [[package]]
 name = "serde"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
 dependencies = [
  "serde_core",
  "serde_derive",
@@ -1178,29 +1188,29 @@
 
 [[package]]
 name = "serde_core"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.3",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.150"
+version = "1.0.151"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
+checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
 dependencies = [
  "itoa",
  "memchr",
@@ -1211,13 +1221,13 @@
 
 [[package]]
 name = "serde_repr"
-version = "0.1.20"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
+checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.3",
 ]
 
 [[package]]
@@ -1246,9 +1256,9 @@
 
 [[package]]
 name = "simd-adler32"
-version = "0.3.9"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
 
 [[package]]
 name = "slab"
@@ -1264,9 +1274,9 @@
 
 [[package]]
 name = "socket2"
-version = "0.6.4"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
+checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
 dependencies = [
  "libc",
  "windows-sys",
@@ -1286,9 +1296,20 @@
 
 [[package]]
 name = "syn"
-version = "2.0.118"
+version = "2.0.119"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1303,7 +1324,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -1327,22 +1348,22 @@
 
 [[package]]
 name = "thiserror"
-version = "2.0.18"
+version = "2.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.18"
+version = "2.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.3",
 ]
 
 [[package]]
@@ -1357,9 +1378,9 @@
 
 [[package]]
 name = "tokio"
-version = "1.52.3"
+version = "1.53.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
+checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
 dependencies = [
  "bytes",
  "libc",
@@ -1371,13 +1392,14 @@
 
 [[package]]
 name = "tokio-util"
-version = "0.7.18"
+version = "0.7.19"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
+checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
 dependencies = [
  "bytes",
  "futures-core",
  "futures-sink",
+ "libc",
  "pin-project-lite",
  "tokio",
 ]
@@ -1499,7 +1521,7 @@
  "bumpalo",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
  "wasm-bindgen-shared",
 ]
 
@@ -1555,7 +1577,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -1566,7 +1588,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -1627,7 +1649,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
  "synstructure",
 ]
 
@@ -1648,7 +1670,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
  "synstructure",
 ]
 
@@ -1682,11 +1704,11 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
 name = "zmij"
-version = "1.0.21"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/Cargo.toml 
new/killport-2.0.2/Cargo.toml
--- old/killport-2.0.1/Cargo.toml       2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/Cargo.toml       2026-08-01 10:04:31.000000000 +0200
@@ -1,9 +1,10 @@
 [package]
 name = "killport"
-version = "2.0.1"
+version = "2.0.2"
 authors = ["Francisco Jimenez Cabrera <[email protected]>"]
 edition = "2021"
-rust-version = "1.74"
+# Actual floor of the dependency tree (icu crates); enforced by the MSRV CI 
job.
+rust-version = "1.86"
 license = "MIT"
 description = "A command-line tool to easily kill processes and containers 
running on a specified port."
 readme = "README.md"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/src/container.rs 
new/killport-2.0.2/src/container.rs
--- old/killport-2.0.1/src/container.rs 2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/src/container.rs 2026-08-01 10:04:31.000000000 +0200
@@ -20,8 +20,10 @@
     /// * `signal` - A enum value representing the signal type.
     pub fn kill(rt: &Runtime, name: &str, signal: KillportSignal) -> 
Result<(), Error> {
         rt.block_on(async {
+            // connect_with_local_defaults honors DOCKER_HOST, so custom
+            // sockets (colima, podman, rancher desktop) are found too.
             let docker =
-                Docker::connect_with_socket_defaults().map_err(|e| 
Error::other(e.to_string()))?;
+                Docker::connect_with_local_defaults().map_err(|e| 
Error::other(e.to_string()))?;
 
             let options = KillContainerOptions {
                 signal: signal.to_string(),
@@ -37,7 +39,7 @@
     /// Finds the containers associated with the specified `port`.
     pub fn find_target_containers(rt: &Runtime, port: u16) -> 
Result<Vec<Self>, Error> {
         rt.block_on(async {
-            let docker = match Docker::connect_with_socket_defaults() {
+            let docker = match Docker::connect_with_local_defaults() {
                 Ok(d) => d,
                 Err(e) => {
                     debug!("Container runtime socket not available: {}", e);
@@ -75,7 +77,7 @@
 
     pub fn is_available(rt: &Runtime) -> Result<bool, Error> {
         rt.block_on(async {
-            let docker = match Docker::connect_with_socket_defaults() {
+            let docker = match Docker::connect_with_local_defaults() {
                 Ok(d) => d,
                 Err(e) => {
                     debug!("Container runtime socket not available: {}", e);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/src/killable.rs 
new/killport-2.0.2/src/killable.rs
--- old/killport-2.0.1/src/killable.rs  2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/src/killable.rs  2026-08-01 10:04:31.000000000 +0200
@@ -29,6 +29,10 @@
 }
 
 impl Killable for Container {
+    /// Kills the container on a freshly-built runtime. The orchestrator
+    /// (`Killport::kill_service_by_port`) routes container kills through
+    /// `ContainerOps` instead, reusing the shared runtime; this impl remains
+    /// for standalone `Killable` use.
     fn kill(&self, signal: KillportSignal) -> Result<bool, Error> {
         let rt = Builder::new_current_thread().enable_all().build()?;
         Self::kill(&rt, &self.name, signal)?;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/src/killport.rs 
new/killport-2.0.2/src/killport.rs
--- old/killport-2.0.1/src/killport.rs  2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/src/killport.rs  2026-08-01 10:04:31.000000000 +0200
@@ -19,6 +19,7 @@
 pub trait ContainerOps {
     fn is_available(&self) -> Result<bool, Error>;
     fn find_target_containers(&self, port: u16) -> Result<Vec<Container>, 
Error>;
+    fn kill_container(&self, name: &str, signal: KillportSignal) -> 
Result<bool, Error>;
 }
 
 /// Real implementation of ProcessFinder that calls the platform-specific 
functions.
@@ -55,6 +56,11 @@
     fn find_target_containers(&self, port: u16) -> Result<Vec<Container>, 
Error> {
         Container::find_target_containers(&self.rt, port)
     }
+
+    fn kill_container(&self, name: &str, signal: KillportSignal) -> 
Result<bool, Error> {
+        Container::kill(&self.rt, name, signal)?;
+        Ok(true)
+    }
 }
 
 /// Killport implementation with injectable dependencies for testability.
@@ -124,7 +130,16 @@
         let target_killables = self.find_target_killables(port, mode)?;
 
         for killable in target_killables {
-            let killed = dry_run || killable.kill(signal.clone())?;
+            let killed = if dry_run {
+                true
+            } else if killable.get_type() == KillableType::Container {
+                // Containers are killed through ContainerOps so the shared
+                // tokio runtime is reused instead of building one per kill.
+                self.container_ops
+                    .kill_container(&killable.get_name(), signal.clone())?
+            } else {
+                killable.kill(signal.clone())?
+            };
             if killed {
                 results.push((killable.get_type(), killable.get_name()));
             }
@@ -197,13 +212,18 @@
     struct FnContainerOps<
         P: Fn() -> Result<bool, Error>,
         C: Fn(u16) -> Result<Vec<Container>, Error>,
+        K: Fn(&str, KillportSignal) -> Result<bool, Error>,
     > {
         is_present: P,
         find_containers: C,
+        kill_container: K,
     }
 
-    impl<P: Fn() -> Result<bool, Error>, C: Fn(u16) -> Result<Vec<Container>, 
Error>> ContainerOps
-        for FnContainerOps<P, C>
+    impl<
+            P: Fn() -> Result<bool, Error>,
+            C: Fn(u16) -> Result<Vec<Container>, Error>,
+            K: Fn(&str, KillportSignal) -> Result<bool, Error>,
+        > ContainerOps for FnContainerOps<P, C, K>
     {
         fn is_available(&self) -> Result<bool, Error> {
             (self.is_present)()
@@ -212,16 +232,22 @@
         fn find_target_containers(&self, port: u16) -> Result<Vec<Container>, 
Error> {
             (self.find_containers)(port)
         }
+
+        fn kill_container(&self, name: &str, signal: KillportSignal) -> 
Result<bool, Error> {
+            (self.kill_container)(name, signal)
+        }
     }
 
     #[allow(clippy::type_complexity)]
     fn no_containers() -> FnContainerOps<
         impl Fn() -> Result<bool, Error>,
         impl Fn(u16) -> Result<Vec<Container>, Error>,
+        impl Fn(&str, KillportSignal) -> Result<bool, Error>,
     > {
         FnContainerOps {
             is_present: || Ok(false),
             find_containers: |_| Ok(vec![]),
+            kill_container: |_, _| Ok(true),
         }
     }
 
@@ -231,6 +257,7 @@
     ) -> FnContainerOps<
         impl Fn() -> Result<bool, Error>,
         impl Fn(u16) -> Result<Vec<Container>, Error>,
+        impl Fn(&str, KillportSignal) -> Result<bool, Error>,
     > {
         FnContainerOps {
             is_present: || Ok(true),
@@ -240,6 +267,7 @@
                     .map(|n| Container { name: n.clone() })
                     .collect())
             },
+            kill_container: |_, _| Ok(true),
         }
     }
 
@@ -340,6 +368,9 @@
         let ct = FnContainerOps {
             is_present: || panic!("Container runtime should not be checked in 
Process mode"),
             find_containers: |_| panic!("Container runtime should not be 
checked in Process mode"),
+            kill_container: |_, _| {
+                panic!("Container runtime should not be checked in Process 
mode")
+            },
         };
         let kp = Killport::new(finder, ct);
         let results = kp.find_target_killables(8080, Mode::Process).unwrap();
@@ -378,6 +409,9 @@
         let ct = FnContainerOps {
             is_present: || panic!("Container runtime should not be checked in 
Process mode"),
             find_containers: |_| panic!("Container runtime should not be 
checked in Process mode"),
+            kill_container: |_, _| {
+                panic!("Container runtime should not be checked in Process 
mode")
+            },
         };
         let kp = Killport::new(finder, ct);
         let results = kp.find_target_killables(8080, Mode::Process).unwrap();
@@ -433,6 +467,7 @@
         let ct = FnContainerOps {
             is_present: || Err(Error::other("container runtime error")),
             find_containers: |_| Ok(vec![]),
+            kill_container: |_, _| Ok(true),
         };
         let kp = Killport::new(finder, ct);
         let result = kp.find_target_killables(8080, Mode::Auto);
@@ -447,6 +482,7 @@
         let ct = FnContainerOps {
             is_present: || Ok(true),
             find_containers: |_| Err(Error::other("container error")),
+            kill_container: |_, _| Ok(true),
         };
         let kp = Killport::new(finder, ct);
         let result = kp.find_target_killables(8080, Mode::Auto);
@@ -576,4 +612,51 @@
         assert_eq!(results[0].0, KillableType::Container);
         assert_eq!(results[0].1, "nginx");
     }
+
+    #[test]
+    fn test_kill_service_container_killed_via_ops() {
+        // Container kills must route through ContainerOps (shared runtime),
+        // not through the standalone Killable impl.
+        let finder = FnProcessFinder {
+            finder: |_| Ok(vec![]),
+        };
+        let ct = FnContainerOps {
+            is_present: || Ok(true),
+            find_containers: |_| {
+                Ok(vec![Container {
+                    name: "nginx".to_string(),
+                }])
+            },
+            kill_container: |name, _| {
+                assert_eq!(name, "nginx");
+                Ok(true)
+            },
+        };
+        let kp = Killport::new(finder, ct);
+        let results = kp
+            .kill_service_by_port(8080, signal(), Mode::Container, false)
+            .unwrap();
+        assert_eq!(results.len(), 1);
+        assert_eq!(results[0].0, KillableType::Container);
+        assert_eq!(results[0].1, "nginx");
+    }
+
+    #[test]
+    fn test_kill_service_container_kill_error_propagates() {
+        let finder = FnProcessFinder {
+            finder: |_| Ok(vec![]),
+        };
+        let ct = FnContainerOps {
+            is_present: || Ok(true),
+            find_containers: |_| {
+                Ok(vec![Container {
+                    name: "nginx".to_string(),
+                }])
+            },
+            kill_container: |_, _| Err(Error::other("docker kill failed")),
+        };
+        let kp = Killport::new(finder, ct);
+        let result = kp.kill_service_by_port(8080, signal(), Mode::Container, 
false);
+        assert!(result.is_err());
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/src/linux.rs 
new/killport-2.0.2/src/linux.rs
--- old/killport-2.0.1/src/linux.rs     2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/src/linux.rs     2026-08-01 10:04:31.000000000 +0200
@@ -8,17 +8,17 @@
 
 /// Finds the inodes associated with the specified `port`.
 ///
-/// Returns a `Vec` of inodes for both IPv4 and IPv6 connections.
+/// Returns the set of socket inodes for both IPv4 and IPv6, TCP and UDP.
 ///
 /// # Arguments
 ///
 /// * `port` - A u16 value representing the port number.
-fn find_target_inodes(port: u16) -> Vec<u64> {
+fn find_target_inodes(port: u16) -> HashSet<u64> {
     let tcp = procfs::net::tcp();
     let tcp6 = procfs::net::tcp6();
     let udp = procfs::net::udp();
     let udp6 = procfs::net::udp6();
-    let mut target_inodes = Vec::new();
+    let mut target_inodes = HashSet::new();
 
     trait NetEntry {
         fn local_address(&self) -> std::net::SocketAddr;
@@ -47,7 +47,7 @@
     }
 
     fn add_matching_inodes<T: NetEntry>(
-        target_inodes: &mut Vec<u64>,
+        target_inodes: &mut HashSet<u64>,
         net_entries: procfs::ProcResult<Vec<T>>,
         port: u16,
     ) {
@@ -69,49 +69,62 @@
     target_inodes
 }
 
+/// Returns a display name for the process: the full command line, falling
+/// back to the short `comm` name when the command line is empty or
+/// unreadable (kernel threads, exec races). `None` means the process is
+/// effectively gone and should be skipped.
+fn process_name(process: &procfs::process::Process) -> Option<String> {
+    match process.cmdline() {
+        Ok(parts) if !parts.is_empty() => Some(parts.join(" ")),
+        _ => process.stat().ok().map(|stat| stat.comm),
+    }
+}
+
 /// Finds the processes associated with the specified `port`.
 ///
 /// Returns a `Vec` of native processes.
 ///
 /// # Arguments
 ///
-/// * `inodes` - Target inodes
+/// * `port` - Target port number
 pub fn find_target_processes(port: u16) -> Result<Vec<UnixProcess>, Error> {
     let mut target_pids: Vec<UnixProcess> = vec![];
-    let mut seen_pids: HashSet<i32> = HashSet::new();
-    let inodes = find_target_inodes(port);
+    let target_inodes = find_target_inodes(port);
 
-    for inode in inodes {
-        let processes = 
procfs::process::all_processes().map_err(std::io::Error::other)?;
-        for p in processes {
-            // Processes can vanish between enumeration and inspection (race 
condition).
-            // Skip any process that disappears mid-scan.
-            let process = match p {
-                Ok(p) => p,
-                Err(_) => continue,
-            };
-
-            if seen_pids.contains(&process.pid) {
-                continue;
-            }
+    if target_inodes.is_empty() {
+        return Ok(target_pids);
+    }
 
-            if let Ok(fds) = process.fd() {
-                for fd in fds {
-                    let fd = match fd {
-                        Ok(fd) => fd,
-                        Err(_) => continue,
-                    };
-
-                    if let FDTarget::Socket(sock_inode) = fd.target {
-                        if inode == sock_inode {
-                            let name = match process.cmdline() {
-                                Ok(parts) => parts.join(" "),
-                                Err(_) => continue,
-                            };
-                            debug!("Found process '{}' with PID {}", name, 
process.pid());
-                            seen_pids.insert(process.pid);
-                            
target_pids.push(UnixProcess::new(Pid::from_raw(process.pid), name));
-                        }
+    // Single pass over /proc: match each process's socket fds against the
+    // full inode set at once (a port bound on tcp/tcp6/udp/udp6 yields up
+    // to four inodes; scanning /proc once per inode is wasteful).
+    let processes = procfs::process::all_processes().map_err(Error::other)?;
+    'next_process: for p in processes {
+        // Processes can vanish between enumeration and inspection (race 
condition).
+        // Skip any process that disappears mid-scan.
+        let process = match p {
+            Ok(p) => p,
+            Err(_) => continue,
+        };
+
+        if let Ok(fds) = process.fd() {
+            for fd in fds {
+                let fd = match fd {
+                    Ok(fd) => fd,
+                    Err(_) => continue,
+                };
+
+                if let FDTarget::Socket(sock_inode) = fd.target {
+                    if target_inodes.contains(&sock_inode) {
+                        let name = match process_name(&process) {
+                            Some(name) => name,
+                            None => continue 'next_process,
+                        };
+                        debug!("Found process '{}' with PID {}", name, 
process.pid());
+                        
target_pids.push(UnixProcess::new(Pid::from_raw(process.pid), name));
+                        // One entry per process, even when several fds match
+                        // (dup'd sockets, multiple protocols on one port).
+                        continue 'next_process;
                     }
                 }
             }
@@ -120,3 +133,71 @@
 
     Ok(target_pids)
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::killable::Killable;
+    use std::net::{TcpListener, UdpSocket};
+
+    #[test]
+    fn test_find_target_processes_no_listeners() {
+        // Use a port that is very unlikely to be in use
+        let result = find_target_processes(19876);
+        assert!(result.is_ok());
+        assert!(result.unwrap().is_empty());
+    }
+
+    #[test]
+    fn test_find_target_processes_tcp_listener() {
+        let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+        let port = listener.local_addr().unwrap().port();
+
+        let processes = find_target_processes(port).unwrap();
+        assert!(
+            !processes.is_empty(),
+            "Expected to find at least one process on port {}",
+            port
+        );
+        for process in &processes {
+            assert!(!process.get_name().is_empty());
+        }
+
+        drop(listener);
+    }
+
+    #[test]
+    fn test_find_target_processes_udp_listener() {
+        let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
+        let port = socket.local_addr().unwrap().port();
+
+        let processes = find_target_processes(port).unwrap();
+        assert!(
+            !processes.is_empty(),
+            "Expected to find process with UDP socket on port {}",
+            port
+        );
+
+        drop(socket);
+    }
+
+    #[test]
+    fn test_find_target_processes_dedup() {
+        // Bind IPv4 and IPv6 on the same port — two distinct socket inodes,
+        // one owning process. The single-pass scan must return one entry.
+        let listener4 = TcpListener::bind("127.0.0.1:0").unwrap();
+        let port = listener4.local_addr().unwrap().port();
+        let listener6 = TcpListener::bind(format!("[::1]:{}", port)).unwrap();
+
+        let processes = find_target_processes(port).unwrap();
+        assert_eq!(
+            processes.len(),
+            1,
+            "Expected exactly 1 deduplicated process entry, got {}",
+            processes.len()
+        );
+
+        drop(listener4);
+        drop(listener6);
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/src/macos.rs 
new/killport-2.0.2/src/macos.rs
--- old/killport-2.0.1/src/macos.rs     2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/src/macos.rs     2026-08-01 10:04:31.000000000 +0200
@@ -1,9 +1,10 @@
 use crate::unix::UnixProcess;
 
+use libproc::libproc::bsd_info::BSDInfo;
 use libproc::libproc::file_info::pidfdinfo;
 use libproc::libproc::file_info::{ListFDs, ProcFDType};
 use libproc::libproc::net_info::{SocketFDInfo, SocketInfoKind};
-use libproc::libproc::proc_pid::{listpidinfo, name};
+use libproc::libproc::proc_pid::{listpidinfo, name, pidinfo};
 use libproc::processes::{pids_by_type, ProcFilter};
 use log::debug;
 use nix::unistd::Pid;
@@ -27,7 +28,14 @@
             if seen_pids.contains(&pid) {
                 continue;
             }
-            let fds = listpidinfo::<ListFDs>(pid, 1024);
+            // Size the fd list to the process's actual fd count — a fixed cap
+            // silently misses sockets in fd-heavy processes. Small headroom
+            // covers fds opened between the two calls; fall back to a generous
+            // default when the count is unavailable.
+            let max_fds = pidinfo::<BSDInfo>(pid, 0)
+                .map(|info| info.pbi_nfiles as usize + 32)
+                .unwrap_or(4096);
+            let fds = listpidinfo::<ListFDs>(pid, max_fds);
             if let Ok(fds) = fds {
                 for fd in fds {
                     if let ProcFDType::Socket = fd.proc_fdtype.into() {
@@ -48,7 +56,19 @@
                                         }
                                     };
                                     if u16::from_be(local_port) == port {
-                                        let process_name = 
name(pid).map_err(io::Error::other)?;
+                                        // The process can exit between socket
+                                        // enumeration and the name lookup; a
+                                        // vanished process must not fail the 
scan.
+                                        let process_name = match name(pid) {
+                                            Ok(process_name) => process_name,
+                                            Err(e) => {
+                                                debug!(
+                                                    "Skipping PID {}: name 
lookup failed ({})",
+                                                    pid, e
+                                                );
+                                                continue 'next_process;
+                                            }
+                                        };
                                         debug!(
                                             "Found process '{}' with PID {} 
listening on port {}",
                                             process_name, pid, port
@@ -234,4 +254,61 @@
 
         drop(socket);
     }
+
+    #[test]
+    fn test_find_target_processes_beyond_1024_fds() {
+        // Regression test: the fd list used to be capped at 1024 entries, so a
+        // socket whose fd index landed past that was silently missed.
+        const TARGET_FDS: libc::rlim_t = 2200;
+
+        unsafe {
+            let mut lim = libc::rlimit {
+                rlim_cur: 0,
+                rlim_max: 0,
+            };
+            if libc::getrlimit(libc::RLIMIT_NOFILE, &mut lim) != 0 {
+                eprintln!("skipping: getrlimit failed");
+                return;
+            }
+            if lim.rlim_cur < TARGET_FDS {
+                let raised = libc::rlimit {
+                    rlim_cur: TARGET_FDS.min(lim.rlim_max),
+                    rlim_max: lim.rlim_max,
+                };
+                if libc::setrlimit(libc::RLIMIT_NOFILE, &raised) != 0
+                    || raised.rlim_cur < TARGET_FDS
+                {
+                    eprintln!("skipping: cannot raise RLIMIT_NOFILE to {}", 
TARGET_FDS);
+                    return;
+                }
+            }
+        }
+
+        // Hold enough fds open that the listener bound afterwards gets an fd
+        // index well past the old 1024 cap.
+        let mut hoard = Vec::new();
+        for _ in 0..1500 {
+            match std::fs::File::open("/dev/null") {
+                Ok(f) => hoard.push(f),
+                Err(_) => break,
+            }
+        }
+        if hoard.len() < 1200 {
+            eprintln!("skipping: could only open {} fds", hoard.len());
+            return;
+        }
+
+        let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+        let port = listener.local_addr().unwrap().port();
+
+        let processes = find_target_processes(port).unwrap();
+        assert!(
+            !processes.is_empty(),
+            "Expected to find listener on port {} even with fd index > 1024",
+            port
+        );
+
+        drop(listener);
+        drop(hoard);
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/src/main.rs 
new/killport-2.0.2/src/main.rs
--- old/killport-2.0.1/src/main.rs      2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/src/main.rs      2026-08-01 10:04:31.000000000 +0200
@@ -60,8 +60,10 @@
         exit(1);
     });
 
-    // Attempt to kill processes listening on specified ports
+    // Attempt to kill processes listening on specified ports. A failing
+    // port must not prevent the remaining ports from being processed.
     let mut any_not_found = false;
+    let mut any_error = false;
 
     for port in args.ports {
         match killport.kill_service_by_port(port, args.signal.clone(), 
args.mode, args.dry_run) {
@@ -84,12 +86,17 @@
                 }
             }
             Err(err) => {
-                error!("{}", err);
-                exit(1);
+                error!("Port {}: {}", port, err);
+                any_error = true;
             }
         }
     }
 
+    // Exit with the worst outcome across all ports:
+    // error (1) > nothing found (2, unless --no-fail) > success (0).
+    if any_error {
+        exit(1);
+    }
     if any_not_found && !args.no_fail {
         exit(2);
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/killport-2.0.1/src/windows.rs 
new/killport-2.0.2/src/windows.rs
--- old/killport-2.0.1/src/windows.rs   2026-07-06 19:46:23.000000000 +0200
+++ new/killport-2.0.2/src/windows.rs   2026-08-01 10:04:31.000000000 +0200
@@ -413,8 +413,12 @@
                 .for_each(|element| {
                     // Convert the port value
                     let local_port: u16 = (element.dwLocalPort as u16).to_be();
-                    if local_port == port {
-                        pids.insert(element.dwOwningPid);
+                    // PID 0 ([System Process]) owns TIME_WAIT rows and PID 4
+                    // (System) owns kernel sockets; neither can nor should be
+                    // terminated, and opening PID 0 fails with 0x57.
+                    let pid = element.dwOwningPid;
+                    if local_port == port && pid != 0 && pid != 4 {
+                        pids.insert(pid);
                     }
                 });
         }

++++++ vendor.tar.xz ++++++
/work/SRC/openSUSE:Factory/killport/vendor.tar.xz 
/work/SRC/openSUSE:Factory/.killport.new.16738/vendor.tar.xz differ: char 15, 
line 1

Reply via email to