Unsolved 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?
CheersEdit: 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?
-
@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 asudo
, other than when it does come to prompting for password, which is a different matter. (Hint: That has been asked many times before, consider usingsudo -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 inonShutdownProcFinished()
, 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 useQProcess::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 StartedSo 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 checkisatty(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 thesudo
line and logs next line.
Just for the record, other people (though not me) have run
sudo
stuff fromQProcess
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 ofQProcess::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 thestatic 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 aQProcess
instance.