Search code examples
shellterminaltriggersbackground-process

trigger a foreground app by a script runnning in background (in same tty, linux)


tui.c: a simple ncurses hello world tui app:

// gcc -o tui tui.c -lncurses
#include <ncurses.h>

int main() {
    initscr();                // start curses mode
    printw("Hello, World!");  // print Hello, World
    refresh();                // print it on the real screen
    getch();                  // wait for user input
    endwin();                 // end curses mode
    return 0;
}

test.c: a c program need to running on background (avoid blocking other terminal operations & tasks)

// gcc -o test test.c -lpthread
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>

int countdown = 2;

void start_tui(){
    system("./start.sh");
}

void sigusr1_handle(int signum){
    system("uptime");
}

void sigtstp_handle(int signum){
    _exit(0);
}

void* func2(void* callback){
    signal(SIGUSR1, sigusr1_handle);                           // trap SIGUSR1
    signal(SIGTSTP, sigtstp_handle);                           // trap SIGTSTP
    printf("this is func2 running...");
    sleep(3);
    int status;

    start_tui();                                               // just start the TUI app

    void (*reset_timer)() = callback;                          // set callback
    reset_timer();                                             // execute callback
    return NULL;
}

void reset(){                                                  // callback func definition
    countdown = 2;                                             // reset count down
}

void func1(){
    while(1){
        while(countdown > 0){                                  // timer 3s
            printf("count down: %d\n", countdown);
            sleep(1);
            countdown--;
        }
        printf("count down: %d\n", countdown);
        if(!countdown){                                        // segregate the process logic
            pthread_t thread;
            pthread_create(&thread, NULL, func2, reset);       // exec func2
            pthread_join(thread, NULL);                        // wait for thread end
            printf("func2 has stopped.\n");
        }
    }
}

int main(){
    func1();
    return 0;
}

start.sh: called by above c program, to startup tui window.

#!/bin/sh

set -m   # enable job control

./tui    # invoke the ncurses tui app 

expected behavior:

$ ./test &      # this should be running in bg, so that no block on other user task on this tty
count down 2
count down 1
count down 0
hello world!    # tui window output on foreground
<press any key to exit from ncurse tui window, interrupt the tui thread>
count down 2
count down 1
count down 0

actual behavior:

$ ./test &      # this should be running in bg, so that no block on other user task on this tty
count down 2
count down 1
count down 0
hello world!    # tui window output on foreground
<try press any key to exit from ncurse tui window, interrupt the tui thread>

<OOPs, the ssh terminal logged out. ssh session is down>
<login again, the tui hello world process is still running...
<the keystroke wont convey msg to inner thread of a background process>

the question:
0 if the test binary run on foreground:

$ test

then everything work's fine, but terminal block to neglect all userinput.

1 how can a background running task invoke a foreground tui app, let the terminal input active for the foreground app?

2 I'm trying to bring the foreground tui app background, but let it accept for stdin and output to stdout of current terminal.


Solution

  • Putting a triggeable job in background

    For putting a job in backroung AND keep tty useable for other foregroung jobs, you have to free STANDARDS descriptors (STD I/O and STDERR):

    Here is a minimal sample (tested under busybox, dash and others):

    Nota instead of your helloworld program, I run uptime which will output something too.

    #!/bin/sh
    
    bgTask() {
        trap 'uptime >&4' USR1
        trap 'break' TSTP
        while :; do
            read -r _
        done
        echo "SUB-Process $$ ended." >&4
    }
    
    exec 4>&1
    bgTask </dev/null >/dev/null 2>&1 &
    
    printf 'Task bgTask %d started!\nYou could trigg them by\n  - kill -USR1 %d
    Then you could stop them by\n  - kill -TSTP %d\n' $! $! $!