Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

librdkafka 2.2.0 uses system to run kinit and hence can receive SIGCHLD #4830

Open
1 of 4 tasks
marlowa opened this issue Aug 24, 2024 · 0 comments
Open
1 of 4 tasks

Comments

@marlowa
Copy link

marlowa commented Aug 24, 2024

Read the FAQ first: https://github.com/confluentinc/librdkafka/wiki/FAQ

Do NOT create issues for questions, use the discussion forum: https://github.com/confluentinc/librdkafka/discussions

Description

When an application uses librdkafka and another library that uses SIGCHLD for its own purposes, SIGCHLD will not be ignored and cannot, must not be ignored. When the application uses kerberos with kafka then librdkafka will run the kinit command. It uses the system call to do this, see the function rd_kafka_sasl_cyrus_kinit_refresh. This means the calling thread will receive SIGCHLD. The code is assuming that the caller will be able to disable that signal. This assumption is not stated anywhere in the documentation. It is quite common to disable that signal. However, not every application can do so. Those that can't, ones that use SIGCHLD for reasons of their own, will get their handler invoked when the kinit command completes. This is certainly not the intention of librdkafka.

How to reproduce

I cannot reproduce any of the details of the application I am working on, since it is proprietary. All I can say is that I am using librdkafka 2.2.0 on RHEL8 and the app is configured to use kerberos authentication with the kinit command configured as the appropriate kafka parameter. The application is also using a proprietary library that uses SIGCHLD for its own purposes. The thread that uses librdkafka is started by that proprietary library so when SIGCHLD is received that library reports that SIGCHLD has been received. The app is a long-running app so its logfile contains many of these messages as the day goes on.

Checklist

IMPORTANT: We will close issues where the checklist has not been completed.
Please provide the following information:

  • librdkafka version (release number or git tag): <REPLACE with e.g., v0.10.5 or a git sha. NOT "latest" or "current">
    2.2.0
  • Apache Kafka version: <REPLACE with e.g., 0.10.2.3>
    The one that comes with confluent 7. I think it is kafka 3.0.
  • librdkafka client configuration: <REPLACE with e.g., message.timeout.ms=123, auto.reset.offset=earliest, ..>
    kerberos authentication
  • Operating system: <REPLACE with e.g., Centos 5 (x64)>
    RHEL8

Suggested fix

Instead of calling system in rd_kafka_sasl_cyrus_kinit_refresh one could call a new function that librdkafka will have to implement. It would use the double-fork-technique such that kinit is run by a grandchild rather than a child. The grandchild can indicate completion to the parent using a standard unix/linux pipe. I asked chatGPT about this problem and it gave sample code for both aspects of the solution. They are reproduced in a simple example program below:

int main() {
int pipefd[2];
pipe(pipefd);

pid_t pid = fork();
if (pid == 0) {
    // First child process
    pid_t grandchild_pid = fork();
    if (grandchild_pid > 0) {
        // First child exits immediately
        exit(0);
    } else if (grandchild_pid == 0) {
        // Grandchild process
        close(pipefd[0]);  // Close the read end of the pipe
        // ... Perform some task here ...
        sleep(2); // Simulate some work

        // Notify parent of completion
        write(pipefd[1], "done", 4);
        close(pipefd[1]);  // Close the write end after writing
        exit(0);
    }
} else if (pid > 0) {
    // Parent process
    waitpid(pid, NULL, 0);  // Wait for the first child

    // Now wait for the grandchild to complete
    char buffer[5] = {0};
    close(pipefd[1]);  // Close the write end of the pipe
    read(pipefd[0], buffer, 4);
    close(pipefd[0]);

    if (strncmp(buffer, "done", 4) == 0) {
        printf("Grandchild has completed its task.\n");
    }
}

return 0;

}

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant