I'm running some code on Mac OSX 10.6.6 and XCode 3.2.4 and I have some pretty standard code: fork(), if pid == 0 then execvp with a command and the args (the args include the command as the first element in the array, and the array is null terminated).
We're going over this in my Operating Systems class and our assignment is to write a simple shell. Run commands with their args and switches, both redirects (< and >) and pipe (|). I'm getting several problems.
1) Sometimes I get the EXC_SOFTWARE signal while debugging (so far I haven't gotten it if I run the app outside of XCode, but I'm new to Mac and wouldn't know what that would look like if I did)
2) Sometimes the getline for the next command gets junk that seems to be printed by other couts. This begins looping forever, exponentially breaking. I have tested with printing getpid() with every prompt and only the beginning process prints these out, I don't appear to have an accidental "fork bomb."
Here's what I have so far:
#include <iostream>
#include <string>
#include <unistd.h>
using namespace std;
char** Split(char* buffer, int &count) {
count = 1;
for (int i = 0; i < strlen(buffer); i++) {
if (buffer[i] == ' ') {
count++;
}
}
const char* delim = " ";
char* t = strtok(buffer, delim);
char** args = new char*[count + 1];
for (int i = 0; i < count; i++) {
args[i] = t;
t = strtok(NULL, delim);
}
args[count] = 0;
return args;
}
void Run(char** argv, int argc) {
int pid = 0;
if ((pid = fork()) == 0) {
//for testing purposes, print all of argv
for (int i = 0; i < argc; i++) {
cout << "{" << argv[i] << "}" << endl;
}
execvp(argv[0], argv);
cout << "ERROR 1" << endl;
exit(1);
} else if (pid < 0) {
cout << "ERROR 2" << endl;
exit(2);
}
wait(NULL);
}
int main(int argc, char * const argv[]) {
char buffer[512];
char prompt[] = ":> ";
int count = 0;
while (true) {
cout << prompt;
cin.getline(buffer, 512);
char **split = Split(buffer, count);
Run(split, count);
}
}
It's exactly what I have, you should be able to cut, paste, and build.
I'm not the best at C++, and chances are there's a memory leak when I don't delete split but my main focus is the EXC_SOFTWARE signal and see what I'm doing wrong with my looping issue. Any thoughts?
EDIT:
The assignment requires very limited error checking and I'm assuming all input is correct. By correct I mean properly formatted and limited for my app to run the command, i.e. no bizarre space count, no & to run async, no multi piping commands, etc.
One problem is that you do not check the return from cin.getline(), so if you type EOF, the code goes into a tight loop. You're also leaking memory.
Try:
while (cout << prompt && cin.getline(buffer, sizeof(buffer))
{
int count = 0;
char **split = Split(buffer, count);
Run(split, count);
delete[] split;
}
The code in Split() does not really handle blank lines at all well. It seems to take an aeon to run execvp() when the only arguments are null pointers, which is what happens if you return a blank line.
I'm able to run multiple simple commands (such as 'vim makefile' and 'make shell' and 'ls -l' and 'cat shell.cpp' and so on - I even did a few with more than two arguments) OK with this, and I can quit the command (shell) with Control-D and so on. I have fixed it so it compiles with no warnings from g++ -O -Wall -o shell shell.cpp. I have not fixed the splitting code so that it handles empty lines or all blank lines correctly.
#include <iostream>
#include <string>
#include <unistd.h>
using namespace std;
char** Split(char* buffer, int &count) {
count = 1;
for (size_t i = 0; i < strlen(buffer); i++) { // #1
if (buffer[i] == ' ') {
count++;
}
}
char** args = new char*[count + 1];
const char* delim = " ";
char* t = strtok(buffer, delim);
for (int i = 0; i < count; i++) {
args[i] = t;
t = strtok(NULL, delim);
}
args[count] = 0;
return args;
}
void Run(char** argv, int argc) {
int pid = 0;
if ((pid = fork()) == 0) {
//for testing purposes, print all of argv
for (int i = 0; i < argc; i++)
{
if (argv[i] != 0) // #2
cout << "{" << argv[i] << "}" << endl;
else
cout << "{ NULL }" << endl; // #3
}
execvp(argv[0], argv);
cout << "ERROR 1" << endl;
exit(1);
} else if (pid < 0) {
cout << "ERROR 2" << endl;
exit(2);
}
wait(NULL);
}
int main(int argc, char * const argv[]) {
char buffer[512];
char prompt[] = ":> ";
while (cout << prompt && cin.getline(buffer, sizeof(buffer))) // #4
{
int count = 0;
char **split = Split(buffer, count);
if (count > 0) // #5
Run(split, count);
delete[] split; // #6
}
}
I've marked the significant changes (they mostly aren't all that big). I'm compiling with GCC 4.2.1 on MacOS X 10.6.6.
I can't readily account for the garbage characters you are seeing in the buffer.
You're making the assumption that the input line contains one more token than spaces. This assumption may fail if the input line is empty, ends or begins with a space or contains multiple consecutive spaces. In these cases, one of the calls to strtok will return NULL, and this will crash the forked process when you try to print that argument in Run. These are the only cases in which I've encountered problems; if you've encountered any others, please specify your input.
To avoid that assumption, you could do the counting with strtok the same way you do the tokenizing. That's generally a good idea: if you need two things to coincide and you can do them the same way, you introduce an additional source of errors if you do them differently instead.
Related
I'm new to C++, and I'm trying to write a project that interacts through command line. Right now, whenever I run my main (which is the executable), I always receive a segmentation fault error when the main program finished.
Edit comment:
I'm told by tutor to use as little as C++ features such as vectors or strings ... I'm also very new to C++, so i'm trying to utilize as many basic C functions as I can.
I'm
My main function looks like this:
int main(int argc, char** argv) {
cout << "starting mvote..." << endl;
int run_flag = 1;
char* actionBuffer = (char*)malloc(100 * sizeof(char));
char* action = (char*)malloc(16 * sizeof(char));
char* readPtr;
char exit[4] = { 'e','x','i','t' };
//parse command line argumentand get the filename
char* filename = argv[2];
cout << filename;
FILE* fp;
char line[64];
//from here, I'm opening the file and read it by lines
fp = fopen(filename, "r");
if (fp == NULL) {
cout << "file not exists";
return -1;
}
while (fgets(line, 64, fp) != NULL) {
cout << line << "\n";
}
fclose(fp);
while (run_flag == 1) {
cout << "what do you want?\n " << endl;
cin.getline(actionBuffer, 1024);
if (strcmp(actionBuffer, exit) == 0) {
cout << "bye!";
run_flag = 0;
break;
}
//if not exit, Look for the space in the input
readPtr = strchr(actionBuffer, ' ');
int size = readPtr - actionBuffer;
//extract the operation
strncpy(action, actionBuffer, size);
for (int i = 0; i < size; i++) {
cout << "operation:" << action[i];
}
// depend on the operation specified before the first empty space
run_flag = 0;
}
free(actionBuffer);
free(action);
return 0;
}
Description:
I first try to open up a csv file which lies in the same folder as main, and I read the file line by line. Then, I just implement a simple command where you can type exit and quit the program.
I allocate two memory, actionBuffer and action, which are used to hold command
Problem: a segmentation fault [core dumped] always exists when I type exit and hit enter, and then the process finished.
Research: So I learned that segmentation fault is due to accessing a memory that does not belongs to me. But where in my program am I trying to access such a memory?
Any advice is appreciated! Thank you.
Just to give you an idea, this would be an example of C++ code
#include<iostream>
#include<fstream>
#include<string_view>
#include<string>
#include<sstream>
#include<exception>
int main(int argc, char** argv) {
std::cout << "starting mvote...\n";
//parse command line argumentand get the filename
std::string filename = argv[2]; // NO CHECKS!
std::cout << filename <<'\n';
//from here, I'm opening the file and read it by lines
{
std::ifstream ifs(filename);
if (!ifs) {
throw std::invalid_argument("file not exists");
}
std::string line;
while (std::getline(ifs, line)) {
std::cout << line << '\n';
}
}
bool run_flag = true;
while (run_flag) {
std::cout << "what do you want?\n";
std::string userInput;
std::getline(std::cin, userInput);
if (userInput == "exit") {
std::cout << "bye!\n";
return 0;
}
std::stringstream userInputSs(userInput);
std::string operation;
while(userInputSs >> operation){
std::cout << "operation: " << operation << '\n';
}
}
}
I have been searching all over for a simple input Pipe example in C++.
Most of the pipes found is old unix style pipes.
Admitting that I am quite unfamiliar with piping, and my purpose is to create a c++ Apache Log pipe, so that all logs can be piped through my program. I assume that the minimum requirement is an input-pipe.
Currently my pipe is listed below.
I have established that when running the command : cat /pathto/access_log | ./myfunction , the output will produce an output like : 0 : 1: 14 HTTP GET found.
And I have established that the pipe gets its input from cin, and not from argv, something that might be stupefying and trivial for the professional programmer.
My current problem now, is that the prgrogram stop when all inputs have been piped in. Trying to type something in the terminal, seem to work, but the input seem to be disregarded and not handled.
But my question is : Is this a properly written input pipe. And if not. What is missing?
void pipecommand(string strcommand, int &cnt1, int &cnt2, bool flag){
//Extra code inserted here to handle http log entries
//-------------
int loc = strcommand.find("GET");
if(loc != string::npos)
{
if(flag){cnt1++;}
else{cnt2++;}
cout << cnt1 << " : " << cnt2 << ": " << loc << " HTTP GET found" << endl;
}
}
int main(int argc, char *argv[])
{
string str_command = "";
string argument = "";
bool argument_f = false;
int counter1 = 0;
int counter2 = 0;
bool isArg = false;
if (argc == 2)
{
argument = argv[1];
argument_f = true;
}
while(getline(cin, str_command)) //getline inserted here
{
if(argument_f){
isArg = true;
pipecommand(argument, counter1, counter2, isArg);
argument_f = false;
}
else{
//getline(cin, str_command);
//Removed and inserted in while
isArg = false;
pipecommand(str_command, counter1, counter2, isArg);
}
}
return 0;
}
I've been beating my head against a wall with this one for a while. I'm only trying to make a simple application to read out the contents of a file. Here's some of the code:
errno_t error;
if ((error = fopen_s(&f, file, "r")) == 0) {
while (true) {
std::wcout << std::endl << "NEW RUN" << std::endl;
wchar_t content[4096];
if (fgetswc(content, 4096, f) == 4096) {
std::wcout << content;
std::wcout.flush();
}
else {
std::wcout << content;
std::wcout.flush();
break;
}
}
fclose(f);
std::wcout << "PLEASE PRINT THIS NOW";
system("pause");
return 0;
}
And the custom fgetswc function:
int fgetswc(wchar_t buffer[], int count, FILE * f) {
for (int i = 0; i < count; i = i + 1) {
wchar_t c = fgetwc(f);
if (c != WEOF) {
buffer[i] = c;
} else {
return i;
}
}
return count;
}
It reads the first 4096 bytes out of the file, but then subsequent std::wcout calls will not print out to the console I have. It reads the rest of the file and ends successfully, as I can see using breakpoints and the debugger. content gets filled up every iteration. I also attempted putting in debug statements, but even those don't get printed. Am I just doing something wrong? As far as I can tell there's no special characters in my file, it's just a log file.
std::wcout << content;
This is effectively calling std::wostream::operator<<(const wchar_t *). It doesn't know that content is not a ␀-terminated string. In fact, it can't possibly know that it has valid length 4096 in the first case and some amount less in the second case (you don't save the return value of fgetswc).
Somewhere in my project I use fork and pipe to execute another process and pipe its I/O to communicate with it (I'm writing it in C++). There is no problem when I compile it in Ubuntu 14.04, it will work just fine, but I compiled it in fedora on a WMWare virtual machine and strange things began to happen. If I run the binary in terminal, there is no error but nothing will be written in the pipe (but getting streams of characters will work). I tried to debug my code in fedora, I put a break point in my code, but then a broken pipe signal was given when process tried to read from pipe (there were no signals when executing in terminal).
So, have any of you encountered such problems before? Is there any difference in piping between debian and red hat linux? Or is it because I'm running fedora on a virtual machine?
CODE:
int mFD_p2c [2];
int mFD_c2p [2];
int mEnginePID;
if (pipe(mFD_p2c) != 0 || pipe(mFD_c2p) != 0)
{
cout << "Failed to pipe";
exit(1);
}
mEnginePID = fork();
if (mEnginePID < 0)
{
cout << "Fork failed";
exit(-1);
}
else if (mEnginePID == 0)
{
if (dup2(mFD_p2c[0], 0) != 0 ||
close(mFD_p2c[0]) != 0 ||
close(mFD_p2c[1]) != 0)
{
cout << "Child: failed to set up standard input";
exit(1);
}
if (dup2(mFD_c2p[1], 1) != 1 ||
close(mFD_c2p[1]) != 0 ||
close(mFD_c2p[0]) != 0)
{
cout << "Child: failed to set up standard output";
exit(1);
}
string engine = "stockfish";
execlp(engine.c_str(), (char *) 0);
cout << "Failed to execute " << engine;
exit(1);
}
else
{
close(mFD_p2c[0]);
close(mFD_c2p[1]);
string str = "uci";
int nbytes = str.length();
if (write(mFD_p2c[1], str.c_str(), nbytes) != nbytes)
{
cout << "Parent: short write to child";
exit(1);
}
cout << "The following string has been written to engine:\n"
<< string(1, '\t') << str;
char readBuffer[2];
string output = "";
while (1)
{
int bytes_read = read(mFD_c2p[0], readBuffer, sizeof(char));
if (readBuffer[0] == '\n')
break;
readBuffer[bytes_read] = '\0';
output += readBuffer;
}
cout << "Got: " << output;
}
I see you're using Stockfish. I too have exactly experienced this behavior from Stockfish. The problem lies within how it handles output. Defined in misc.h:
#define sync_cout std::cout << IO_LOCK
And looking at the code again we'll see that IO_LOCK is an enum which is used in an overloaded friend operator for cout:
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m;
if (sc == IO_LOCK)
m.lock();
if (sc == IO_UNLOCK)
m.unlock();
return os;
}
What I see here is that during using cout, a mutex is locked. I don't know how exactly this affects cout's output in a pipe instead of stdout, but I'm positive that this is the cause for the problem. You can check it by removing the lock functionality.
Edit: I forgot to mention that the pipe behavior is not different in linux based systems as mentioned before, but there might be slight differences between distributions handling mutexes used with pipes.
There are no differences in piping between debian and red hat, but the following list of questions may help you:
-Are the Ubuntu and the Fedora using the same architecture (64 bit vs 32) ?
-Are you using the same version of gcc (or any other compiler) ?
(Suggestion: use cerr for your error outputs, and maybe your debug output too -> you dup the standard outputs and inputs, so if something fails you may not see it)
Anyhow, here's how you turn it into a self-contained, compilable example:
stockfish
#cat stockfish
tr a-z A-Z #just so we do something
echo #need to end with a "\n" or else the parent won't break out of the while loop
Run command:
make pipes && PATH=.:$PATH pipes
pipes.cc
//pipes.cc
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
using namespace std;
int mFD_p2c [2];
int mFD_c2p [2];
int mEnginePID;
if (pipe(mFD_p2c) != 0 || pipe(mFD_c2p) != 0)
{
cout << "Failed to pipe";
exit(1);
}
mEnginePID = fork();
if (mEnginePID < 0)
{
cout << "Fork failed";
exit(-1);
}
else if (mEnginePID == 0)
{
if (dup2(mFD_p2c[0], 0) != 0 ||
close(mFD_p2c[0]) != 0 ||
close(mFD_p2c[1]) != 0)
{
cout << "Child: failed to set up standard input";
exit(1);
}
if (dup2(mFD_c2p[1], 1) != 1 ||
close(mFD_c2p[1]) != 0 ||
close(mFD_c2p[0]) != 0)
{
cout << "Child: failed to set up standard output";
exit(1);
}
string engine = "stockfish";
char *const args[]={};
int ret;
execvp(engine.c_str(), args);
//I need the endl here or else it doesn't show for me when the execvp fails; I wasn't able to compile the original exec command so I used a different one from the exec* family
cout << "Failed to execute " << engine << endl;
exit(1);
}
else
{
close(mFD_p2c[0]);
close(mFD_c2p[1]);
string str = "uci";
int nbytes = str.length();
if (write(mFD_p2c[1], str.c_str(), nbytes) != nbytes)
{
cout << "Parent: short write to child";
exit(1);
}
//My particular child process tries to read to the end, so give it the EOF
close(mFD_p2c[1]);
cout << "The following string has been written to engine:\n"
<< string(1, '\t') << str;
char readBuffer[2];
string output = "";
while (1)
{
int bytes_read = read(mFD_c2p[0], readBuffer, sizeof(char));
if (readBuffer[0] == '\n')
break;
readBuffer[bytes_read] = '\0';
output += readBuffer;
}
cout << "Got: " << output;
}
return 0;
}
output:
The following string has been written to engine:
uciGot: UCI
I am working on a code where it will do Linux command piping. Basically in my code, it will parse the user input command, then run it using the execvp function.
However, to do this, I would need to know the command, as well as its parameters. I have been trying to get the parsing to work correctly, however, it seems that when I do a test case, the output from both of the arrays that store their respective programs is the same. The commands/parameters are stored in a char array called prgname1 and prgname2.
For instance, if I were to run my program with the parameter "ps aux | grep [username]", then the output of prgname1[0] and prgname2[0] are both [username]. They are supposed to be ps and grep, respectively.
Can anyone take a look at my code and see where I might be having an error which is causing this?
Thanks!
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#define MAX_PARA_NUM 5
#define MAX_COMMAND_LEN 1024
using namespace std;
int main(int argc, char *argv[]) {
char *prgname1[MAX_PARA_NUM], *prgname2[MAX_PARA_NUM];
char command[MAX_COMMAND_LEN];
int pfd[2];
pipe(pfd);
pid_t cid1, cid2;
char *full = argv[1];
char str[MAX_COMMAND_LEN];
int i = 0;
int j = 0;
int k = 0;
int ind = 0;
while (ind < strlen(full)) {
if (full[ind] == ' ') {
strncpy(command, str, i);
cout << command << endl;
prgname1[j] = command;
j++;
i = 0;
ind++;
}
else {
str[i] = full[ind];
i++;
ind++;
}
if(full[ind] == '|') {
i = 0;
j = 0;
ind+=2;
while (ind < strlen(full)) {
if (full[ind] == ' ') {
strncpy(command, str, i);
cout << command << endl;
prgname2[j] = command;
j++;
i = 0;
ind++;
}
else {
str[i] = full[ind];
i++;
ind++;
}
if (ind == strlen(full)) {
strncpy(command, str, i);
cout << command << endl;
prgname2[j] = command;
break;
}
}
}
}
// test output here not working correctly
cout << prgname1[0] << endl;
cout << prgname2[0] << endl;
// exits if no parameters passed
if (argc != 2) {
cout << "Usage:" << argv[0] << endl;
exit(EXIT_FAILURE);
}
// exits if there is a pipe error
if (pipe(pfd) == -1) {
cerr << "pipe" << endl;
exit(EXIT_FAILURE);
}
cid1 = fork(); // creates child process 1
// exits if there is a fork error
if (cid1 == -1 || cid2 == -1) {
cerr << "fork";
exit(EXIT_FAILURE);
}
// 1st child process executes and writes to the pipe
if (cid1 == 0) {
char **p = prgname1;
close(1); // closes stdout
dup(pfd[1]); // connects pipe output to stdout
close(pfd[0]); // closes pipe input as it is not needed
close(pfd[1]); // closes pipe output as pipe is connected
execvp(prgname1[0], p);
cerr << "execlp 1 failed" << endl;
cid2 = fork();
}
// 2nd child process reads from the pipe and executes
else if (cid2 == 0) {
char **p = prgname2;
close(0); // closes stdin
dup(pfd[0]); // connects pipe input to stdin
close(pfd[0]); // closes pipe input as pipe is connected
close(pfd[1]); // closes pipe output as it is not needed
execvp(prgname2[0], p);
cerr << "execlp 2 failed" << endl;
}
else {
sleep(1);
waitpid(cid1, NULL, 0);
waitpid(cid2, NULL, 0);
cout << "Program successfully completed" << endl;
exit(EXIT_SUCCESS);
}
return 0;
}
argv[1] gives you the first argument on the command line - not the entire command line. If you want the full list of command line arguments passed into the process, you will need to append argv[1], argv[2], ..., argv[argc - 1] together with a space between each.
Additionally, when you process it, you are setting the pointer for your prgname1[index] to command, so every time you set a given character pointer, they are all pointing to the same location (hence, they are all the same value). You need to allocate space for each element in prgname1 and copy command into it (using strncpy). Alternatively, using std::string and std::vector eliminates much of your current code.