3

I have a polling program in C++ that cannot run cleanly as a background process. Therefore I want to prevent the user from being able to use & after invoking the name of my program in a Linux terminal.

How can I prevent the user from invoking & after the name of my script?

isakbob
  • 1,439
  • 2
  • 17
  • 39
  • 5
    I don't think you can, or should. A better approach would be to modify your program so it can run cleanly as a background process. – Jeremy Friesner Nov 28 '18 at 21:49
  • You can always grab your pid and then check if you're a background process (and if so, throw some error and exit). This is in bash, but it should be similar in C++ with system functions: https://unix.stackexchange.com/q/118462 – scohe001 Nov 28 '18 at 21:51
  • Doesn't work if the user pauses the program and than executes `bg` – JVApen Nov 28 '18 at 22:15
  • 4
    if the only thing that goes wrong is that the output gets mixed up then thats the users choice. They decide to & it for a reason, maybe they dont mind the messed up output, maybe they have piped the output to a file, maybe they are sure there is no other output to get scrambled with – pm100 Nov 28 '18 at 22:23
  • 1
    I agree with @pm100. The UNIX way is to trust that users know what they're doing. Avoid being too "helpful". If they mess themselves up, so be it. If output messes up their terminal they can restart your program with output redirected to a log file or to `/dev/null`. – John Kugelman Nov 28 '18 at 22:29

2 Answers2

1

While I agree with the comments saying that you should generally trust the users, this tries to answer the question as I've come to understand it from reading the comments ("If my program runs in the background, the command terminal is usable and operational, but the output of the program obscures user input and displaces the prompt").

This will not prevent the program from running, but streaming to std::cout will be discarded so the program can run without disturbing the user. Redirecting the output to a file or a pipe will still work.

#define _POSIX_SOURCE

#include <iostream>
#include <stdexcept>

#include <stdio.h>   // fileno
#include <unistd.h>  // isatty
#include <signal.h>  // signal
#include <fcntl.h>   // fcntl
#include <termios.h> // tcgetattr/tcsetattr

void handle_input(int /*sig*/) {
    signal(SIGTTIN, SIG_IGN);
    std::cin.setstate(std::ios::failbit);
}

void handle_output(int /*sig*/) {
    signal(SIGTTOU, SIG_IGN);
    std::cout.setstate(std::ios::failbit);
}

class TCAttr {
    int m_fd;
    termios m_attr;
public:
    TCAttr(int fd) : m_fd(fd), m_attr() {
        // get input, output and local attributes
        if(tcgetattr(m_fd, &m_attr) != 0)
            m_fd = -1;
    }
    TCAttr(FILE* fp) : TCAttr(fileno(fp)) {}
    TCAttr() : m_fd(-1), m_attr() {}
    ~TCAttr() {
        // restore original input, output and local attributes
        if(m_fd>=0)
            tcsetattr(m_fd, TCSADRAIN, &m_attr);
    }
    //
    bool set_lmode(tcflag_t flag) {
        termios tmp;
        if(tcgetattr(m_fd, &tmp)!=0) return false;
        tmp.c_lflag = flag;
        return tcsetattr(m_fd, TCSADRAIN, &tmp)==0;
    }
    bool add_lmode(tcflag_t flag) {
        termios tmp;
        if(tcgetattr(m_fd, &tmp)!=0) return false;
        tmp.c_lflag |= flag;
        return tcsetattr(m_fd, TCSADRAIN, &tmp)==0;
    }
    bool remove_lmode(tcflag_t flag) {
        termios tmp;
        if(tcgetattr(m_fd, &tmp)!=0) return false;
        tmp.c_lflag &= ~flag;
        return tcsetattr(m_fd, TCSADRAIN, &tmp)==0;
    }
};

int main() {
    TCAttr tca(stdout);

    if(isatty(fileno(stdin))) {
        // if tty input is requested in background mode
        // SIGTTIN will be sent
        signal(SIGTTIN, handle_input);
    }

    if(isatty(fileno(stdout))) {
        // if tty output is requested in background mode
        // SIGTTOU will be sent
        signal(SIGTTOU, handle_output);
        tca.add_lmode(TOSTOP);
    }

    std::cout << "Thanks for not streaming to a tty in background mode\n";
    std::string name;
    std::cin >> name;
    std::cout << name << "\n";
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

It is bad practice to do this. The UNIX convention is to assume that the user is doing the right thing and make accommodations accordingly. Either modify the program to make it run cleanly as a background process, or presume the user will pipe the output to something like /dev/null or a log.

isakbob
  • 1,439
  • 2
  • 17
  • 39