Skip to content

nut-scanner: retry SNMP session opens after descriptor exhaustion - #3673

Open
user01010111 wants to merge 20 commits into
networkupstools:masterfrom
user01010111:fix/snmp-emfile-recovery
Open

user01010111 wants to merge 20 commits into
networkupstools:masterfrom
user01010111:fix/snmp-emfile-recovery

Conversation

@user01010111

Copy link
Copy Markdown
Contributor

Depends on #3649. I am opening this as a draft while that PR is unmerged.
The branch includes its four existing commits followed by this four-file
SNMP recovery change.

I retain SNMP targets whose session open fails with EMFILE and retry them one
at a time after the original SNMP scans finish. If a serial attempt still runs
out of descriptors, I report that host and stop retrying it. Other session-open
errors keep their existing handling.

This follows PR #3649 and partially addresses #2576. It uses the existing
admission helpers and does not adjust shared thread limits or other scan types.

I tested the actual scanner worker in an instrumented harness with NetSNMP
5.9.5.2 and a loopback SNMP responder. The baseline missed all five targets under
controlled descriptor exhaustion; this change found all five exactly once after
descriptors were released. Persistent exhaustion terminated with a warning per
host and balanced descriptor and permit counts.

I also ran 124 focused scenarios across semaphore, counter-only and no-thread
Linux builds with address, undefined-behaviour and leak sanitizers. The GNU89
build, project checks and distcheck-light passed, including 16/16 test
programmes and staged install/uninstall. Documentation rendering was disabled;
the light distribution check used its standard placeholder manual pages.

AI assistance: OpenAI Codex, including a focused review of worker ownership and
admission accounting.

Skip inactive protocol semaphores, unwind partial admission, and return
owned permits when worker allocation or creation fails. Allocate tracking
entries before starting workers and keep NUT hostname and authentication
cleanup with the worker until it finishes.

Add offline regression tests using real threads and semaphores, preserve
the counter and non-threaded paths, and record the change in NEWS.

Related to networkupstools#1166. Global semaphore initialisation policy remains separate.

Prepared with assistance from OpenAI Codex (gpt-6-astra).

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
Pass the configured SSL include flags to the scanner tests for NSS
builds. Order scanner recursion after common and client dependencies,
and generate the SNMP header before compiling the test source.

Prepared with assistance from OpenAI Codex (gpt-6-astra).

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
Track the protocol semaphore only after successful initialisation, and
check destruction or close before clearing fixture state. Assert clean
state at scenario entry and clear the global semaphore after teardown.

Assisted by OpenAI Codex (gpt-6-astra).

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
Move the shared worker helpers into nutscan-thread.c and keep declarations in the header. Preserve internal symbol visibility and the existing failure-injection tests.

Assisted by OpenAI Codex (gpt-6-astra).

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
Retain SNMP hosts whose session open fails with EMFILE and retry them
serially after the original workers finish. Report persistent exhaustion
without requeueing, preserving admission accounting and target ownership.

Add focused recovery tests and a NEWS entry. This partially addresses
issue networkupstools#2576 and follows the admission helpers from PR networkupstools#3649.

Assisted by OpenAI Codex.

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

A ZIP file with standard source tarball and another tarball with pre-built docs for commit d961bb1 is temporarily available: NUT-tarballs-PR-3673.zip.

Comment thread tests/nutscan-snmp-retry-test.c Dismissed
Comment thread tests/nutscan-snmp-retry-test.c Dismissed
void *arg;
};

static void *offline_worker(void *opaque)
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Comment thread tests/nutscan-thread-test.c Fixed
Keep test setup, scanner execution and checks active when built with -DNDEBUG.

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
@jimklimov jimklimov added nut-scanner SNMP AI For good or bad, machine tools are upon us. Humans are still the responsible ones. portability We want NUT to build and run everywhere possible labels Sep 21, 2026
@jimklimov jimklimov added this to the 2.8.6 milestone Sep 21, 2026
…e other out-of-dir stuff [networkupstools#3673]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
…ode [networkupstools#3673]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
… acquire [networkupstools#3673]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
…arg() for simplicity [networkupstools#3673]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
…safe side to cast it as (unsigned int) [networkupstools#3673]

...This may get revisited after we consistently use
one boolean type in the code base.

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>

@jimklimov jimklimov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted a few specific questions here, mostly about erring on the safe side while working with memory, and will post some casual fixes to the code too.

General questions:

  • Why is this refactor not applied to serial port scanning? IIRC it used semaphores too?..
  • Why is the fallback (to follow up with overflowing IP addresses) only applied to SNMP and not other protocols? The idea does seem sane...

Comment on lines 1040 to 1043

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a particular reason to remove this (opportunistic) cleanup? Did it break something? Do we potentially lose something with it dropped?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removed branch posted permits for entries marked inactive. The refactor records only successfully created workers; failed creation releases its admission permits without adding a tracked entry. Successful workers release their permits when joined. An inactive entry therefore has no outstanding permit to release here, and normal permit cleanup still happens through the join path.

} /* if: could we "pass" or not? */
} /* while */

free(ip_str);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we set ip_str=NULL here too, erring on the safe side?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added ip_str = NULL after the free in f9269a1.

#if defined HAVE_PTHREAD && (defined HAVE_SEMAPHORE_UNNAMED || defined HAVE_SEMAPHORE_NAMED)
nut_scanner_semaphore_release(semaphore, semaphore_scantype, max_threads_scantype);
#endif
free(ip_dest);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should safely set ip_dest=NULL here...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added ip_dest = NULL after the free in f9269a1.

int ret = nut_scanner_thread_create(&thread_array, &thread_count,
list_nut_devices_thready, (void *)nut_arg);
if (ret != 0) {
free_nut_arg(nut_arg);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set nut_arg=NULL here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added nut_arg = NULL after cleanup in f9269a1.

if (nut_arg) {
free_nut_arg(nut_arg);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set nut_arg=NULL here to err on the safe side? Maybe propagate it as ** pointer so all owners/observers end up with NULL when they should?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added nut_arg = NULL at both cleanup callers in f9269a1 and retained your NULL-input guard. I kept free_nut_arg() taking the pointer by value: ownership transfers to the worker when thread creation succeeds, and the dispatcher handles cleanup if creation fails. Each owner now clears its local pointer after cleanup. A ** parameter would clear the supplied pointer, but would not clear other aliases.

} /* if: could we "pass" or not? */
} /* while */

free(ip_str);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we set ip_str=NULL here too, erring on the safe side?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added ip_str = NULL after the free in f9269a1.

} /* if: could we "pass" or not? */
} /* while */

free(ip_str);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we set ip_str=NULL here too, erring on the safe side?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added ip_str = NULL after the free in f9269a1.

@jimklimov jimklimov added the refactor/fightwarn PR or issue proposal to improve code maintainability without functional changes, or to fix warnings label Sep 21, 2026
Clear local pointers after releasing scanner-owned storage.

Assisted by OpenAI Codex.

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
Reuse the shared admission and thread helpers for serial scans, release
permits when worker creation fails, and join workers before freeing their
borrowed port names or destroying the device mutex.

Extend the existing offline admission test for serial scans and record
the change in NEWS.

Assisted by OpenAI Codex.

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
Comment thread tests/nutscan-thread-test.c Dismissed
Comment thread tests/nutscan-thread-test.c Dismissed
Comment thread tests/nutscan-thread-test.c Dismissed
Comment thread tests/nutscan-thread-test.c Dismissed
Comment thread tests/nutscan-thread-test.c Dismissed
Link the serial archive before the common libraries that supply its
logging and Windows serial helpers. This fixes the unresolved symbols
when linking nutscan-serial-test with shared private libraries on MinGW.

Assisted by OpenAI Codex (GPT-6).

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
@user01010111

Copy link
Copy Markdown
Contributor Author

Serial scanning was an omission. Commit 6272293 now uses the existing admission helpers there, allocates tracking before starting workers, releases permits when thread creation fails, and keeps borrowed port names alive until all workers are joined.

I have kept descriptor-exhaustion recovery limited to SNMP because that is the session-open failure path reproduced and tested here. Extending it to the other scanner backends needs validation of each library's error reporting and cleanup before retrying. This remains a partial fix for #2576.

@user01010111
user01010111 marked this pull request as ready for review October 12, 2026 08:26
Carry the common scanner-test Net-SNMP executable flags and SSL dependency
so static archive references resolve for every fixture, including retries.
Retain the original library passthrough flags and cache both forms through
the existing dependency macro.

Keep both fixture mutexes separate from the library and order common-library
sub-makes to prevent races over shared replacement-function objects.

Validated static scanner and retry tests on Linux and native MinGW Windows,
clean two-job builds, and distcheck-light with install/uninstall.

Prepared with OpenAI Codex (GPT-6) assistance, following the earlier
Claude Opus 5.5 review.

Signed-off-by: user01010111 <12504630+user01010111@users.noreply.github.com>
@user01010111

Copy link
Copy Markdown
Contributor Author

The scanner and retry tests pass against static libnutscan on Linux and MinGW Windows, including clean parallel builds with two jobs. This carries the corrected #3649 common test-link rule, common-library build ordering and SSL dependency, so the retry target receives the static Net-SNMP libraries as well. Both fixtures keep their mutex separate from the library's definition. The distribution and install/uninstall checks pass.

Prepared with Codex assistance, following the earlier Claude Opus 5.5 review.

@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 14, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 23, 2026
@github-actions github-actions Bot deleted a comment from AppVeyorBot Oct 23, 2026
@AppVeyorBot

Copy link
Copy Markdown

✅ Build nut 2.8.5.5897-master completed (commit d154405169 by @jimklimov)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI For good or bad, machine tools are upon us. Humans are still the responsible ones. nut-scanner portability We want NUT to build and run everywhere possible refactor/fightwarn PR or issue proposal to improve code maintainability without functional changes, or to fix warnings SNMP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants