Programming language to choose to execute bash commands - c++

For my school project I must make a C++ program. The program has to use already installed applications on the Linux OS. To get familiar with managing other processes from within a C++ application I want to make a program that sets a wlan interface to monitor mode. The code that I have written so far is pretty long and doesn't seems efficient. Are there any ways to make my code more compact and efficient? At the end of the program I want to execute iwconfig to check if the wlan really is in monitor mode. What is the best way to do that?
#include <unistd.h>
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main(int argc, char *argv[])
{
string wlan = "wlan1";
pid_t ifconfigDown;
ifconfigDown = fork();
if(ifconfigDown == 0)//ifconfig
{
execl("/sbin/ifconfig", "ifconfig", wlan.c_str(), "down",(char*)0 );
}
else //parent
{
usleep(500000);
pid_t iwconfigMode;
iwconfigMode = fork();
if(iwconfigMode == 0)//ifconfig
{
execl("/sbin/iwconfig","iwconfig",wlan.c_str(),"mode","monitor",(char*)0 );
}
else//parent
{
usleep(500000);
pid_t ifconfigUp;
ifconfigUp = fork();
if(ifconfigUp == 0)//ifconfig
{
execl("/sbin/ifconfig", "ifconfig", wlan.c_str(), "up", (char*)0 );
}
else//parent
{
usleep(500000);
pid_t iwconfig;
iwconfig = fork();
if(iwconfig == 0)//iwconfig
{
execl("/sbin/iwconfig", "iwconfig", (char*)0 );
//check if wlan1 is in monitor mode
}
}
}
waitpid(-1, NULL, 0);
return 0;
}
}
string rPopenEnd (string cmd)
{
FILE *fp = popen(cmd.c_str(), "r");
if (fp == NULL)
{
return "ERROR";
}
else
{
uint16_t line_size = 20;
char line[line_size];
string result;
while (fgets(line, line_size, fp))
result += line;
wait(NULL);
return result;
}
}
int main(int argc, char *argv[])
{
rPopenEnd("iwconfig");
}

Related

Restart a program of child process in C++ linux

I have learned recently about processes and threads, and I am building a project where I need to run a program inside a child process.
The problem is that after the program in the child process exits I don't know how to restart and run the program again.
Here's a test which demonstrates what I am trying to do.
main.cpp file:
#include <iostream>
#include <unistd.h>
#include <sys/ptrace.h>
#include "handler.h"
int main(int argc, char** argv)
{
if (argc < 2)
{
std::cerr << "usage: program name" << std::endl;
return 1;
}
pid_t pid = fork();
if (pid == 0)
{
if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) < 0)
std::cerr << "ptrace error" << std::endl;
else
execl(argv[1], argv[1], nullptr);
}
else if (pid >= 1)
{
Handler handler(std::string(argv[1]), pid);
handler.run();
}
}
handler.h file:
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <sys/ptrace.h>
#include <sys/wait.h>
class Handler
{
private:
pid_t m_pid;
std::string m_name;
public:
Handler(const std::string& name, pid_t pid) : m_name(name), m_pid(pid)
{
}
void run()
{
std::string line;
while (true)
{
std::cout << "(command) ";
std::getline(std::cin, line);
if (line == "run")
{
int options = 0, status = 0;
ptrace(PTRACE_CONT, m_pid, nullptr, nullptr);
int pid = waitpid(m_pid, &status, options);
// trying to restart again the program
if (pid < 0)
{
m_pid = fork();
execl(m_name.c_str(), m_name.c_str(), nullptr);
}
}
}
}
};

Open TTY to use with execlp and dup

I am trying to create a minimal code to use pipe/fork/execlp.
So far so good, I am using execlp with bash -c, so if I do.
echo asd |./a.out cat
> asd
So it is working as expected.
But if I try to use anything that needs a TTY, it does not work.
Like ./a.out vim, I get "Vim: Warning: Input is not from a terminal"
And the vim that was open does not works as expected.
I tried to find on the internet an example on how to open a TTY, the only one that I found was:
http://www.danlj.org/lad/src/minopen.c
My Code, so far is:
#include <iostream>
#include <cstdio>
#include <string.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
typedef struct pCon{
int fout[2];
int fin[2];
int fd[2];
int pid1, pid2;
} connectionManager;
std::string command = "";
/*
* Implementation
*/
void childFork(connectionManager *cm);
int main(int argc, char *argv[]) {
int size;
if(argc < 2) exit(1);
else command = argv[1];
connectionManager *cm = new connectionManager;
pipe(cm->fd);
if((cm->pid1 = fork()) == -1)exit(1);
if (cm->pid1 == 0)
{
const unsigned int RCVBUFSIZE = 2000;
char echoString[RCVBUFSIZE];
while((size = read(fileno(stdin),echoString,RCVBUFSIZE)) > 0)
write(cm->fd[1], echoString, size);
close(cm->fd[1]);
}
else
childFork(cm);
return 0;
}
void childFork(connectionManager *cm){
char *buffer = new char[2000];
int size;
close(cm->fd[1]);
dup2(cm->fd[0], 0);
close(cm->fd[0]);
pipe(cm->fout);
if((cm->pid2 = fork()) == -1)exit(1);
if (cm->pid2 == 0)
{
close(cm->fout[0]);
int returnCode = execlp("bash", "bash", "-c", command.c_str(), NULL);
if(returnCode!=0)
std::cerr << "Error starting the bash program" << std::endl;
}
else
{
close(cm->fout[1]);
while((size = read(cm->fout[0], buffer, 2000 )) > 0 )
write(fileno(stdout), buffer, size);
}
}
I tried to keep the minimal necessary code to make it work.
Is there any way to implement TTY on this code, I know that does not seems to be such trivial task.
Can someone help me with that?
I also tried to open the tty and dup it, but no luck so far.
Try to use pseudo terminal. You can use opentty. For your purpose you can use forkpty which combines pty with fork. I've created a small example for you. About the same as your program, just it works. I've kept it simple, so I don't handle the terminal control characters.
#include <pty.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/select.h>
int main(int argc, char *argv[])
{
if (argc<1) return 1;
int master;
pid_t pid = forkpty(&master, NULL, NULL, NULL); // opentty + login_tty + fork
if (pid < 0) {
return 1; // fork with pseudo terminal failed
}
else if (pid == 0) { // child
char *args[] = { argv[1], argv[2], NULL }; // prg + 1 argument
execvp(argv[1], args); // run the program given in first param
}
else { // parent
struct termios tios;
tcgetattr(master, &tios);
tios.c_lflag &= ~(ECHO | ECHONL);
tcsetattr(master, TCSAFLUSH, &tios);
while(1) {
fd_set read_fd, write_fd, err_fd;
FD_ZERO(&read_fd);
FD_ZERO(&write_fd);
FD_ZERO(&err_fd);
FD_SET(master, &read_fd);
FD_SET(STDIN_FILENO, &read_fd);
select(master+1, &read_fd, &write_fd, &err_fd, NULL);
if (FD_ISSET(master, &read_fd))
{
char ch;
int c;
if (c=read(master, &ch, 1) != -1) // read from program
write(STDOUT_FILENO, &ch, c); // write to tty
else
break; // exit when end of communication channel with program
}
if (FD_ISSET(STDIN_FILENO, &read_fd))
{
char ch;
int c=read(STDIN_FILENO, &ch, 1); // read from tty
write(master, &ch, c); // write to program
}
}
}
return 0;
}
For compiling use -lutil .
While running a new tty device appears in /dev/pts .
vim accepts it as a terminal.

How does Objective C stdin/stdout piping work when called from C/C++?

This is a problem I've been working on all afternoon, I think I have reduced it to its central problem, which appears to be unexpected behavior when piping data to/from an Objective C command line application that is called from a C++ application.
When executed alone, the Objective C program works as expected. When the C++ Pipe (which is the "master" in this case, the C++ is calling the Objective C executable) is calling a C/C++ executable similar to the below Objective C code, everything also works as expected.
Furthermore, if the input code is removed from the Objective C, or if the C++ program orders Objective C to be piped to a file (so the command would be "./HelloWorld > dump.txt" instead of "./HelloWorld") everything performs as expected.
However, when the code as presented bellow is executed, the C++ hangs when attempting to read the Objective C's stdout, on the first try before any attempts to read stdin have been made by Objective C.
Objective C
#import <Foundation/Foundation.h>
void c_print(NSString* prnt)
{
printf("%s", [prnt cStringUsingEncoding:NSUTF8StringEncoding]);
}
void c_print_ln(NSString* prnt)
{
printf("%s\n", [prnt cStringUsingEncoding:NSUTF8StringEncoding]);
}
NSString* read_till(char c)
{
NSMutableString* ret = [[NSMutableString alloc] initWithString:#""];
char r = getchar();
while(r!=c && r!= '\0')
{
[ret appendFormat:#"%c",r];
r = getchar();
}
return ret;
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
c_print_ln(#"Hello, World!");
NSString* exmp = read_till('\n');
c_print_ln([[NSString alloc] initWithFormat:#"String I read: \"%#\"",exmp]);
}
return 0;
}
C++ (.h file)
#ifndef PIPE_H
#define PIPE_H
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <iostream>
#define PIPE_READ 0
#define PIPE_WRITE 1
class outsideExecutable
{
private:
char buf[1024];
bool is_good;
int infp, outfp;
public:
outsideExecutable(char* command);
~outsideExecutable();
bool isGood();
std::string readline();
void writeline(std::string source);
};
#endif
C++ (.cpp file)
#include "Pipe.h"
using namespace std;
int main()
{
cout<<"Testing Pipe"<<endl;
outsideExecutable* exe = new outsideExecutable((char*)"./HelloWorld");
exe->readline();
exe->writeline("reading example");
exe->readline();
delete exe;
}
static pid_t popen2(const char *command, int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0)
{
close(p_stdin[PIPE_WRITE]);
dup2(p_stdin[PIPE_READ], PIPE_READ);
close(p_stdout[PIPE_READ]);
dup2(p_stdout[PIPE_WRITE], PIPE_WRITE);
execl("/bin/sh", "sh", "-c", command, NULL);
perror("execl");
exit(1);
}
if (infp == NULL)
close(p_stdin[PIPE_WRITE]);
else
*infp = p_stdin[PIPE_WRITE];
if (outfp == NULL)
close(p_stdout[PIPE_READ]);
else
*outfp = p_stdout[PIPE_READ];
return pid;
}
outsideExecutable::outsideExecutable(char* command)
{
is_good = false;
if (popen2(command, &infp, &outfp) <= 0)
return;
is_good = true;
}
outsideExecutable::~outsideExecutable()
{
}
bool outsideExecutable::isGood()
{
return is_good;
}
std::string outsideExecutable::readline()
{
if(!is_good)
return "";
string ret = "";
char hld;
read(outfp, &hld, 1);
while(hld!='\n' && hld!='\0')
{
ret = ret + hld;
read(outfp, &hld, 1);
}
cout<<"We read:"<<ret<<endl;
return ret;
}
void outsideExecutable::writeline(std::string source)
{
if(!is_good)
return;
//Do nothing
cout<<"Sending command: "<<source<<endl;
source = source+"\n";
write(infp, source.c_str(), source.length());
}
#endif
Anyone have any ideas what could be wrong with this? I've got quite a bit of experience with C/C++, and it appears that the piping code from that side of things is working well. It really seems like this is an example of Objective C just not playing nice, I've never seen an example of piping failing like this.
rich (https://stackoverflow.com/users/1566221/rici) just provided the answer to this question in the comment above. Here is the updated Objective C code to fix it:
void c_print(NSString* prnt)
{
printf("%s", [prnt cStringUsingEncoding:NSUTF8StringEncoding]);
fflush(stdout);
}
void c_print_ln(NSString* prnt)
{
printf("%s\n", [prnt cStringUsingEncoding:NSUTF8StringEncoding]);
fflush(stdout);
}
Apparently stdout needs to be flushed in Objective C for piping to work properly.

program that reacts to inotify and prints the events

I am working in Ubuntu. I want to monitor a folder and print every event that pops up in the subfolders (print files).
I have the following code but it doesn't work. When executed, there is no println of the events.
In the second code I only see the events from the folder. The events from each subfolder do not pop up.
#include <string>
#include <iostream>
#include <stdio.h>
using namespace std;
std::string exec(char* cmd) {
FILE* pipe = popen(cmd, "r");
if (!pipe) return "ERROR";
char buffer[256];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 256, pipe) != NULL)
result += buffer;
}
pclose(pipe);
cout<<"result is: "<<result<<endl;
return result;
}
int main()
{
//while(1)
//{
string s=exec((char*)"inotifywait -rme create /home/folder/");
cout << s << endl;
//}
return 0;
}
This code only prints the events from the folder I'm monitoring. It doesn't print the events from each subfolder. I don't know how to improve it for my needs.
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <iostream>
void processNewFiles(int fd, int wd);
int main(int argc, char** argv)
{
const char* dirPath = "/home/folder/" ;//argv[1];
int fd = inotify_init();
int wd = inotify_add_watch(fd, dirPath, IN_CREATE);
if (wd)
{
processNewFiles(fd, wd);
inotify_rm_watch(fd, wd);
}
}
void processNewFiles(int fd, int wd)
{
bool done = false;
do
{
int qLen = 0;
ioctl(fd, FIONREAD, &qLen);
char* buf = new char[qLen];
int num = read(fd, buf, qLen);
if (num == qLen)
{
inotify_event* iev = reinterpret_cast<inotify_event*>(buf);
if (iev->wd == wd && iev->mask & IN_CREATE)
{
std::cout << "New file created: " << iev->name << std::endl;
}
}
delete [] buf;
} while (!done);
}
Your second solution does not work because inotify_add_watch is not working recursivly. You would have to add watches for subdirectories manually. As this might be annoying, it is also possible to use the utility inotifywait as you do in your first example.
Your first example is not working because you're reading from the pipe forever. If you kill the inotifywait process (e.g. if you're the only person on the machine and this is the only inotifywait process just using "killall inotifywait") you will get your output because you'll break out of the loop reading from the pipe. If you output something inside the loop, it will work, too.

C++ pipes between multiple children

I am working on a project and I got it mostly figured out except for one minor(big) problem. I can't seem to figure out how to create pipes between any number of children.
for example I am taking in command line arguments to determine how many children will be produced. The first child doesn't have input but has output and the last child outputs to STD output. I need to pass values into the first child and into each child after that in order. Here is what i got:
#include <errno.h>
#include <cstdio>
#include <iostream>
#include <sys/wait.h>
using namespace std;
int main(int argc, char *argv[]) {
pid_t childpid;
int x2ypipe[2];
pipe(x2ypipe);
if(x2ypipe==0) {
cout<<"ERROR:"<<errno<<endl;
}
int y2zpipe[2];
pipe(y2zpipe);
if(y2zpipe==0) {
cout<<"ERROR:"<<errno<<endl;
}
pid_t xchild =fork();
if(xchild==0) {
dup2(x2ypipe[1],STDOUT_FILENO);
close(x2ypipe[0]);
close(x2ypipe[1]);
int a=execl(argv[1],argv[1], (char*)NULL);
if(a==-1) {
perror("The following error occurred at A");
}
}
for(int i=2; i<(argc-1); i++) {
childpid =fork();
if(childpid==0) {
dup2(x2ypipe[0],STDIN_FILENO);
close(x2ypipe[0]);
close(x2ypipe[1]);
//direct y2z pipe to standard output and replace the child with the program part2
dup2(x2ypipe[1],y2zpipe[1]);
dup2(y2zpipe[1],STDOUT_FILENO);
close(y2zpipe[0]);
close(y2zpipe[1]);
int b=execl(argv[i],argv[i],(char *)NULL);
if(b==-1) {
perror("The following error occurred at B");
}
}
}
pid_t zchild =fork();
if(zchild==0) {
dup2(y2zpipe[0],STDIN_FILENO);
close(y2zpipe[0]);
close(y2zpipe[1]);
int c=execl(argv[argc-1],argv[argc-1],(char *)NULL);
if(c==-1) {
perror("The following error occurred at C");
}
}
close(x2ypipe[0]);
close(x2ypipe[1]);
wait(NULL);
wait(NULL);
wait(NULL);
}
now right now I am only passing in three programs in to the argv[] and it works fine. I will have to add a if statement in my for loop to check for the last/highest possible value of i to connect the y2z pipe to the zchild. What I am having trouble doing it connecting the children to each other within the for loop. How would I go about creating a new pipe for each child from the last child?
Maybe this will help. Notice how I call pipe() inside my for loop, so I don't have to think of new "x2y", "y2z", "z2omega", etc, etc names for the pipe pairs.
Also notice how I used a variable prevfd from outside the for loop to carry the previous iterations's pipe file descriptor into the next iteration. And how it points to "/dev/null" to start with.
Finally, notice how I call wait() precisely as many times as I need to, in a loop, rather than writing it 3 (or 4 or 5 or ... 1,397) times.
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdlib>
#include <cstdio>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
int prevfd;
prevfd = open("/dev/null", O_RDONLY);
if(prevfd < 0) {
perror("/dev/null");
exit(1);
}
for(int i = 1; i < argc; ++i) {
int pipefd[2];
int kid;
if(i != argc-1 && pipe(pipefd)) {
perror("pipe");
break;
}
if(!fork()) {
dup2(prevfd, 0);
close(prevfd);
if(i != argc-1) {
dup2(pipefd[1], 1);
close(pipefd[0]);
close(pipefd[1]);
}
execl(argv[i], argv[i], (char*)0);
perror(argv[i]);
exit(1);
}
close(prevfd);
prevfd = pipefd[0];
close(pipefd[1]);
}
while(wait((int*)0) != -1)
;
return 0;
}
You need a separate pipe between each pair of connected processes.