Stay in directory with popen - c++

I want to make some C++ program and I'm using function popen here to send commands to command line in Unix. It works fine, but when I call cd directory, the directory doesn't change. I thing that it's same when I try to run cd directory in some script, after finishing script directory path change back. So, scripts I must run like . ./script.sh not ./sript.sh, but how to do that with popen function? I have tried to add ". " before first argument of popen, but running ". ls" makes error.
Code:
cout << "# Command from " << session->target().full() << ": " << message.body() << endl;
//cout << "Prisla zprava" << endl;
//m_session->send( "Hello World", "No Subject" );
//system( message.body().c_str() );
//if ( message.body() == "" )
FILE* outp;
char buffer[100];
string outps = "";
outp = popen( message.body().c_str(), "r" );
while ( !feof(outp) )
{
fgets( buffer, 100, outp );
outps = outps + buffer;
}
pclose(outp);
cout << "& Output from command: " << outps << endl;
m_session->send( outps.c_str(), "Output" );
In message.body(); is string which I want to run (I'm receiving this from XMPP). When the string is for example "ls", it returns string with list of files in actual directory. But when the message is "cd directory", nothing happens, like trying to change directory in scripts.

Typically, the way the popen() command executes the command is via the shell. So, it opens a pipe, and forks. The child does some plumbing (connecting the pipe to the standard input or standard output - depending on the flag) and then executes
execl("/bin/sh", "/bin/sh", "-c", "what you said", (char *)0);
So, how it all behaves is going to depend on your key environment variables - notably PATH.
If you want to execute a script in the current directory, then one of these options:
outp = popen("./script.sh", "r");
outp = popen("sh -x ./script.sh", "r");
outp = popen("sh -c './script.sh arg1 arg2'", "r");
If you want to execute the 'ls' command:
outp = popen("/bin/ls /the/directory", "r");
And if you want to change directory before running something:
outp = popen("cd /somewhere/else; ./script", "r");
And so on...
If you want to change the directory of the program that is using popen(), then you need to use the 'chdir()' system call (or possibly fchdir()). If you think you might want to get back to where you started, use:
int fd = open(".", O_RDONLY);
chdir("/some/where/else");
...do stuff in new directory
fchdir(fd);
(Clearly, you need some error checking in that lot.)

It seems you have a bit of code that you do not understand. You are reading from outp, a pipe. Naming an input pipe outp is rather confusing. You then take the string you've read and pass it to m_session->send().
Nowhere in this whole process are you interacting with the Unix command line. In particular, popen() is not.

Related

Is there a way to pipe IO?

I'm trying to pipe 2 IO object, i come from nodejs and we can do something like that:
const child_process = require('child_process')
const shell = child_process.spawn('/bin/sh')
shell.stdout.pipe(process.stdout)
shell.stdin.write('pwd\n')
shell.stdin.write('ls\n')
/* write all command i want */
and im looking for do the same thing in crystal
i know for the current example we can write
shell = Process.new("/bin/sh", input: Process::Redirect::Pipe, output: STDOUT, error: STDOUT)
shell.input << "ls\n"
shell.input << "pwd\n"
# all commands i want
but for some reason passing TCPSocket to Process.new input/output/error dont work very well (see here too if you have time Process and TCPSocket not close properly in crystal)
so im looking for an alternative way who will look like:
shell = Process.new("/bin/sh", input: Process::Redirect::Pipe, output: Process::Redirect::Pipe, Process::Redirect::Pipe)
shell.output.pipe(STDOUT) # not the crystal pipe but like the nodejs pipe
shell.input << "ls\n"
shell.input << "pwd\n"
# all commands i want
You can use IO.copy inside a coroutine:
shell = Process.new("/bin/sh", input: :pipe, output: :pipe, error: :pipe)
spawn { IO.copy shell.output, STDOUT }
spawn { IO.copy shell.error, STDERR }
shell.input << "ls /\n"
shell.input << "pwd\n"
shell.wait
https://carc.in/#/r/75z4

tar command in execl using busybox. Error: no such file or directory

I have a linux based device that runs c++ code using QT framework. Using QProcess is not an option, since we don't have the QT compiled to support it.
I can't create a tar.gz archive using execl().
It returns -1(fail) and error is "No such file or directory"
Code sample:
std::string applicationPathWithName = "/bin/busybox";
QString dataDirectory("/opt/appl/data/");
QString archiveName = QString("AswLogs.tar.gz");
char* applName;
applName = new char [applicationPathWithName.size() + 1];
strcpy(applName, applicationPathWithName.c_str());
itsFlmFileManagerPtr->writeInFile(eFlmFileTypes_LogFile, data); //This creates logs.txt successfully
pid_t pid = fork();
QString command = QString("tar -czvf %1%2 %3logs.txt").arg(dataDirectory).arg(archiveName).arg(dataDirectory);
if(0 == pid)
{
INFO("Pid is 0");
int execStatus = 0;
execStatus = execl(applName, applName, command.toStdString().c_str(), (char*)NULL);
INFO("Execl is done, execStatus= " << execStatus);
std::string errorStr = strerror(errno);
INFO("Error: " << errorStr);
_exit(EXIT_FAILURE);
}
else if (pid < 0)
{
INFO("Failed to fork");
}
else
{
INFO("pid=" << pid);
int status;
if(wait(&status) == -1)
{
INFO("Wait child error");
}
INFO("Resume from fork");
}
Output:
pid=877
Pid is 0
Execl is done, execStatus= -1
Error: No such file or directory
Resume from fork
Permissions:
logs.txt 666 |
busybox 755
How can I get more error details or what is wrong here?
Edit:
So, after a while, I tried to do just the .tar archive and it worked.
Then I tried just to do the .gz compression and it also worked.
Solution:
So, at least in my case, the solution was to do the tar.gz in two steps(Two processes required):
execl("/bin/busybox", "/bin/busybox", "tar", "-cvf", "/opt/appl/data/logs.tar", "/opt/appl/data/logs.txt", (char*) NULL);
execl("/bin/busybox", "/bin/busybox", "gzip", "/opt/appl/data/logs.tar", (char*) NULL);
I don't know what platform or compiler this is, but it generally isn't possible to pass whole command lines to execl(). If I understanding correctly, you are running something like this:
execl ("/bin/busybox", "/bin/busybox", "tar -czvf blah blah", null);
but in general you need
execl ("/bin/busybox", "/bin/busybox", "tar", "-czvf", "blah", "blah", null);
That is, you need to parse the command line down to its individual arguments. That should be easy enough in the case you described, since you already know what the individual arguments are.
I think the problem is that /bin/busybox starts, but chokes when it tries to interpret "tar -czvf blah blah" as the name of an applet to run.
Incidentally -- and probably not related -- busybox "tar" won't handle gzip compression internally by default, unless you have enabled this feature at build time.

Executing the shell command in QProcess.Piping the input

I am trying to pipe the commands and execute it, but I am not able to figure how to pipe it.
I am trying to copy multiple files at once using the shell command
for %I in (source) do copy %I (destination)
QString files = "for %I in (source) do copy %I (destination)"
QProcess copy ;
copy.start(files);
I have to implement the piping to do that.
for Eg.
QProcess sh;
sh.start("sh", QStringList() << "-c" << "ifconfig | grep inet");
sh.waitForFinished();
QByteArray output = sh.readAll();
sh.close();
How can I implement piping for my copy process?
Try this example:
QProcess sh;
sh.start( "sh", { "-c", "ifconfig | grep inet" } );
if ( !sh.waitForFinished( -1 ) )
{
qDebug() << "Error:" << sh.readAllStandardError();
return -1;
}
const auto output = sh.readAllStandardOutput();
// ...
waitForFinished() should be called in blocking mode and it must be checked if it was successful or not.

c++ popen, fgetc, fscanf, impossible to read a line, non error

I can open a process
(i perform a shell script whose output is
1443772262175; URL filter
1443772267339; URL blocked ...) with popen, no problem
but after impossible to read it
I try fscanf, fgets(buff_out, sizeof(buff_out)-1, pf)
int lineno = 0;
if ((pf = popen(files_to_open, "r")) != NULL)
while (!feof(pf))
{
fscanf(pf, "%s %s", buffer, afficher);
message << buffer << " " << afficher ;
}
or while (fgets(buff_out, sizeof(buff_out)-1, pf) != NULL) {
}
i try with a program test it works fine but with a program more complexe (parallel) that i can't debug (the programm complexe) it fails no error just nothing to scan or read
Should i abandon popen?
Thanks for help

shell command inside the linux daemon

I have written the daemon in C/C++ in linux.
Now I want to get the out put of ls -l (list directory) command inside daemon and write output of command to the file.
I know how to write to the file from my daemon, but,
I don't know how to execute ls -l command and get the output in buffer.
Here is the code...
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failures here */
ofs << "set sid : fail";
ofs.close();
exit(EXIT_FAILURE);
}
ofs << "\nchdir :" << chdir(filePath) << "\n";
/* Change the current working directory */
if ((chdir(filePath)) < 0) {
/* Log any failures here */
ofs << "chdir : fail";
ofs.close();
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
while(1){
//here I want to execute the ls -l and get output of the command
}
You can use popen that executes a shell command and return the output as a pipe:
#include <stdio.h>
FILE* pipe = popen("ls -l", "r");
if (!pipe) return "ERROR";
You can also use system to execute any shell command:
#include <stdlib.h>
int system(const char *command);
To get the output of ls -l, forward it to a file ls -l >> myls.log than read that file.
system("ls -l >> myls.log");