Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Child process keeps crow instance running. #866

Open
kyrylolvov opened this issue Aug 12, 2024 · 9 comments
Open

Child process keeps crow instance running. #866

kyrylolvov opened this issue Aug 12, 2024 · 9 comments
Labels
question Issue can be closed by providing information

Comments

@kyrylolvov
Copy link

Hello! Is there a way to somehow kill crow inside a child process or not let it inherit?

#define CROW_JSON_USE_MAP

#include "controllers/resources_controller.h"

crow::App<middleware::RestApiMiddleware> app;

int main() {
    std::cout << "Starting ArcHPC Baremetal API..." << std::endl;

    api::ResourcesController::init();

    app.port(18080).multithreaded().run();

    return 0;
}
#include "controllers/resources_controller.h"

#include <sys/socket.h>
#include <unistd.h>

namespace api {

void ResourcesController::init() {
    CROW_ROUTE(app, "/resources").methods(crow::HTTPMethod::GET)(listResources);
    CROW_ROUTE(app, "/resources").methods(crow::HTTPMethod::POST)(createResource);
}

void ResourcesController::listResources(const crow::request &req, crow::response &res) {
    crow::json::wvalue response;
    response["data"] = "Resources List";

    res.code = 201;
    res.body = response.dump();

    res.end();
}

void ResourcesController::createResource(const crow::request &req, crow::response &res) {
    crow::json::wvalue response;
    response["data"] = "Create Resource";

    pid_t pid = fork();

    if (pid == -1) {
        std::cerr << "Fork failed" << std::endl;

        exit(1);
    } else if (pid == 0) {
        // Child process
        std::cout << "Child process: PID = " << getpid() << std::endl;

        app.stop();

        // Simulate some work
        while (1) {
        };

        std::cout << "Child process exiting" << std::endl;

        exit(0);
    } else {
        // Parent process
        std::cout << "Parent process: Child PID = " << pid << std::endl;

        res.code = 201;
        res.body = response.dump();

        res.end();
    }
}

}   // namespace api

This is my code. Even though logs for closing app are printed in the console, when I exit the app, the port is still taken.

@kyrylolvov
Copy link
Author

Is there a solution which doesn't involve killing the child processes? Rather just killing crow app.

@kyrylolvov
Copy link
Author

@gittiver Any idea on how to solve this? Somehow the .stop() doesn't fully close the file descriptors (at least in a child process).

@gittiver
Copy link
Member

Not really.

There is a while(1) loop after stop() in createResource.
What about the used middleware, maybe remove it temporarily to check if it works then?
and do you use the master branch or the release?

@gittiver gittiver added the question Issue can be closed by providing information label Aug 14, 2024
@kyrylolvov
Copy link
Author

Not really.

There is a while(1) loop after stop() in createResource. What about the used middleware, maybe remove it temporarily to check if it works then? and do you use the master branch or the release?

The used middleware is just setting Content-Type header to json.

#pragma once

#include "crow.h"

namespace middleware {

struct RestApiMiddleware {
    struct context {};

    void before_handle(crow::request &req, crow::response &res, context &ctx) {}

    void after_handle(crow::request &req, crow::response &res, context &ctx) {
        res.set_header("Content-Type", "application/json");
    }
};

}   // namespace middleware

I have just updated Crow to latest release, and it's still not fully stopped in the child process.

I wrote this function, and calling it in the child process actually fixes the issue.

#include "utilities/common.h"

void utilities::CommonUtilities::cleanUpSocket(int port) {
    // Stop the application
    app.stop();

    // Iterate through all possible file descriptors
    for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) {
        struct sockaddr_in addr;
        socklen_t len = sizeof(addr);

        // Try to get the socket name for the current file descriptor
        if (getsockname(fd, (struct sockaddr *)&addr, &len) == 0) {
            // Check if the socket is an IPv4 socket and matches the given port
            if (addr.sin_family == AF_INET && ntohs(addr.sin_port) == port) {
                std::cout << "Closed socket on port " << port << " in child process" << std::endl;
                // Close the socket
                close(fd);
                // Exit the loop as we've found and closed the desired socket
                break;
            }
        }
    }
}

@gittiver
Copy link
Member

Maybe I am wrong but you are closing the server in the request_handler but after that the :: after_handle of the middleware is called by the app, I am not shure, possibly this is the reason why the server does not stop completely.

Could you check without the middlewar, if the server stops correctly?

@gittiver
Copy link
Member

btw. you can directly return a json object from the handler, then the content-type will be set correctly already.

@kyrylolvov
Copy link
Author

Maybe I am wrong but you are closing the server in the request_handler but after that the :: after_handle of the middleware is called by the app, I am not shure, possibly this is the reason why the server does not stop completely.

Could you check without the middlewar, if the server stops correctly?

Yeah, I just tried removing middleware and calling app.stop() in the child, without the function to close sockets manually. The logs that it has been closed appear, but when I exit the program, ss -ln src :18080 shows that the port is still taken.

@kyrylolvov
Copy link
Author

btw. you can directly return a json object from the handler, then the content-type will be set correctly already.

But if I just return the json object directly, how do I change the status code of the response? Couldn't find that in the examples.

@gittiver
Copy link
Member

gittiver commented Oct 24, 2024

btw. you can directly return a json object from the handler, then the content-type will be set correctly already.

But if I just return the json object directly, how do I change the status code of the response? Couldn't find that in the examples.

crow::response ResourcesController::listResources(const crow::request &req) {
crow::json::wvalue res;
res["data"] = "Resources List";

return response(201,res);
}

should do the trick.

And it spares the middleware.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Issue can be closed by providing information
Projects
None yet
Development

No branches or pull requests

2 participants