Qt5 QProcess handling arguments with "special" characters - c++

I'm maintaining a Qt5 program that uses QProcess to execute ncftpget on Linux.
The command that I'm attempting to execute is:
ncftpget -E -Z -u username -p 'a#$b1379' 1.1.1.1 /x/y/temp/ update/file
Which simply transfers a file from a server into a local directory. If I execute the above command on the bash command line it works properly.
However, when my Qt5 application executes the same command using QProcess the ftp server replies saying the username/password is not correct. When the password doesn't contain any "special" characters it works properly.
As mentioned by S.M. QProcess isn't executing the command in a shell. So I'm assuming fork() and some version of the exec() call is made. How do I make QProcess do the correct thing here?
I'm assuming that the problem has do to with the special character(s) in the password. When I build my QProcess argument list I specifically make sure the password is surrounded by ' so that the special characters are escaped.
Code snippet:
processFTP.setParent(this->parent());
processFTP.setProcessChannelMode(QProcess::MergedChannels);
// ncftpArgs is a QStringList
// snippet doesn't show complete argument list
ncftpArgs->push_back("-Z");
ncftpArgs->push_back("-u");//username
ncftpArgs->push_back(QString((*phashSettings)[FTPUSERNAME]));
ncftpArgs->push_back("-p");//pw
// password can have special characters when shell executes command.
QString password((*phashSettings)[FTPPASSWORD]);
password.prepend("'");
password.append("'");
ncftpArgs->push_back(password);
ncftpArgs->push_back(QString((*phashSettings)[FTPSERVERIP]));
ncftpArgs->push_back(QString((*phashSettings)[FTPTEMPDIR]));
//beging the ncftpget session
QString sExe = "ncftpget"; //process name
// processFTP is a QProcess object
processFTP.start(sExe, (*ncftpArgs));
bSuccess = processFTP.waitForStarted();
// .... more code to monitor process etc...
The code logs the command and arguments that will passed to processFTP and everything looks correct.
How do I properly set up the arguments and start the QProcess object so that the password argument containing the special characters are properly passed to the executable ncftpget?
And/or how do I go about debugging the problem?

When I build my QProcess argument list I specifically make sure the password is surrounded by ' so that the special characters are escaped.
Do not append or prepend the password with ', remove these lines
password.prepend("'");
password.append("'");
QProcess::start is responsible for proper way of program arguments passing. Besides bash interpreter is not run by this call in your code.

Related

What is the correct way to execute a CMD command with QProcess to get the output as QString?

I'm new using QT, I need to execute a CMD command on windows and get the output of this command as a string to later process and get specific data. The following code works well (it seems to work well). The only problem is that I get the following warning: "start is deprecated", I think this warning message is because the start method needs an arguments list as parameter.
QString command = "tasklist /FI \"IMAGENAME eq notepad.exe\"";
QProcess *executeCommand = new QProcess();
executeCommand->start(command);
executeCommand->waitForFinished(-1);
QString output = executeCommand->readAllStandardOutput();
executeCommand->terminate();
qDebug() << commandOutput;
how can I remove this warning message?
Also I found in the web that I can use system() to execute a CMD command, but I'm not able to catch the output as string.
Another question: which of the above options is better to achieve what I'm trying to do, the QProcess or System (if is possible to get the output as QString)?
Thanks in advance!
There is an answer read QProcess output to string recommending static method QProcess::readAllStandardOutput() returning QByteArray. Then just instantiate QString from QByteArray implicitly.
If you are working on Qt application, it is better to stay inside Qt API to keep the code more or less portable.

QProcess Backup Database on QT C++

I want to backup my database with qprocess in QT program, the code is as follows, but 0kb occurs when backing up and when I look at the error Qprocess: Destroyed while process("mysqldump.exe") is still runnuing.
QProcess dump(this);
QStringlist args;
QString path="C:/Users/mahmut/Desktop/dbbackupfile/deneme.sql";
args<<"-uroot"<<"-proot"<<"kopuz"<<">";
dump.setStandardOutputFile(path);
dump.start("mysqldump.exe",args);
if(!dump.waitForStarted(1000))
{
qDebug()<<dump.errorString();
}
Can you help to me? ı do not understand this error and okb back up file.
Your program terminates before process finished, you need to either use static bool QProcess::startDetached(program, arguments, workingDirectory) or add dump.waitForFinished(); to the end.
Also, you dont need to add ">" to arguments. You already redirected output with dump.setStandardOutputFile(path), ">" does not work with process as it requires shell to execute command, QProcess does not use shell it just runs one process not shell expression.

How can I find why system can not run my application?

I have a c++ program that run a command and pass some arguments to it. The code is as follow:
int RunApplication(fs::path applicationPathName,std::string arguments)
{
std::string applicationShortPath=GetShortFileName(applicationPathName);
std::string cmd="\""+applicationShortPath +"\" "+ arguments+" >>log.txt 2>&1 \"";
std::cout<<cmd<<std::endl;
int result=std::system(cmd.c_str());
return result;
}
When I run system command, the cmd window appears shortly and then closes, but the result is 1 and the cmd was not run (the command should generate output which is not generated).
To check that the cmd is correct, I stopped the application just before system line and copy/ paste cmd content to a cmd window and it worked.
I am wondering how can I find why application is not run in system()?
the cmd has this value just before running it:
"D:/DEVELO~3/x64/Debug/enfuse.exe" -w --hard-mask --exposure-weight=1 --saturation-weight=0.328 --contrast-weight=0.164 -o "C:/Users/m/AppData/Local/Temp/1.tif" "C:/Users/m/AppData/Local/Temp/1.jpg" "C:/Users/m/AppData/Local/Temp/2.jpg" >>log.txt 2>&1 "
How can I find why it is not working?
Is there any way that I set the system so it doesn't close cmd window so I can inspect it?
is there any better way to run a command on OS?
Does Boost has any solution for this?
Edit
After running it with cmd /k, I get this error message:
The input line is too long.
How can I fix it other than reducing cmd line?
There are two different things here: if you have to start a suprocess, "system" is not the best way of doing it (better to use the proper API, like CreateProcess, or a multiplatform wrapper, but avoid to go through the command interpreter, to avoid to open to potential malware injection).
But in this case system() is probably the right way to go since you in fact need the command interpreter (you cannot manage things like >>log.txt 2>&1 with only a process creation.)
The problem looks like a failure in the called program: may be the path is not correct or some of the files it has to work with are not existent or accessible with appropriate-permission and so on.
One of the firt thing to do: open a command prompt and paste the string you posted, in there. Does it run? Does it say something about any error?
Another thing to check is how escape sequence are used in C++ literals: to get a '\', you need '\\' since the first is the escape for the second (like \n, or \t etc.). Although it seems not the case, here, it is one of the most common mistakes.
Use cmd /k to keep the terminal: http://ss64.com/nt/cmd.html
Or just spawn cmd.exe instead and inspect the environment, permissions, etc. You can manually paste that command to see whether it would work from that shell. If it does, you know that paths, permssions and environment are ok, so you have some other issue on your hands (argument escaping, character encoding issues)
Check here How to execute a command and get output of command within C++ using POSIX?
Boost.Process is not official yet http://www.highscore.de/boost/process/

Problems passing source command to bash from c++ application

I am developing an application for work that allows the users to quickly set environment variables on a terminal basis. By setting the path in each terminal we ensure files with the same name in different directories aren't causing application testing to be problematic. I am Using Qt to build the program which is c++ based and all the datatypes are foundationally the same.
I am using the following code to invoke commands in the terminal from which the application launches from using system(). I can run commands into the bash just fine with code; however, I run into a problem when I attempt to use a command with arguments. This is probably why source doesn't seem to work right as the source command is followed by the filename. It would appear that I drop the argument appended after the bash command.
My Code:
void assignTerminalToPath(QString path)
{
QString data = "";
QString currentUsersHomeDirectory = QDir::homePath();
QString tmpScriptLocation = currentUsersHomeDirectory;
QByteArray ba;
tmpScriptLocation += "/.tmpSourceFile";
QFile tmpSourceFile(tmpScriptLocation);
if(tmpSourceFile.open(QFile::WriteOnly | QFile::Truncate))
{
QTextStream output(&tmpSourceFile);
data.append("export PATH=.:");
data.append(path);
data.append(":$PATH");
output << QString("#!/bin/bash\n");
output << data;
tmpSourceFile.close();
}
data.clear();
data.append("/bin/bash -c source ");
data.append(tmpScriptLocation);
ba = data.toLatin1();
const char *cStr = ba.data();
system(cStr);
}
Perhaps I'm not referencing bash correctly and I need something outside of -c?
Reference Execute shell/bash command using C/C++
Thanks for any help in advance!
source is not a program that you can call, it is embedded bash command. It is designed to be processed by bash without invoking another copy of bash, such that environment variables can be changed in current bash copy.
However, you cannot call source as part of system(). And even if you did succeed at that, its effects to change environment variables would be completely lost for caller app once system() has returned.
Try a command to envelop with parameters in double quotes ("command - arg1 - to arg2") to transfer in the function system().
used:
char *com = "\"command -arg1 -arg2\"";
system(com);

How to get forkpty/execvp() to properly handle redirection and other bash-isms?

I've got a GUI C++ program that takes a shell command from the user, calls forkpty() and execvp() to execute that command in a child process, while the parent (GUI) process reads the child process's stdout/stderr output and displays it in the GUI.
This all works nicely (under Linux and MacOS/X). For example, if the user enters "ls -l /foo", the GUI will display the contents of the /foo folder.
However, bash niceties like output redirection aren't handled. For example, if the user enters "echo bar > /foo/bar.txt", the child process will output the text "bar > /foo/bar.txt", instead of writing the text "bar" to the file "/foo/bar.txt".
Presumably this is because execvp() is running the executable command "echo" directly, instead of running /bin/bash and handing it the user's command to massage/preprocess.
My question is, what is the correct child process invocation to use, in order to make the system behave exactly as if the user had typed in his string at the bash prompt? I tried wrapping the user's command with a /bin/bash invocation, like this: /bin/bash -c the_string_the_user_entered, but that didn't seem to work. Any hints?
ps Just calling system() isn't a good option, since it would cause my GUI to block until the child process exits, and some child processes may not exit for a long time (if ever!)
If you want the shell to do the I/O redirection, you need to invoke the shell so it does the I/O redirection.
char *args[4];
args[0] = "bash";
args[1] = "-c";
args[2] = ...string containing command line with I/O redirection...;
args[4] = 0;
execv("/bin/bash", args);
Note the change from execvp() to execv(); you know where the shell is - at least, I gave it an absolute path - so the path-search is not relevant any more.