From bc31fc2ad26bd64e3df23b261516351a6daa03a5 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Fri, 13 Mar 2026 15:01:55 +0000 Subject: [PATCH 1/2] DAOS-17688 ddb: Prevent C stdout output truncation when piped or redirected Without disabling C stdout buffering, output from C functions such as printf() can be lost or delayed when the ddb command stdout is connected to a pipe or redirected to a file. This is caused by Unix fully-buffering C stdout in such cases, unlike Go's stdout which is always unbuffered. Fix this by calling initCStdout() at startup to disable C stdout buffering, ensuring all C output is flushed immediately regardless of how stdout is connected. Signed-off-by: Cedric Koch-Hofer --- src/control/cmd/ddb/main.go | 4 ++++ src/control/logging/cstdio.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/control/logging/cstdio.go diff --git a/src/control/cmd/ddb/main.go b/src/control/cmd/ddb/main.go index 9cf5bb2089d..3dbc49186ce 100644 --- a/src/control/cmd/ddb/main.go +++ b/src/control/cmd/ddb/main.go @@ -366,6 +366,10 @@ func parseOpts(args []string, opts *cliOptions) error { func main() { var opts cliOptions + if err := logging.InitCStdout(); err != nil { + exitWithError(err) + } + if err := parseOpts(os.Args[1:], &opts); err != nil { exitWithError(err) } diff --git a/src/control/logging/cstdio.go b/src/control/logging/cstdio.go new file mode 100644 index 00000000000..563f7ad5df0 --- /dev/null +++ b/src/control/logging/cstdio.go @@ -0,0 +1,33 @@ +package logging + +/* + #include +*/ +import "C" +import "github.com/pkg/errors" + +// InitCStdout disables C stdout buffering and must be called before any write to stdout. +// +// When a Go program's stdout is connected to a pipe (e.g. redirected or piped to another +// process), C stdout becomes fully-buffered by Unix convention instead of line-buffered, +// which can cause output from C functions such as printf() to be lost or delayed. +// Disabling buffering ensures that C stdout output is flushed immediately, consistent +// with the behavior of Go's own stdout. +// +// References: +// - https://stackoverflow.com/questions/42634640/using-cgo-why-does-c-output-not-survive-piping-when-golangs-does +// - https://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-strin +// - https://stackoverflow.com/questions/3723795/is-stdout-line-buffered-unbuffered-or-indeterminate-by-default +// - https://groups.google.com/g/comp.lang.c/c/dvRKt-iuO40# +func InitCStdout() error { + rc, err := C.setvbuf(C.stdout, nil, C._IONBF, 0) + if rc == 0 { + return nil + } + + if err != nil { + return errors.Wrap(err, "failed to disable C stdout buffering") + } + + return errors.Errorf("failed to disable C stdout buffering: rc=%d", int(rc)) +} From 2ad16ff93420879f6df940947ddb5f116c692a30 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Fri, 13 Mar 2026 15:01:55 +0000 Subject: [PATCH 2/2] DAOS-17688 ddb: Prevent C stdout output truncation when piped or redirected Fix reviewers comments: - Update function name - Update documentation - Add comment before function call Features: recovery Signed-off-by: Cedric Koch-Hofer --- src/control/cmd/ddb/main.go | 3 ++- src/control/logging/cstdio.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/control/cmd/ddb/main.go b/src/control/cmd/ddb/main.go index 3dbc49186ce..20d3e5279f7 100644 --- a/src/control/cmd/ddb/main.go +++ b/src/control/cmd/ddb/main.go @@ -366,7 +366,8 @@ func parseOpts(args []string, opts *cliOptions) error { func main() { var opts cliOptions - if err := logging.InitCStdout(); err != nil { + // Must be called before any write to stdout. + if err := logging.DisableCStdoutBuffering(); err != nil { exitWithError(err) } diff --git a/src/control/logging/cstdio.go b/src/control/logging/cstdio.go index 563f7ad5df0..0660f506243 100644 --- a/src/control/logging/cstdio.go +++ b/src/control/logging/cstdio.go @@ -6,7 +6,7 @@ package logging import "C" import "github.com/pkg/errors" -// InitCStdout disables C stdout buffering and must be called before any write to stdout. +// DisableCStdoutBuffering disables C code write to stdout buffering and must be called before any write to stdout. // // When a Go program's stdout is connected to a pipe (e.g. redirected or piped to another // process), C stdout becomes fully-buffered by Unix convention instead of line-buffered, @@ -19,7 +19,7 @@ import "github.com/pkg/errors" // - https://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-strin // - https://stackoverflow.com/questions/3723795/is-stdout-line-buffered-unbuffered-or-indeterminate-by-default // - https://groups.google.com/g/comp.lang.c/c/dvRKt-iuO40# -func InitCStdout() error { +func DisableCStdoutBuffering() error { rc, err := C.setvbuf(C.stdout, nil, C._IONBF, 0) if rc == 0 { return nil