shell command inside the linux daemon - c++

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");

Related

how to implement pipe command from linux shell in c++?

i am working in a project to implement a mini linux shell ,
i want to implement a pipe command where it basically works like this :
command1 | command2: using the pipe character “|” will produce a pipe, redirects command1 stdout to its write channel and command2 stdin to its read channel.
or:
command1 |& command2: using the pipe character “|&” will produce a pipe, redirects command1 stderr to the pipe’s write channel and command2 stdin to the pipe’s read channel.
now command 1 can be either an external command from linux that i run using execv or a built in command that i wrote , and command2 is always an external command
my code is not working correctly and i don't know where is the problem exactly , because i implemented many commands and they all worked perfect for example (cp, redirection ... ) , so the base is good in my code , but the pipe is just wrong ! for example if the command is : showpid | ./parser.exe 1
where parser.exe is a giving file that does parsing on the command , for example here if showpid prints : shell process pid is 12311 , then calling this command showpid | ./parser.exe 1 the output should be "shell" , but in my code the output is shell process pid is 12311
this is my pipe command implementation :
this is the class of the pipe command :
class PipeCommand : public Command {
private:
int pipeNum;
int split;
string cmd1;
string cmd2;
public:
PipeCommand(const char* cmd_line);
virtual ~PipeCommand() {}
void execute() override;
};
// the pipe constructor , here i want to extract each command from the right and left side of the pipe from the cmd_line , which is the command line that i get
// fro example : " showpid | grep 1 "
PipeCommand::PipeCommand(const char* cmd_line):Command(cmd_line) {
pipeNum = -1;
isBackground = _isBackgroundComamnd(cmd_line);
string cmd1 = "", cmd2 = "";
int split = -1;
for (int i = 0; i < this->num_args; i++) {
if (strcmp(args[i], "|") == 0) {
split = i;
pipeNum = 1;
break;
}
if (strcmp(args[i], "|&") == 0) {
split = i;
pipeNum = 2;
break;
}
}
for (int i = 0; i < split; i++) {
cmd1 = cmd1 + args[i] + " ";
}
for (int i = split + 1; i < num_args; i++) {
cmd2 = cmd2 + args[i] + " ";
}
// the implementation of the pipe command
void PipeCommand::execute() {
int pipeFd[2];
int pid;
pipe(pipeFd);
pid = fork();
if (pid == 0) { // child process .
close(pipeFd[1]);
dup2(pipeFd[1], pipeNum);
if (isBuiltInCMD(args[0])) { // if the command is built in which means i wrote it i run it like this ( this works fine i checked it)
Command *newCmd = CreateBuiltInCommand(const_cast<char *>(cmd1.c_str()));
newCmd->execute();
exit(0);
} else { // if the command is external than use execv
const char **argv = new const char *[4];
argv[0] = "/bin/bash";
argv[1] = "-c";
argv[2] = cmd1.c_str();
argv[3] = nullptr;
execv(argv[0], const_cast<char **>(argv));
perror("execvp failed");
}
} else { // the parent process , basically runs the command2 , which it can be only an external command
pid = fork(); // we fork again in the parent process
if (pid == 0) { // the child process executes the secomd command using execv
dup2(pipeFd[0], STDIN_FILENO);
close(pipeFd[0]);
dup2(pipeFd[0], pipeNum);
// execute
const char **argv = new const char *[4];
argv[0] = "/bin/bash";
argv[1] = "-c";
argv[2] = cmd2.c_str();
argv[3] = nullptr;
execv(argv[0], const_cast<char **>(argv));
perror("execvp failed");
} else { // the parent process waits
waitpid(pid,NULL,0);
close(pipeFd[1]);
close(pipeFd[0]);
}
}
}
I think you should look at the order that you are closing / duping file descriptors. Specifically:
The first command needs to use existing stdin (fd 0). Don't close it.
But you should close existing stdout (fd 1) and THEN do the fd dup so it becomes 1.
The second command does it the other way.
I would test with a MUCH simpler example. Get the piping thing to work and THEN do the exec thing.
This is edited information added later.
In a C/C++ world, you have 3 standard files when the program starts:
FD 0 is stdin -- Used for input
FD 1 is stdout -- Used for normal output
FD 2 is stderr -- Used for error output
When you do this:
grep foo < file.txt | grep bar
What the shell does is:
-Does the pipe call to get the input and output files
-On the first grep for foo, close fd 0 (stdin) and open file.txt for input. It will land on 0, and thus is stdin to the grep command.
-Close stdout and assign it to the out part of the pipe
On the second grep:
-Close 1 (stdin)
-And move the pipe input portion to 1 so stdin is set.
Thus, in the end:
part 1 fd 0 (stdin) is the file
part 1 fd 1 (stdout) is the output portion of the pipe
part 2 fd 0 (stdin) is the input portion of the pipe

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.

system / pipe call changes special characters in command passed for execution

I wanted to use system / pipe command to execute command which has special characters. below is the sample code.
After executing the command through system / pipe, it changes the command by changing special characters.
I am surprised to see that system command is changing the text passed as command.
run(char *cmd)
{
FILE *in;
extern FILE *popen();
char buff[2048]= {0,};
if(!(in = popen(cmd, "r")))
{
exit(1);
}
while(fgets(buff, sizeof(buff), in)!=NULL)
{
printf("%s", buff);
}
pclose(in);
}
main()
{
char cmd[2048]={0,};
sprintf(cmd,"echo \"'http://1.2.3.4/files-spaces-specialchars-
ascii/%23#%23##!#!#!#%23%23$$$$$$$ASA(()
(!FreemakeAudioConverterSetup.exe'\" >>/tmp/logger 2>&1");
printf("this is CMD:[%s]\n",cmd);
system("echo "" > /tmp/logger"); /* to clear file containt */
system(cmd);
run(cmd);
}
OUTPUT
[terminal]$ ./a.out
this is CMD:[echo "'http://1.2.3.4/files-spaces-specialchars-ascii/%23#%23##!#!#!#%23$$$$$$$ASA(()(!FreemakeAudioConverterSetup.exe'" >>/tmp/logger 2>&1]
[terminal]$ cat /tmp/logger
'http://1.2.3.4/files-spaces-specialchars-ascii/%23#%23##!#!#!#%23538853885388(()(!FreemakeAudioConverterSetup.exe'
'http://1.2.3.4/files-spaces-specialchars-ascii/%23#%23##!#!#!#%23538953895389(()(!FreemakeAudioConverterSetup.exe'
[terminal]$
As shown above the original command URL is getting changed after executing through system / pipe command.
Any inputs from developers?
correcting the code, so it cleanly compiles results in:
Note: I also added a call to perror() so if the call to popen() fails the user is properly notified of what happened.
#include <stdio.h>
#include <stdlib.h>
void run(char *cmd)
{
FILE *in;
extern FILE *popen();
char buff[2048]= {'\0'};
if(!(in = popen(cmd, "r")))
{
perror( "popen for read failed" );
exit(1);
}
while(fgets(buff, sizeof(buff), in)!=NULL)
{
printf("%s", buff);
}
pclose(in);
}
int main( void )
{
char cmd[2048]={'\0'};
sprintf(cmd, "%s", "echo \"'http://1.2.3.4/files-spaces-specialchars-"
"ascii/%23#%23##!#!#!#%23%23$$$$$$$ASA(()"
"(!FreemakeAudioConverterSetup.exe'\" >>/tmp/logger 2>&1");
printf("this is CMD:[%s]\n",cmd);
system("echo "" > /tmp/logger"); /* to clear file containt */
system(cmd);
run(cmd);
}
then running that code results in:
this is CMD:[echo "'http://1.2.3.4/files-spaces-specialchars-ascii/%23#%23##!#!#!#%23%23$$$$$$$ASA(()(!FreemakeAudioConverterSetup.exe'" >>/tmp/logger 2>&1]
and cat /tmp/logger results in:
cat /tmp/logger
'http://1.2.3.4/files-spaces-specialchars-ascii/%23#%23##!#!#!#%23%23193951939519395(()(!FreemakeAudioConverterSetup.exe'
'http://1.2.3.4/files-spaces-specialchars-ascii/%23#%23##!#!#!#%23%23193961939619396(()(!FreemakeAudioConverterSetup.exe'

c shell input redirection stays open

I'm using the following code part to perform input redirection for my custom C++ shell.
While the output redirection similar to this works well, the child process for the input redirection stays open and doesn't return, like it keeps waiting for new input.
What is the best way to 'ask' or 'force' a child process like this to return immediately after reading input?
Code for input redirection
int in_file = open(in, O_CREAT | O_RDONLY , S_IREAD | S_IWRITE);
pid_t pid = fork();
if (!pid) {
dup2(in_file, STDIN_FILENO);
if (execvp(argv[0], argv) < 0) {
cerr << "*** ERROR: exec failed: "<< argv[0] << endl;
exit(EXIT_FAILURE);
}
}
close(in_file);
Code for output redirection
out_file = open(out, O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU);
pid_t pid = fork();
if (!pid) {
dup2(out_file, STDOUT_FILENO);
if (execvp(argv[0], argv) < 0) {
cerr << "*** ERROR: exec failed: "<< argv[0] << endl;
exit(EXIT_FAILURE);
}
}
close(out_file);
I used the following commands to test:
ps aux > out.txt
grep root < out.txt
The first command returns to shell after succesfully writing to out.txt. The second command reads succesfully from out.txt, but doesn't return or stop.
The child still has the in_file open. You must close it before exec:
dup2(in_file, STDIN_FILENO);
close(in_file);
(error checking omitted for brevity). Because the child still has an open file descriptor, it never sees the file as being closed, so it blocks on a read waiting for someone to write more data. The child process does not realize that it is the one holding the file descriptor open. Another option is to set the 'close-on-exec' flag for the file descriptor, so that it is closed automatically on exec. (Search for FD_CLOEXEC)
First, check the arguments to execvp(). If this code is part of main(int argc, char* argv[]), then argv[0] is your own program, not grep. This means that your program is recursively re-executing itself for ever.
Then, make sure that there is no error when opening in with:
if (in_file < 0) { perror(in); ... }
If in_file is an invalid descriptor, dup2() will fail and grep will run reading from the terminal, hence, not terminating. BTW, using O_CREAT | O_RDONLY looks fishy. Why read from a file that does not previously exist?

Stay in directory with popen

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.