Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QProcess signals not received in slots except in Debug with breakpoints?



  • Hello,
    I'm adding a "shutdown computer" feature on my App using QProcess.
    Here is how I do it:

            else if (_doShutdownWhenDone && !_shutdownCmd.isEmpty())
            {
                _shutdownProc = new QProcess();
                connect(_shutdownProc, &QProcess::readyReadStandardOutput, this, &NgPost::onShutdownProcReadyReadStandardOutput, Qt::DirectConnection);
                connect(_shutdownProc, &QProcess::readyReadStandardError,  this, &NgPost::onShutdownProcReadyReadStandardError,  Qt::DirectConnection);
                connect(_shutdownProc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &NgPost::onShutdownProcFinished, Qt::QueuedConnection);
                qDebug() << "Shutdown cmd: " << _shutdownCmd;
                _shutdownProc->start(_shutdownCmd);
            }
    

    So the shutdown command I'm testing for Linux is:

    /usr/bin/sudo -n /sbin/poweroff
    

    Here are my slots:

    void NgPost::onShutdownProcReadyReadStandardOutput()
    {
        QString line(_shutdownProc->readAllStandardOutput());
        _cout << "Shutdown out: " << line << endl << flush;
    }
    
    void NgPost::onShutdownProcReadyReadStandardError()
    {
        QString line(_shutdownProc->readAllStandardOutput());
        _cerr << "Shutdown ERROR: " << line << endl << flush;
    }
    
    void NgPost::onShutdownProcFinished(int exitCode)
    {
        qDebug() << "Shutdown proc finished with exitCode: " << exitCode;
        _shutdownProc->deleteLater();
        _shutdownProc = nullptr;
    }
    

    I'm testing with a user not having sudoers no password privilege.
    So the process due to the -n (non interactive) option should return 1 and write on Error:

    $ sudo -n /sbin/poweroff 
    sudo: a password is required
    

    I don't understand, I'm getting no outputs at all if I run in Release or Debug mode...
    So I imagine the _shutdownProc is not deleted...

    But as soon I debug it and add breakpoints in the slots, I'm arriving in them (first the Error one then Finished) and everything is fine.

    Any clue why I don't have the same behaviour without breakpoints?
    Cheers

    Edit: I've tried to use the static QProcess::execute and the HMI hangs. Any idea why?
    Why does it work with breakpoints and seems to never end without?
    Here is the cmd that hangs:

    QProcess::execute("/usr/bin/sudo -n /sbin/poweroff ");
    

    How can I use sudo with QProcess?


  • Qt Champions 2019

    @mbruel said in QProcess signals not received in slots except in Debug with breakpoints?:

    else if (_doShutdownWhenDone && !_shutdownCmd.isEmpty())
    {
    _shutdownProc = new QProcess();
    connect(_shutdownProc, &QProcess::readyReadStandardOutput, this, &NgPost::onShutdownProcReadyReadStandardOutput, Qt::DirectConnection);
    connect(_shutdownProc, &QProcess::readyReadStandardError, this, &NgPost::onShutdownProcReadyReadStandardError, Qt::DirectConnection);
    connect(_shutdownProc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &NgPost::onShutdownProcFinished, Qt::QueuedConnection);
    qDebug() << "Shutdown cmd: " << _shutdownCmd;
    _shutdownProc->start(_shutdownCmd);
    }

    Where do you actually execute this code?
    Are you sure it is actually executed?
    If the user is not in sudo group how is this going to work at all?
    Note: you should not pass command and its parameters as one string to QProcess, do it like shown in documentation:

    QString program = "./path/to/Qt/examples/widgets/analogclock";
    QStringList arguments;
    arguments << "-style" << "fusion";
    QProcess *myProcess = new QProcess(parent);
    myProcess->start(program, arguments);
    


  • @mbruel
    There should be no issues running a sudo, other than when it does come to prompting for password, which is a different matter. (Hint: That has been asked many times before, consider using sudo -S if you need to.)

    I cannot spot anything wrong in your code. Please try two things:

    • Just try with some other innocuous command. Verify your code does work.

    • Add slots onto started() & stateChanged().

    Also, what does your code do after the last line launching the start()? Do you enter a event loop to allow signals to be received and processed? And if _shutdownProc is a class member variable or whatever, make sure it is not getting deleted?

    I don't understand, I'm getting no outputs at all if I run in Release or Debug mode...

    I trust you are getting qDebug() << "Shutdown cmd: " << _shutdownCmd;?!

    Working with breakpoints but not without sounds a bit like a timing issue. Make sure that _shutdownProc does not get deleted elsewhere! And just for now comment out its deletion in onShutdownProcFinished(), just in case that can get called before it sees the message arrive on stderr. I agree that should still have shown output, and I agree I can't explain when you use QProcess::execute(), but you have to start looking somewhere!



  • @jsulm said in QProcess signals not received in slots except in Debug with breakpoints?:

    Where do you actually execute this code?
    Are you sure it is actually executed?
    If the user is not in sudo group how is this going to work at all?
    Note: you should not pass command and its parameters as one string to QProcess, do it like shown in documentation:

    The code is executed. I see my "Shutdown cmd: " line. It is executed at end of a task. So the event loop is running. If the user is not in sudo group, I expect an error and the process to finish properly.
    It doesn't change anything using start with args or in one go.

    @JonB said in QProcess signals not received in slots except in Debug with breakpoints?:

    I cannot spot anything wrong in your code. Please try two things:

    I've added slots for started(), stateChanged() and also errorOccurred().
    Here is the code now:

            else if (_doShutdownWhenDone && !_shutdownCmd.isEmpty())
            {
                int exitCode = QProcess::execute("/bin/ls", QStringList() << "-alh");
                qDebug() << QString("Shutdown exit code: %1").arg(exitCode);
                _shutdownProc = new QProcess();
                connect(_shutdownProc, &QProcess::readyReadStandardOutput, this, &NgPost::onShutdownProcReadyReadStandardOutput, Qt::DirectConnection);
                connect(_shutdownProc, &QProcess::readyReadStandardError,  this, &NgPost::onShutdownProcReadyReadStandardError,  Qt::DirectConnection);
                connect(_shutdownProc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &NgPost::onShutdownProcFinished, Qt::QueuedConnection);
                connect(_shutdownProc, &QProcess::started, this, &NgPost::onShutdownProcStarted, Qt::DirectConnection);
                connect(_shutdownProc, &QProcess::stateChanged,  this, &NgPost::onShutdownProcStateChanged,  Qt::DirectConnection);
                connect(_shutdownProc, &QProcess::errorOccurred,  this, &NgPost::onShutdownProcError, Qt::DirectConnection);
                qDebug() << "Shutdown cmd: " << _shutdownCmd;
                _shutdownProc->start("/usr/bin/sudo", QStringList() << "-n" << "/bin/ls");
    //            _shutdownProc->start(_shutdownCmd);
            }
    

    with the slots:

    void NgPost::onShutdownProcStarted()
    {
        qDebug() << "Shutdown proc Started";
    }
    
    void NgPost::onShutdownProcStateChanged(QProcess::ProcessState newState)
    {
        qDebug() << "Shutdown proc new State: " << newState;
    }
    
    void NgPost::onShutdownProcError(QProcess::ProcessError error)
    {
        qDebug() << "Shutdown proc Error: " << error;
    }
    

    Here is my output:
    Shutdown exit code: 0 (that's for the QProcess::execute of the ls (I've also just befor the output of the ls)
    Shutdown cmd: "/usr/bin/sudo -n /sbin/poweroff"
    Shutdown proc new State: QProcess::Starting
    Shutdown proc new State: QProcess::Running
    Shutdown proc Started

    So I don't know if it is relevant or not but the started slot is triggered after the state is changed to Running.
    I never get in the finished slot. So it sounds like the Process never ends.
    Kind of what I get if I replace the QProcess::execute to launch the same sudo command: it hangs...
    To be clear, if I put in first line:

               int exitCode = QProcess::execute("/usr/bin/sudo", QStringList() << "-n" << "/bin/ls");
               qDebug() << QString("Shutdown exit code: %1").arg(exitCode);
    

    The my IHM freeze (as my NgPost object lives in the Main thread with the HMI) and I never see the line "Shutdown exit code"

    So it kind of sounds to me there is an issue with sudo... like maybe the -n option is not working...
    But I don't get why if I run in debug with breakpoints in every slots, everything works fine!

    @JonB said in QProcess signals not received in slots except in Debug with breakpoints?:

    Also, what does your code do after the last line launching the start()? Do you enter a event loop to allow signals to be received and processed? And if _shutdownProc is a class member variable or whatever, make sure it is not getting deleted?

    My code does nothing after launching the start. It's normally waiting for some user actions. (the event loop is started, the HMI and this object lives both in the main Thread)
    _shutdownProc is never deleted. It would be ONLY in the finished slot...

    @JonB said in QProcess signals not received in slots except in Debug with breakpoints?:

    Working with breakpoints but not without sounds a bit like a timing issue. Make sure that _shutdownProc does not get deleted elsewhere! And just for now comment out its deletion in onShutdownProcFinished(), just in case that can get called before it sees the message arrive on stderr. I agree that should still have shown output, and I agree I can't explain when you use QProcess::execute(), but you have to start looking somewhere!

    Indeed it sounds like a timing issue but how to act on that? As I said, I'm not deleting the _shutdownProc.
    Commenting the deletion of _shutdownProc in the finished slot doesn't change anything. If I'm not using breakpoints, I never go in the finish slot :'(

    EDIT: I just tried to replace the -n sudo option by -S and I'm getting the same thing: the process never ends, I never get any the stdout or error slots... I really don't get it!
    Could sudo be in a state where it is waiting to read things? that's why it would be blocked?
    But normally with the -n command, it doesn't expect any input. Either it has the rights to run OR it just print out the error and finishes with 1.



  • @mbruel
    Things to try:

    • Get rid of sudo, try just a plain, innocuous /bin/ls. If you haven't already done that with current code, we must just ensure that works OK!

    • As user who will not have permission, try from shell terminal:

    /usr/bin/sudo -n /bin/ls </dev/null >&/dev/null
    

    Not quite the same as what's going on from Qt, but we want to verify its behaviour when stdin/stdout/stderr are not attached to a tty. It behaves differently (e..g if it is going to prompt for a password, that code can check isatty(0)).

    • When your process appears to be running but not exiting, go do a suitable ps to locate it among running processes. Do you see it, what state is it in?

    • Put your desired sudo statement into a shell script. Surround it with suitable logging lines. Let's see whether it completes the sudo line and logs next line.

    Just for the record, other people (though not me) have run sudo stuff from QProcess and it does work. Why your situation is different I don't know....



  • @JonB said in QProcess signals not received in slots except in Debug with breakpoints?:

    Get rid of sudo, try just a plain, innocuous /bin/ls. If you haven't already done that with current code, we must just ensure that works OK!

    working well:

     cmd:  "/usr/bin/sudo -n /sbin/poweroff"
    Shutdown proc new State:  QProcess::Starting
    Shutdown proc new State:  QProcess::Running
    Shutdown proc Started
    Shutdown proc new State:  QProcess::NotRunning
    Shutdown proc finished with exitCode:  0
    Shutdown out: total 39384
    drwxr-xr-x  3 bruel bruel    4096 Feb 13 13:18 .
    drwxr-xr-x 11 bruel bruel    4096 Feb 12 18:38 ..
    -rw-r--r--  1 bruel bruel  863848 Feb 13 11:23 AboutNgPost.o
    -rw-r--r--  1 bruel bruel  610704 Feb 13 11:23 ArticleBuilder.o
    ...
    

    @JonB said in QProcess signals not received in slots except in Debug with breakpoints?:

    As user who will not have permission, try from shell terminal:

    seems all good:

    ~$ /usr/bin/sudo -n /bin/ls </dev/null >&/dev/null
    ~$ echo $?
    1
    

    @JonB said in QProcess signals not received in slots except in Debug with breakpoints?:

    When your process appears to be running but not exiting, go do a suitable ps to locate it among running processes. Do you see it, what state is it in?

    Hum... I didn't try to check if I could see the process!
    When I first tried here is what I saw:

    bruel@riyad:~$ ps -ef | grep sudo
    206:root      4464     1  0 Feb12 tty2     00:00:00 /usr/bin/sudo /sbin/poweroff
    217:bruel     5338     1  0 00:20 tty2     00:00:00 sh -c /usr/bin/sudo /sbin/poweroff
    218:root      5339  5338  0 00:20 tty2     00:00:00 /usr/bin/sudo /sbin/poweroff
    219:bruel     5567     1  0 00:24 tty2     00:00:00 sh -c echo "toto" | sudo /sbin/poweroff
    220:root      5569  5567  0 00:24 tty2     00:00:00 sudo /sbin/poweroff
    221:root      6297     1  0 00:33 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    223:root      6601     1  0 00:36 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    224:root      6715     1  0 00:38 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    225:root      6830     1  0 00:39 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    226:root      6845     1  0 00:40 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    227:root      6904     1  0 00:41 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    228:root      7154     1  0 00:43 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    229:root      7239     1  0 00:46 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    230:root      7340     1  0 00:48 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    231:root      7723     1  0 00:54 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    233:root      7829     1  0 00:56 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    235:root      8268     1  0 01:41 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    236:root      8389     1  0 01:42 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    237:root      8776     1  0 02:15 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    238:root      9106     1  0 02:23 tty2     00:00:00 /usr/bin/sudo -n /sbin/poweroff
    248:root     10175     1  0 11:06 tty2     00:00:00 /usr/bin/sudo -n /bin/ls
    249:root     10265     1  0 11:08 tty2     00:00:00 /usr/bin/sudo -n /bin/ls
    252:root     11462     1  0 11:24 tty2     00:00:00 /usr/bin/sudo -n /bin/ls
    254:root     11628     1  0 11:49 tty2     00:00:00 /usr/bin/sudo -S /bin/ls
    274:root     13707 13672  0 13:23 tty2     00:00:00 /usr/bin/sudo -S /bin/ls
    280:bruel    13718  2148  0 13:24 pts/1    00:00:00 grep --color=auto -n -P sudo
    

    So many instances of sudo that were there either due to my tests in a terminal or previous instance of my App... I'm not sure why I had those ghosts (arf... don't remember the exact term..) cause I was stopping them with a Ctrl-C and had back the hand on the terminal.... and Qt should kill all his processes when the app is killed.

    So I've killed all of them. Try again the soft. Now it is working...
    I've set back the code to launch /bin/ls with sudo first with QProcess::execute then try to launch /sbin/poweroff with sudo also. Here is my output:

    sudo: a password is required
    "Shutdown exit code: 1"
     cmd:  "/usr/bin/sudo -n /sbin/poweroff"
    Shutdown proc new State:  QProcess::Starting
    
    Shutdown proc new State:  QProcess::Running
    Shutdown proc Started
    Shutdown ERROR: sudo: a password is required
    
    Shutdown proc new State:  QProcess::NotRunning
    Shutdown proc finished with exitCode:  1
    

    Hum... that's quite weird...

    I don't manage to get again some fantom processes of sudo...
    I should have try first to use a script to check if they were coming from my app with the QProcess.

    Any idea why I had this behaviour?



  • I think all those sudo ghosts were coming from the my Qt App. That's why I was never seeing them finishing and that my HMI was freezing when I was using QProcess::execute.
    Any idea why? I don't manage to reproduce it now...

    Edit: Well I guess I won't use QProcess::execute just in case it would happen again and stick with the slots... worst case nothing happen and I've an orphan process...



  • @mbruel
    Like you, I'm a little lost.

    When processes exit but their parent has not wait()ed on them, that's when you get zombie processes (e.g. https://vitux.com/how-to-kill-zombie-processes-in-ubuntu-18-04/). Doubtless connected to failure of QProcess::finished being called.

    If anything, I'd have they would come from the QProcess instance methods, QProcess::execute ought to collect them. But who knows. And debugging/abandoning causes issues.

    and Qt should kill all his processes when the app is killed.

    Can't recall if that's right. More like, they continue to run/become zombies/orphans if parent exits without waiting?

    I truly don't know any more, and I think you're saying situation has changed now anyway. They were just some suggestions :)



  • @JonB said in QProcess signals not received in slots except in Debug with breakpoints?:

    Can't recall if that's right. More like, they continue to run/become zombies/orphans if parent exits without waiting?
    Hum it might have happened when I was using QProcess::execute, the HMI was frozen and after a while I was getting a popup from QtCreator I guess to ask if I'd like to wait or kill it. Yeah I imagine that as the process was not waited it was becoming orphan or zombie (that's the word I was looking for, cheers ;))

    But how/why did the first sudo call became orphan and didn't finish with QProcess::execute??? that's kind of a mystery.. The joy of computer science!!! haha Then I guess it didn't like to have an zombie sudo and was generating more...

    Anyway, got it working now... I'll won't use a the blocking call. I'll see if my users report that the shutdown is not working and/or if the app creates zombies lol

    Thanks for your help anyway, was quite useful, I didn't thought about using ps to check my processes ;)



  • @mbruel
    I don't know the answer, but the static QProcess::execute() was added into Qt quite recently. It's a simple wrapper for what many people want to just do: run a process, wait for an exit code, done. For more control you are better using a QProcess instance.


Log in to reply