Skip to content

Commit 1e02b38

Browse files
authored
Merge pull request #3437 from dougnazar/fix_libusb_threads_when_backgrounded
Fix libusb threads when backgrounded (and split `background()` method into parent/child phases)
2 parents cf973cc + 3e09321 commit 1e02b38

4 files changed

Lines changed: 79 additions & 16 deletions

File tree

NEWS.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ https://github.com/networkupstools/nut/milestone/13
138138
based UPS cards that already answer the rest of the subtree. [#2608]
139139

140140
- common code:
141+
* Refactored `common::background()` method used by numerous NUT daemons
142+
to handle parent and child code paths by separately addressable methods.
143+
This got used in the shared drivers `main` code file to preclude losing
144+
track of some libusb resources when forking the daemon. [#3437]
141145
* Revised 'dstate' machinery to track socket connections closed mid-way
142146
through a call, to avoid access after `free()`. [#3302]
143147
* When dealing with driver `AF_UNIX` sockets (non-WIN32), use non-blocking

common/common.c

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -817,30 +817,25 @@ void open_syslog(const char *progname)
817817
#endif /* WIN32 */
818818
}
819819

820-
/* close ttys and become a daemon */
821-
void background(void)
820+
int background_fork(void)
822821
{
823-
/* Normally we enable SYSLOG and disable STDERR,
824-
* unless NUT_DEBUG_SYSLOG envvar interferes as
825-
* interpreted in syslog_is_disabled() method: */
826-
int syslog_disabled = syslog_is_disabled(),
827-
stderr_disabled = (syslog_disabled == 0 || syslog_disabled == 2);
822+
int pid = 0;
828823

829824
#ifndef WIN32
830-
int pid;
831-
832825
if ((pid = fork()) < 0)
833826
fatal_with_errno(EXIT_FAILURE, "Unable to enter background");
834827
#endif /* !WIN32 */
835828

836-
if (!syslog_disabled)
837-
/* not disabled: NUT_DEBUG_SYSLOG is unset or invalid */
838-
xbit_set(&upslog_flags, UPSLOG_SYSLOG);
839-
if (stderr_disabled)
840-
/* NUT_DEBUG_SYSLOG="none" or unset/invalid */
841-
xbit_clear(&upslog_flags, UPSLOG_STDERR);
829+
return pid;
830+
}
842831

832+
/* close ttys and become a daemon */
833+
void background(void)
834+
{
843835
#ifndef WIN32
836+
int pid;
837+
838+
pid = background_fork();
844839
if (pid != 0) {
845840
/* parent */
846841
/* these are typically fds 0-2: */
@@ -849,9 +844,28 @@ void background(void)
849844
close(STDERR_FILENO);
850845
_exit(EXIT_SUCCESS);
851846
}
847+
#else /* WIN32 */
848+
NUT_WIN32_INCOMPLETE_MAYBE_NOT_APPLICABLE();
849+
#endif /* WIN32 */
850+
background_child();
851+
}
852+
853+
void background_child(void)
854+
{
855+
/* Normally we enable SYSLOG and disable STDERR,
856+
* unless NUT_DEBUG_SYSLOG envvar interferes as
857+
* interpreted in syslog_is_disabled() method: */
858+
int syslog_disabled = syslog_is_disabled(),
859+
stderr_disabled = (syslog_disabled == 0 || syslog_disabled == 2);
852860

853-
/* child */
861+
if (!syslog_disabled)
862+
/* not disabled: NUT_DEBUG_SYSLOG is unset or invalid */
863+
xbit_set(&upslog_flags, UPSLOG_SYSLOG);
864+
if (stderr_disabled)
865+
/* NUT_DEBUG_SYSLOG="none" or unset/invalid */
866+
xbit_clear(&upslog_flags, UPSLOG_STDERR);
854867

868+
#ifndef WIN32
855869
/* make fds 0-2 (typically) point somewhere defined */
856870
# ifdef HAVE_DUP2
857871
/* system can close (if needed) and (re-)open a specific FD number */

drivers/main.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,6 +2188,7 @@ int main(int argc, char **argv)
21882188
# ifndef WIN32
21892189
int cmd = 0;
21902190
pid_t oldpid = -1;
2191+
int background_pipefd[2];
21912192
# else /* WIN32 */
21922193
/* FIXME NUT_WIN32_INCOMPLETE : *actually* handle WIN32 builds too */
21932194
const char * cmd = NULL;
@@ -2995,6 +2996,34 @@ int main(int argc, char **argv)
29952996
* when its a pdu! */
29962997
dstate_setinfo("device.type", "ups");
29972998

2999+
#ifndef WIN32
3000+
if (foreground == 0) {
3001+
/* start backgrounding */
3002+
int ret, pid;
3003+
3004+
ret = pipe(background_pipefd);
3005+
if (ret)
3006+
fatal_with_errno(EXIT_FAILURE, "pipe creation failed");
3007+
3008+
pid = background_fork();
3009+
if (pid > 0) {
3010+
/* parent: wait for child to send success or exit */
3011+
char ch;
3012+
3013+
close(background_pipefd[1]);
3014+
3015+
/* Wait for child */
3016+
ret = read(background_pipefd[0], &ch, 1);
3017+
3018+
close(background_pipefd[0]);
3019+
close(STDIN_FILENO);
3020+
close(STDOUT_FILENO);
3021+
close(STDERR_FILENO);
3022+
_exit(ret == 1 ? EXIT_SUCCESS : EXIT_FAILURE);
3023+
}
3024+
}
3025+
#endif
3026+
29983027
dstate_setinfo("driver.state", "init.device");
29993028
upsdrv_callbacks.upsdrv_initups();
30003029
dstate_setinfo("driver.state", "init.quiet");
@@ -3192,11 +3221,23 @@ int main(int argc, char **argv)
31923221

31933222
switch (foreground) {
31943223
case 0:
3224+
#ifndef WIN32
3225+
/* close handles */
3226+
background_child();
3227+
3228+
/* notify parent of success */
3229+
i = write(background_pipefd[1], "G", 1);
3230+
if (i < 1)
3231+
upslogx(LOG_ERR, "Unable to call parent pipe for shutdown");
3232+
close(background_pipefd[1]);
3233+
#else
31953234
background();
3235+
#endif
31963236
/* We had saved a PID before backgrounding, but
31973237
* it changes when backgrounding - so save again
31983238
*/
31993239
writepid(pidfn);
3240+
32003241
break;
32013242

32023243
/* >0: Keep the initial PID; don't care about "!dump_data" here

include/common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@ void open_syslog(const char *progname);
287287
/* close ttys and become a daemon */
288288
void background(void);
289289

290+
/* Support functions for backgrounding */
291+
int background_fork(void);
292+
void background_child(void);
293+
290294
/* allow tagging the (forked) process in logs to ease debugging */
291295
const char *getproctag(void);
292296
/* save a copy of tag, or call with NULL to clean and free the internal buffer;

0 commit comments

Comments
 (0)