Belal Qatoum
Belal Qatoum

Reputation: 21

How to fork multiple child processes to open separate terminal windows?

I am working on building a simple X O (tic-tac-toe) game in C on a Linux system. I want to fork three child processes:

Each child process should open in a separate terminal window to interact with the user. For example, Player 1 will input the row and column to place their "X" on the board.

I would like to achieve this using only one program file. How can I run this program so that each child process opens its own terminal window for interaction?

I have managed to open separate terminals for each child process using the system() function and running simple commands like ls, but I want to run the actual child process functions in these new terminals.

Upvotes: 2

Views: 185

Answers (2)

Belal Qatoum
Belal Qatoum

Reputation: 21

The solution to running specific code in a child process involves creating a separate function for each task you want the child to perform. The key idea is to open a new terminal for each child process. Below is an example demonstrating how to achieve this using system() and passing command-line arguments to the child processes.

#include <stdio.h>

void display_code(/* appropriate parameters */);
void player1(/* appropriate parameters */);
void player2(/* appropriate parameters */);

int main(int argc, char *argv[]) {
    // Initialize required variables and IPC mechanisms (message queue, shared memory, etc.)
    
    if (argc > 1) {
        if (strcmp(argv[1], "display") == 0) {
            display_code(/* appropriate arguments */);  // Function for display child
        } else if (strcmp(argv[1], "player1") == 0) {
            player1(/* appropriate arguments */);  // Function for player 1 child
        } else if (strcmp(argv[1], "player2") == 0) {
            player2(/* appropriate arguments */);  // Function for player 2 child
        }
        exit(0);
    }

    // Fork and execute each child process in a new terminal
    if (fork() == 0) {  // Display child
        system("konsole --hold -e './bo display' --title 'Display'");
        exit(0);
    }

    if (fork() == 0) {  // Player 1 child
        system("konsole --hold -e './bo player1' --title 'Player 1'");
        exit(0);
    }

    if (fork() == 0) {  // Player 2 child
        system("konsole --hold -e './bo player2' --title 'Player 2'");
        exit(0);
    }

    // Parent process
    for (int i = 0; i < 3; i++) {
        wait(NULL);
    }

    return 0;
}

In this code:

main() initializes the necessary resources and checks if any command-line arguments are passed to determine the function to execute.

Each child process is forked and executed in a new terminal using the system() function with the konsole command.

The parent process waits for all child processes to complete before cleaning up IPC resources and exiting.

Following this approach, you can run each child process in a separate terminal and execute specific functions based on the command-line arguments.

Upvotes: 0

rand&#39;Chris
rand&#39;Chris

Reputation: 1330

Interesting - I think you're making this a little bit too complicated though. I agree with the approach mentioned by @Hackerman. Here's the idea, assuming that your application is text or curses based, and when you run your app you want to see two GUI terminals appear, one for each player:

  • In your main app, setup an AF_UNIX socket() for the X child and for the O child.
  • Then fork off the 'X' child, and exec gnome-terminal -- myapp X
  • Then fork off the 'O' child and exec gnome-terminal -- myapp O
  • Update your application to parse the X and O commandline arguments via int main( int argc, const char* const argv[] )
  • Communicate between client and server (child and parent) via the sockets.

Your question suggests you want the child process to create the terminal, but I don't see a way to open a "raw" gnome-terminal (I assume this is what you want to do based on your question tags). I had originally written to use pipe() for IPC, but gnome-terminal isn't going to give you direct access to stdin and stdout of the child processes. Something else may do that but I didn't find a "raw xterm" or "GUI getty" type application.

You can get a new pty via forkpty() and openpty() but I'm not that familiar with these and don't think you want to write your own terminal emulator. So instead, make the child be the gnome-terminal and have it run your logic based on the argument you pass. You could make these separate programs but you explicitly stated you want to use the same one, so your setup code will look something like:

struct sockaddr_un Xaddr, Oaddr;
int main( int argc, const char* const argv[] ) {
    char *sockdir = getenv( "XDG_RUNTIME_HOME" );
    if( !sockdir ) sockdir = "/tmp";
    // Create Xaddr and Oaddr temporary socket names; tmpnam() or getpid()
    // Alternately, use 1 socket and put "X" and "O" in the command stream.

    switch( argv[1][0] ) {
        case 'X': return do_X();
        case 'O': return do_O();
    } // default: assume the parent logic

    int Xchild = fork();
    if( 0 == Xchild ) execl( argv[0], "X", NULL );
    int Ochild = fork();
    if( 0 == Ochild ) execl( argv[0], "O", NULL );

    int Xsocket, Osocket;
    Xsocket = socket( AF_UNIX, SOCK_SEQPACKET, 0 );
    Osocket = socket( AF_UNIX, SOCK_SEQPACKET, 0 );
    /* ... See `man 7 unix` for details ... */
}
void do_X()
{
    int socket = socket( AF_UNIX, SOCK_SEQPACKET, 0 );
    connect( socket, Xaddr, sizeof( Xaddr ));
    // "X" player logic
}
void do_O()
{
    int socket = socket( AF_UNIX, SOCK_SEQPACKET, 0 );
    connect( socket, Oaddr, sizeof( Oaddr ));
    // "O" player logic
}

I hope that's clear enough but have to admit I didn't test it. The principles should be sound but I'd love to hear if there's an easier way.

Regarding the 3rd process to display the resultant output, I would ask why that can't be done from the main program. However: opening a 3rd window would be the same general logic.

Upvotes: 0

Related Questions