<commit_before>#include<iostream>
#include<sstream>
#include<stdio.h>
#include<boost/tokenizer.hpp>
#include<errno.h>
#include<string>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/unistd.h>

using namespace std;
using namespace boost;

typedef tokenizer<char_separator<char> > Tok;

void readCommands(string str);
int conjunct(int n, stringstream& ss, const Tok::iterator &it);
void skipCommand(Tok::iterator &it, Tok &tokens);
void splitString(char** args, stringstream& ss, int n);

int main(){

    cout << endl;
    string str;
    string usrname = getlogin();
    char hostname[128];
    int success = gethostname(hostname, sizeof hostname);
    string userinfo = "";

    if(usrname != "\0" && success == 0){
        userinfo = usrname + "@" + hostname;
    }

    bool notExited = true;

    while(notExited){
        cout << userinfo << "$ ";
        getline(cin, str);
        readCommands(str);
    }

    return 0;
}

void readCommands(string str){

    char_separator<char> sep("\" ", ";#|&");

    Tok tokens(str, sep);
    stringstream ss;
    int n = 0;

    for(Tok::iterator it = tokens.begin(); it != tokens.end(); it++){
        if(*it == "#"){
            conjunct(n, ss, it);
            return;
        }
        else if(*it == ";"){
            conjunct(n, ss, it);
            n = 0;
            it++;
            if(it == tokens.end()) break;
        }
        else if(*it ==  "&"){
            if(conjunct(n, ss, it) == -1){
                skipCommand(it, tokens);
            }
            n = 0;
            if(it == tokens.end()) break;
            it++;
        }
        else if(*it == "|"){
            if(conjunct(n, ss, it) != -1){
                skipCommand(it, tokens);
            }
            n = 0;
            if(it == tokens.end()) break;
            it++;
        }

        if(*it != "&" && *it != "|"){
            ss << *it;
            ss << " ";
            n++;
        }
    }

    if(n > 0){
        Tok::iterator it = tokens.begin();
        conjunct(n, ss, it);
    }
}

int conjunct(int n, stringstream& ss, const Tok::iterator &it){

    if(n == 0 && *it != "#"){
        cerr << "Bash: syntax error near unexpected token \'" << *it << "\'" << endl;
        return -2;
    }
    else if(n == 0 && *it == "#") return 0;
    char** args = new char*[n + 1];

    splitString(args, ss, n);

    int pid = fork();

    if(pid == -1){
        perror("There was an error with the fork().");
        exit(1);
    }
    else if(pid == 0){
        if(-1 == execvp((const char*) args[0], (char* const*) args)){
            perror(args[0]);
            exit(1);
        }
    }
    else if(pid > 0){
        if(-1 == wait(0)){
            perror("There was an error with wait().");
            return -1;
        }
    }
    return 0;
}

void skipCommand(Tok::iterator &it, Tok &tokens){
    while(it != tokens.end() && *it != ";"){
        it++;
    }
}

void splitString(char** args, stringstream& ss, int n){

    string *str = new string[n];

    for(int i = 0; i < n; i++){

        ss >> str[i];

        args[i] = (char*) str[i].c_str();
    }

    args[n] = NULL;

    char exitC[] = "exit";

    if(strcmp(args[0], exitC) == 0){
        cout << endl;
        exit(0);
    }

    delete[] str;
}
<commit_msg>updated rshell.cpp to delete dynamically allocated memory in conjunct() function<commit_after>#include<iostream>
#include<sstream>
#include<stdio.h>
#include<boost/tokenizer.hpp>
#include<errno.h>
#include<string>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/unistd.h>

using namespace std;
using namespace boost;

typedef tokenizer<char_separator<char> > Tok;

void readCommands(string str);
int conjunct(int n, stringstream& ss, const Tok::iterator &it);
void skipCommand(Tok::iterator &it, Tok &tokens);
void splitString(char** args, stringstream& ss, int n);

int main(){

    cout << endl;
    string str;
    string usrname = getlogin();
    char hostname[128];
    int success = gethostname(hostname, sizeof hostname);
    string userinfo = "";

    if(usrname != "\0" && success == 0){
        userinfo = usrname + "@" + hostname;
    }

    bool notExited = true;

    while(notExited){
        cout << userinfo << "$ ";
        getline(cin, str);
        readCommands(str);
    }

    return 0;
}

void readCommands(string str){

    char_separator<char> sep("\" ", ";#|&");

    Tok tokens(str, sep);
    stringstream ss;
    int n = 0;

    for(Tok::iterator it = tokens.begin(); it != tokens.end(); it++){
        if(*it == "#"){
            conjunct(n, ss, it);
            return;
        }
        else if(*it == ";"){
            conjunct(n, ss, it);
            n = 0;
            it++;
            if(it == tokens.end()) break;
        }
        else if(*it ==  "&"){
            if(conjunct(n, ss, it) == -1){
                skipCommand(it, tokens);
            }
            n = 0;
            if(it == tokens.end()) break;
            it++;
        }
        else if(*it == "|"){
            if(conjunct(n, ss, it) != -1){
                skipCommand(it, tokens);
            }
            n = 0;
            if(it == tokens.end()) break;
            it++;
        }

        if(*it != "&" && *it != "|"){
            ss << *it;
            ss << " ";
            n++;
        }
    }

    if(n > 0){
        Tok::iterator it = tokens.begin();
        conjunct(n, ss, it);
    }
}

int conjunct(int n, stringstream& ss, const Tok::iterator &it){

    if(n == 0 && *it != "#"){
        cerr << "Bash: syntax error near unexpected token \'" << *it << "\'" << endl;
        return -2;
    }
    else if(n == 0 && *it == "#") return 0;
    char** args = new char*[n + 1];

    splitString(args, ss, n);

    int pid = fork();

    if(pid == -1){
        perror("There was an error with the fork().");
        exit(1);
    }
    else if(pid == 0){
        if(-1 == execvp((const char*) args[0], (char* const*) args)){
            perror(args[0]);
            exit(1);
        }
    }
    else if(pid > 0){
        if(-1 == wait(0)){
            perror("There was an error with wait().");
            return -1;
        }
    }
    delete[] args;
    return 0;
}

void skipCommand(Tok::iterator &it, Tok &tokens){
    while(it != tokens.end() && *it != ";"){
        it++;
    }
}

void splitString(char** args, stringstream& ss, int n){

    string *str = new string[n];

    for(int i = 0; i < n; i++){

        ss >> str[i];

        args[i] = (char*) str[i].c_str();
    }

    args[n] = NULL;

    char exitC[] = "exit";

    if(strcmp(args[0], exitC) == 0){
        cout << endl;
        exit(0);
    }

    delete[] str;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
#include "comParse.h"
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace boost;
using namespace std;

struct redirect;
bool redirection(char** argv, redirect& rdts, const int fd);

struct redirect {
	bool doI_Rdct; // input redirect
	int indexIR;
	int savedSTDIN; // for duping STDIN and saving it
	int rfd; // when opening new fd in 0 for reading

	bool doO_Rdct; // output redirect
	int indexOR;
	int savedSTDOUT; // for duping STDOUT and saving it
	int wofd; // when opening new fd in 1 for writing

	bool doE_Rdct; // output redirect
	int indexER;
	int savedSTDERR; // for duping STDERR and saving it
	int wefd; // when opening new fd in 2 for writing

	vector<int*> currentFD;

	redirect() {
		doI_Rdct = false;
		savedSTDIN = -1;
		rfd = -1;
		indexIR = -1;

		doO_Rdct = false;
		savedSTDOUT = -1;
		wofd = -1;
		indexOR = -1;

		doE_Rdct = false;
		savedSTDERR = -1;
		wefd = -1;
		indexER = -1;
	}
	void closeCurrFDs() {
		if (!currentFD.empty()) {
			for (vector<int*>::iterator it = currentFD.begin();
					it != currentFD.end(); it++) {
				if (-1 == close(*(*it))) {
					perror("close");
					exit(-1);
				}
			}

			currentFD.clear();
		}
	}
	~redirect() {
		//closeCurrFDs();
	}
};

int main() {
	while (true) {
		char** argv = NULL;
		vector<char*> commands;
		comParse comPr;
		do {
			cerr << "$ " << flush;

			string input = "";
			getline(cin, input);	// Get command

			commands = comPr.parseLine(input);
		} while (commands.size() == 1); // if empty line, repeat req for command. sz 1 = just NULL delim.

		argv = &commands[0];
		redirect rdts;

		for (unsigned int i = 0; i < commands.size() - 1; i++) { // NULL at end
			if ( strcmp(commands.at(i), ";") == 0
					|| strcmp(commands.at(i), "0<")) {
				delete[] commands.at(i);
				argv[i] = NULL;

				break;
			} else if (strcmp(commands.at(i), "<") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;

				rdts.doI_Rdct = true;
				rdts.indexIR = i;
			} else if (strcmp(commands.at(i), ">") == 0
					|| strcmp(commands.at(i), "1>") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;

				rdts.doO_Rdct = true;
				rdts.indexOR = i;
			} else if (strcmp(commands.at(i), "2>") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;

				rdts.doE_Rdct = true;
				rdts.indexER = i;
			}
		}

		/* Confusing as heck, let me explain!
		 * char const* means pointer to a constant char
		 * so char const* const means a const pointer to a const char
		 * and char const* const* means a const pointer to a const char pointer!
		 * (Read declarations from right to left to make it make sense -
		 *  char const* = POINTER (*) to a CONST CHAR)
		 */
		//char const* const* c_arr = &commands[0]; // even if Not necessary: keep as really cool discovery!

		bool doExec = true; // If false, do not fork and exec command

		char ex[5] = "exit";
		if (strcmp(argv[0], ex) == 0) {
			comPr.deleteCStrings(commands); // Has heap allocated memory, always should be dealt with
			return 0; // EXIT command
		}

		if (rdts.doI_Rdct) doExec = redirection(argv, rdts, STDIN_FILENO);
		if (rdts.doO_Rdct) doExec = redirection(argv, rdts, STDOUT_FILENO);
		if (rdts.doE_Rdct) doExec = redirection(argv, rdts, STDERR_FILENO);

		if (doExec) {
			int pid = fork();
			if (pid == -1) {	// Error in fork
				perror("error in fork");
				comPr.deleteCStrings(commands);
				exit(-1);
			} else if (pid == 0) {	// Child Process
				if (-1 == execvp(argv[0], argv)) {
					// All children need to exit! (forgot this)
					if (errno == EACCES) { // Access denied
						perror(argv[0]);
						exit(-1);
					} else if (errno == ENOEXEC) { // Not Exec
						perror(argv[0]);
						exit(-1);
					} else if (errno == ENOENT) { // Does not exist
						perror(argv[0]);
						exit(-1);
					} else {
						perror("error in execvp");
						exit(-1);
					}
				}
			} else if (pid > 0) { // parent!
				if (-1 == wait(0))
					perror("wait");
				if (errno != 0 && errno != EACCES && errno != ENOEXEC && errno != ENOENT)
					exit(-1);
			}
		}

		if (rdts.doI_Rdct) {
			if (-1 == dup2(rdts.savedSTDIN, rdts.rfd)) {	// Restore stdin
				perror("dup2-input");
				exit(-1);
			}
		}
		if (rdts.doO_Rdct) {
			if (-1 == dup2(rdts.savedSTDOUT, rdts.wofd)) {	// Restore stdout
				perror("dup2-output");
				exit(-1);
			}
		}
		if (rdts.doE_Rdct) {
			if (-1 == dup2(rdts.savedSTDERR, rdts.wefd)) {	// Restore stdout
				perror("dup2-stderr");
				exit(-1);
			}
		}

		comPr.deleteCStrings(commands);
		//rdts.closeCurrFDs();
	}

	return 0;
}

// closes respective fd and opens file in its place
bool redirection(char** argv, redirect& rdts, const int fd) {
	bool doRedir; // generalize between input, output, and errput, with pointers
	int* savedFD;
	int* newFD;
	int* index;
	int FLAG;

	if (fd == STDIN_FILENO) {
		doRedir = rdts.doI_Rdct;
		savedFD = &(rdts.savedSTDIN);
		newFD = &(rdts.rfd);
		index = &(rdts.indexIR);

		FLAG = O_RDONLY;
	} else if (fd == STDOUT_FILENO) {
		doRedir = rdts.doO_Rdct;
		savedFD = &(rdts.savedSTDOUT);
		newFD = &(rdts.wofd);
		index = &(rdts.indexOR);

		FLAG = O_WRONLY | O_CREAT | O_TRUNC;
	} else if (fd == STDERR_FILENO) {
		doRedir = rdts.doE_Rdct;
		savedFD = &(rdts.savedSTDERR);
		newFD = &(rdts.wefd);
		index = &(rdts.indexER);

		FLAG = O_WRONLY | O_CREAT | O_TRUNC;
	} else {
		cerr << "Redirection failed\n";
		exit(-1);
	}

	if (doRedir) {
		// dup the STDIN
		if ( -1 == (*savedFD = dup(fd)) ) {
			perror("dup-redirect" + fd);
			exit(-1);
		}

		// close STDIN
		if ( -1 == close(fd) ) {
			perror("close-redirect" + fd);
			exit(-1);
		}

		// open file
		if ( -1 == (*newFD = open(argv[*index + 1], FLAG)) ) {
			if (errno == EACCES || errno == ENOENT) { 	//FIXME
				perror(argv[rdts.indexIR + 1]); 	// filename syntax
				return false;
			} else {
				perror("open-redirect" + fd);
				exit(-1);
			}
		}

		// record which fd to close
		rdts.currentFD.push_back(newFD);
	}

	return true;
}

<commit_msg>Fixed segfault. Now multi-bit and all 3 first redirects work\!<commit_after>#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
#include "comParse.h"
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace boost;
using namespace std;

struct redirect;
bool redirection(char** argv, redirect& rdts, const int fd);

struct redirect {
	bool doI_Rdct; // input redirect
	int indexIR;
	int savedSTDIN; // for duping STDIN and saving it
	int rfd; // when opening new fd in 0 for reading

	bool doO_Rdct; // output redirect
	int indexOR;
	int savedSTDOUT; // for duping STDOUT and saving it
	int wofd; // when opening new fd in 1 for writing

	bool doE_Rdct; // output redirect
	int indexER;
	int savedSTDERR; // for duping STDERR and saving it
	int wefd; // when opening new fd in 2 for writing

	vector<int*> currentFD;

	redirect() {
		doI_Rdct = false;
		savedSTDIN = -1;
		rfd = -1;
		indexIR = -1;

		doO_Rdct = false;
		savedSTDOUT = -1;
		wofd = -1;
		indexOR = -1;

		doE_Rdct = false;
		savedSTDERR = -1;
		wefd = -1;
		indexER = -1;
	}
	void closeCurrFDs() {
		if (!currentFD.empty()) {
			for (vector<int*>::iterator it = currentFD.begin();
					it != currentFD.end(); it++) {
				if (-1 == close(*(*it))) {
					perror("close");
					exit(-1);
				}
			}

			currentFD.clear();
		}
	}
	~redirect() {
		//closeCurrFDs();
	}
};

int main() {
	while (true) {
		char** argv = NULL;
		vector<char*> commands;
		comParse comPr;
		do {
			cerr << "$ " << flush;

			string input = "";
			getline(cin, input);	// Get command

			commands = comPr.parseLine(input);
		} while (commands.size() == 1); // if empty line, repeat req for command. sz 1 = just NULL delim.

		argv = &commands[0];
		redirect rdts;

		for (unsigned int i = 0; i < commands.size() - 1; i++) { // NULL at end
			if ( strcmp(commands.at(i), ";") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;

				break;
			} else if (strcmp(commands.at(i), "<") == 0
					|| strcmp(commands.at(i), "0<") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;

				rdts.doI_Rdct = true;
				rdts.indexIR = i;
			} else if (strcmp(commands.at(i), ">") == 0
					|| strcmp(commands.at(i), "1>") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;

				rdts.doO_Rdct = true;
				rdts.indexOR = i;
			} else if (strcmp(commands.at(i), "2>") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;

				rdts.doE_Rdct = true;
				rdts.indexER = i;
			}
		}

		/* Confusing as heck, let me explain!
		 * char const* means pointer to a constant char
		 * so char const* const means a const pointer to a const char
		 * and char const* const* means a const pointer to a const char pointer!
		 * (Read declarations from right to left to make it make sense -
		 *  char const* = POINTER (*) to a CONST CHAR)
		 */
		//char const* const* c_arr = &commands[0]; // even if Not necessary: keep as really cool discovery!

		bool doExec = true; // If false, do not fork and exec command

		char ex[5] = "exit";
		if (strcmp(argv[0], ex) == 0) {
			comPr.deleteCStrings(commands); // Has heap allocated memory, always should be dealt with
			return 0; // EXIT command
		}

		if (rdts.doI_Rdct) doExec = redirection(argv, rdts, STDIN_FILENO);
		if (rdts.doO_Rdct) doExec = redirection(argv, rdts, STDOUT_FILENO);
		if (rdts.doE_Rdct) doExec = redirection(argv, rdts, STDERR_FILENO);

		if (doExec) {
			int pid = fork();
			if (pid == -1) {	// Error in fork
				perror("error in fork");
				comPr.deleteCStrings(commands);
				exit(-1);
			} else if (pid == 0) {	// Child Process
				if (-1 == execvp(argv[0], argv)) {
					// All children need to exit! (forgot this)
					if (errno == EACCES) { // Access denied
						perror(argv[0]);
						exit(-1);
					} else if (errno == ENOEXEC) { // Not Exec
						perror(argv[0]);
						exit(-1);
					} else if (errno == ENOENT) { // Does not exist
						perror(argv[0]);
						exit(-1);
					} else {
						perror("error in execvp");
						exit(-1);
					}
				}
			} else if (pid > 0) { // parent!
				if (-1 == wait(0))
					perror("wait");
				if (errno != 0 && errno != EACCES && errno != ENOEXEC && errno != ENOENT)
					exit(-1);
			}
		}

		if (rdts.doI_Rdct) {
			if (-1 == dup2(rdts.savedSTDIN, rdts.rfd)) {	// Restore stdin
				perror("dup2-input");
				exit(-1);
			}
		}
		if (rdts.doO_Rdct) {
			if (-1 == dup2(rdts.savedSTDOUT, rdts.wofd)) {	// Restore stdout
				perror("dup2-output");
				exit(-1);
			}
		}
		if (rdts.doE_Rdct) {
			if (-1 == dup2(rdts.savedSTDERR, rdts.wefd)) {	// Restore stdout
				perror("dup2-stderr");
				exit(-1);
			}
		}

		comPr.deleteCStrings(commands);
		//rdts.closeCurrFDs();
	}

	return 0;
}

// closes respective fd and opens file in its place
bool redirection(char** argv, redirect& rdts, const int fd) {
	bool doRedir; // generalize between input, output, and errput, with pointers
	int* savedFD;
	int* newFD;
	int* index;
	int FLAG;

	if (fd == STDIN_FILENO) {
		doRedir = rdts.doI_Rdct;
		savedFD = &(rdts.savedSTDIN);
		newFD = &(rdts.rfd);
		index = &(rdts.indexIR);

		FLAG = O_RDONLY;
	} else if (fd == STDOUT_FILENO) {
		doRedir = rdts.doO_Rdct;
		savedFD = &(rdts.savedSTDOUT);
		newFD = &(rdts.wofd);
		index = &(rdts.indexOR);

		FLAG = O_WRONLY | O_CREAT | O_TRUNC;
	} else if (fd == STDERR_FILENO) {
		doRedir = rdts.doE_Rdct;
		savedFD = &(rdts.savedSTDERR);
		newFD = &(rdts.wefd);
		index = &(rdts.indexER);

		FLAG = O_WRONLY | O_CREAT | O_TRUNC;
	} else {
		cerr << "Redirection failed\n";
		exit(-1);
	}

	if (doRedir) {
		// dup the STDIN
		if ( -1 == (*savedFD = dup(fd)) ) {
			perror("dup-redirect" + fd);
			exit(-1);
		}

		// close STDIN
		if ( -1 == close(fd) ) {
			perror("close-redirect" + fd);
			exit(-1);
		}

		// open file
		if ( -1 == (*newFD = open(argv[*index + 1], FLAG)) ) {
			if (errno == EACCES || errno == ENOENT) { 	//FIXME
				perror(argv[rdts.indexIR + 1]); 	// filename syntax
				return false;
			} else {
				perror("open-redirect" + fd);
				exit(-1);
			}
		}

		// record which fd to close
		rdts.currentFD.push_back(newFD);
	}

	return true;
}

<|endoftext|>


<commit_before>#include <boost/tokenizer.hpp>
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <cstdlib>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <sstream>
#include <vector>

using namespace boost;
using namespace std;

int main()
{
	string args;
	while(1 != 2)
	{
		cout << "$ ";
		getline(cin, args);

		string exitf;
		char *argv[9];
		int i = 0;
		vector<string> arg_s;
		tokenizer<> tok(args);
		for(tokenizer<>::iterator beg=tok.begin(); beg!=tok.end();++beg) {
			if(*beg == "exit")
				exitf = *beg;

			arg_s.push_back(*beg);
			argv[i] = new char[6];
			strcpy(argv[i], const_cast<char*>(arg_s[i].c_str()));
			++i;
		}
		argv[i] = NULL;
		int pid = fork();
		if(pid == -1) {
			perror("fork");
			exit(1);
		}
		if(pid == 0) {
			int r = execvp(argv[0], argv);
			if(r == -1) {
				perror("execvp");
				exit(1);
			}
		}
		else {
			if(-1 == waitpid(pid, &pid, 0)) {
				perror("waitpid");
			}
			if(exitf == "exit")
				exit(1);
		}

//	int pid = fork();
//	if(pid == -1) {
//		perror("fork");
//		exit(1);
//	}
//	if(pid == 0)
//	{
		//char *argv2[4];
		//argv2[0] = new char[6];
		//strcpy(argv[0], "ls");
		//argv2[1] = new char[6];
		//strcpy(argv[1], "-a");
		//argv2[2] = new char [6];
		//strcpy(argv[2], "-l");

	//	int pid2 = fork();
	//	if(pid2 == -1) {
	//		perror("fork");
	//		exit(1);
	//	}
	//	if(pid2 == 0) {
	///		if(execvp(argv[0], argv) == -1) {
	//			perror("execvp");
	//			exit(1);
	//		}
	//	}
	//	else {
	//		if(-1 == waitpid(pid2,&pid2,0 ))
	//			perror("waitpid");
	//	}
	//
	  //      if(execvp(argv[1], argv) == -1) {
	//		perror("execvp");
	//		exit(1);
	//	}
//
//		cout << "after" << endl;
//	}
//	else {
//		if(-1 == waitpid(pid, &pid, 0))
//			perror("waitpid");
//	}

	}

	return 0;
}
<commit_msg>rshell.cpp updated<commit_after>#include <boost/tokenizer.hpp>
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <cstdlib>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <sstream>
#include <vector>

//using namespace boost;
using namespace std;

int main()
{
	using namespace boost;
	string args;
	while(1 != 2)
	{
		cout << "$ ";
		getline(cin, args);

		char *argv[9];
		int i = 0;
		vector<string> arg_s;

		tokenizer<> tok(args);
		for(tokenizer<>::iterator beg=tok.begin(); beg!=tok.end();++beg) {

			arg_s.push_back(*beg);
			argv[i] = new char[6];
			strcpy(argv[i], const_cast<char*>(arg_s[i].c_str()));
			if(*beg == "exit")
				exit(1);
			++i;
		}
		argv[i] = NULL;
		int pid = fork();
		if(pid == -1) {
			perror("fork");
			exit(1);
		}
		if(pid == 0) {
			int r = execvp(argv[0], argv);
			if(r == -1) {
				perror("execvp");
				exit(1);
			}
		}
		else {
			if(-1 == waitpid(pid, &pid, 0)) {
				perror("waitpid");
			}
		}

//	int pid = fork();
//	if(pid == -1) {
//		perror("fork");
//		exit(1);
//	}
//	if(pid == 0)
//	{
		//char *argv2[4];
		//argv2[0] = new char[6];
		//strcpy(argv[0], "ls");
		//argv2[1] = new char[6];
		//strcpy(argv[1], "-a");
		//argv2[2] = new char [6];
		//strcpy(argv[2], "-l");

	//	int pid2 = fork();
	//	if(pid2 == -1) {
	//		perror("fork");
	//		exit(1);
	//	}
	//	if(pid2 == 0) {
	///		if(execvp(argv[0], argv) == -1) {
	//			perror("execvp");
	//			exit(1);
	//		}
	//	}
	//	else {
	//		if(-1 == waitpid(pid2,&pid2,0 ))
	//			perror("waitpid");
	//	}
	//
	  //      if(execvp(argv[1], argv) == -1) {
	//		perror("execvp");
	//		exit(1);
	//	}
//
//		cout << "after" << endl;
//	}
//	else {
//		if(-1 == waitpid(pid, &pid, 0))
//			perror("waitpid");
//	}

	}

	return 0;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <boost/tokenizer.hpp>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <list>
#include "comParse.h"
#include "redirect.h"
#include "digit.h"

using namespace boost;
using namespace std;

void input_sigHandler(int param) {
	if (param == SIGINT) {
		char currentDir[512];
		if (NULL == (getcwd(currentDir, 512))) { // Currently outputs error that directory is too long. Don't like this FIXME
			perror("getcwd");
			currentDir[0] = '\0';
		}
		cout << '\n' << currentDir << " $ " << flush;
	}
	if (param == SIGTSTP) {}
}

void quit_sigHandler(int param) {
	if (param == SIGINT) {
		cout << endl;
	}
}

int main() {
	list<int> stoppedpids;
	signal (SIGTSTP, input_sigHandler); // Ctrl C

	while (true) {
		signal(SIGINT, input_sigHandler);
		char** argv = NULL;
		vector<char*> commands;
		comParse comPr;

		char currentDir[512]; // Current Working Directory
		if (NULL == (getcwd(currentDir, 512))) { // Currently outputs error that directory is too long. Don't like this FIXME
			perror("getcwd");
			currentDir[0] = '\0';
		}

		do {
			cout << currentDir << " $ " << flush;

			string input = "";
			getline(cin, input);	// Get command

			commands = comPr.parseLine(input);
		} while (commands.size() == 1); // if empty line, repeat req for command. sz 1 = just NULL delim.

		bool doExec = true; // If false, do not fork and exec command

		//MASSIVE SECTION OF CODE DEDICATED TO PARSING FOR SEMI, PIPES
		//-------------------------------------------------------------------------
		int timeout = 0;
		vector<char*> realcom; // command + arguments - input redirection & semicolon
		vector< vector<char*> > realcomsP;
		vector<bool> isPipe;
		bool setPipe = false; // pipe will only be made if needed
		argv = &commands.at(0);
		redirect rdts;	// contains redirection flags and values

		for (unsigned int i = 0; i < commands.size() - 1; i++) { // NULL at end
			char* c = commands.at(i);
			// if semicolon or pipe, split command into two
			if ( strcmp(commands.at(i), ";") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;
				timeout = 1;

				realcom.push_back(NULL);
				realcomsP.push_back(realcom);
				realcom.clear();
				isPipe.push_back(false);
			} else if (strcmp(c, "|") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;
				timeout = 1;

				realcom.push_back(NULL);
				realcomsP.push_back(realcom);
				realcom.clear();
				isPipe.push_back(true);

				setPipe = true;
			} else if (strchr(c, '<') != NULL) {
				int fd = -1;
				// FIXME: Currently assumes 1 digit fd. K for assignment.
				// Convert char to int with - 48
				if (isdigit(commands.at(i)[0])) fd = commands.at(i)[0] - 48; // num before operator

				delete[] commands.at(i);
				argv[i] = NULL;

				timeout = 2;	// do not push this or next arg
				rdts.v_opfd.push_back( pair<int,int>(I_REDIRECT, fd) ); // op, fd

				if ( !(i + 1 < commands.size()) ) {	// next arg must exist (filename)
					cerr << "rshell: syntax error at <\n";
					doExec = false;
					break;
				}
				rdts.v_ind.push_back(i + 1); // index
			} else if (strchr(c, '>') != NULL) {
				int fd = -1;
				// FIXME: Currently assumes 1 digit fd. K for assignment.
				// Convert char to int with - 48
				if (isdigit(commands.at(i)[0])) fd = commands.at(i)[0] - 48; // num before operator

				if (strstr(commands.at(i), ">>>") != NULL) {
					cerr << "rshell: syntax error at >\n";
					doExec = false;
					break;
				}
				else if (strstr(commands.at(i), ">>") != NULL)
					rdts.v_opfd.push_back( pair<int,int>(ORD_APPEND, fd) ); // op, fd
				else {
					rdts.v_opfd.push_back( pair<int,int>(O_REDIRECT, fd) ); // op, fd
//					c = commands.at(i - 1);
//					if (i - 1 < 0 || strcmp( c, ":" ) == 0) // should truncate file if :>
//						realcom.pop_back();
				}

				delete[] commands.at(i);
				argv[i] = NULL;

				timeout = 2;	// do not push this or next arg

				if ( !(i + 1 < commands.size() - 1) ) {	// next arg must exist (filename)
					cerr << "rshell: syntax error at <";
					exit(-1);
				}
				rdts.v_ind.push_back(i + 1); // index
			}

			if (timeout != 0) timeout--;
			else realcom.push_back(commands.at(i));
		}

		realcom.push_back(NULL);
		realcomsP.push_back(realcom);
		isPipe.push_back(false);

		// Test produces weird results but gdb says contents are correct...wth?
		//for (unsigned int i = 0; i < realcomsP.size(); i++) {
		//	cerr << i << ": ";
		//	for (unsigned int j = 0; j < realcomsP.at(i).size(); j++) {
		//		cerr << realcomsP.at(1).at(j) << ' ';
		//	}
		//	cerr << endl;
		//}
		//exit(0);

		/* Confusing as heck, let me explain!
		 * char const* means pointer to a constant char
		 * so char const* const means a const pointer to a const char
		 * and char const* const* means a const pointer to a const char pointer!
		 * (Read declarations from right to left to make it make sense -
		 *  char const* = POINTER (*) to a CONST CHAR)
		 */
		//char const* const* c_arr = &commands[0]; // even if Not necessary: keep as really cool discovery!

		if (doExec != false) doExec = setRdct(argv, rdts);
		//if (rdts.doO_Rdct) doExec = redirection(argv, rdts, STDOUT_FILENO);
		//if (rdts.doE_Rdct) doExec = redirection(argv, rdts, STDERR_FILENO);
		char ex[5] = "exit";
		int fdpipe[2];
		int savefdpipe[2];
		bool resetPipe = false;

		if (setPipe) {
			if (-1 == pipe(fdpipe)) {
				perror("pipe");
				exit(-1);
			}
		}

		signal(SIGINT, quit_sigHandler);

		for (unsigned int i = 0; doExec && i < realcomsP.size(); i++) {
			argv = &realcomsP.at(i).at(0); // initialize to char**

			if (strcmp(argv[0], ex) == 0) {
				comPr.deleteCStrings(commands); // Has heap allocated memory, always should be dealt with
				return 0; // EXIT command
			}

			int pid = fork();
			if (pid == -1) {	// Error in fork
				perror("error in fork");
				comPr.deleteCStrings(commands);
				exit(-1);
			} else if (pid == 0) {	// child Process
				signal(SIGINT, SIG_DFL);
				signal(SIGTSTP, SIG_DFL);

				if (0 == strcmp(argv[0], "cd")) exit(0); // cd: PARENT PROCESS
				else if (0 == strcmp(argv[0], "fg")) {	// fg
					int stoppedID = stoppedpids.size() ? stoppedpids.size() : 1; // default, most recent or 1
					int sizePids = stoppedpids.size();

					if (realcomsP.at(i).size() >= 2) {
						if (-1 != (stoppedID = toDigit(argv[1])) && sizePids >= stoppedID) {
							list<int>::iterator it = stoppedpids.begin(); // List has no middle of list accessor
							for (int i = 0; i < stoppedID; i++, it++);
							int pidStopped = *it;

							if (-1 == kill(pidStopped, SIGCONT))
								perror("rshell: fg"); // No such job
						} else cerr << "rshell: fg: " << argv[1] << ": no such job";
					} else {
						if (sizePids >= stoppedID) {
							if (-1 == kill(stoppedpids.back(), SIGCONT)) {
								string s = "rshell: fg";
								s +=  argv[1];
								perror(s.c_str()); // No such job
							}
						} else cerr << "rshell: fg: current: no such job";
					}
				}

				// If piping, this command outputs to pipe
				if (isPipe.at(i)) {
					// move output's fd
					if (-1 == (savefdpipe[1] = dup(STDOUT_FILENO))) {
						perror("dup w/ pipe");
						exit(-1);
					}
					// place pipe in output
					if (-1 == dup2(fdpipe[1], STDOUT_FILENO)) {
						perror("dup w/ pipe");
						exit(-1);
					}
				}

				char* cpathvar = getenv("PATH");
				if (cpathvar == NULL) {
					perror("getenv");
					exit(-1);
				}

				string pathvar = cpathvar;
				typedef boost::tokenizer<char_separator<char> > tokenizer;
				char rm_delim[2] = {':', '\0'}; // null is delim, not char for filtering
				char_separator<char> charSep(rm_delim); // should ignore only whitespace
				tokenizer parse(pathvar, charSep);

				errno = 0;

				for (tokenizer::iterator it = parse.begin(); it != parse.end(); it++) {
					string s = *it + "/";
					s += argv[0];
					if (-1 != execv(s.c_str(), argv)) {
						break;
					}
				}

				if (errno != 0) { // perror for execv
					// All children need to exit! (forgot this)
					if (errno == EACCES) { // Access denied
						perror(argv[0]);
					} else if (errno == ENOEXEC) { // Not Executable
						perror(argv[0]);
					} else if (errno == ENOENT) { // Does not exist
						perror(argv[0]);
					} else {
						perror("error in execv");
					}

					exit(-1);
				}

				// if last command piped, next command's input is from pipe
				if (isPipe.at(i)) {
					// put output back in place
					if (-1 == dup2(savefdpipe[1],STDOUT_FILENO)) {
						perror("dup w/ pipe");
						exit(-1);
					}
					// close newly dup'ed output
					if (-1 == (close(savefdpipe[1]))) {
						perror("close w/ pipe");
						exit(-1);
					}
					// save input
					if (-1 == (savefdpipe[0] = dup(STDIN_FILENO))) {
						perror("dup w/ pipe");
						exit(-1);
					}
					// put pipe into input
					if (-1 == dup2(fdpipe[0],STDIN_FILENO)) {
						perror("dup w/ pipe");
						exit(-1);
					}

					resetPipe = true;
				} else if (resetPipe) {
					// put input back in place
					if (-1 == dup2(savefdpipe[0],STDIN_FILENO)) {
						perror("dup w/ pipe");
						exit(-1);
					}
					// close newly dup'ed input
					if (-1 == (close(savefdpipe[0]))) {
						perror("dup w/ pipe");
						exit(-1);
					}

					resetPipe = false;
				}

				exit(0);
			} else if (pid > 0) { // parent!
				int status;

				// Ctrl Z: WUNTRACED is an option that lets the parent wait for processes that
				// have stopped too, not just terminated.
				// If stopped, add its pid to a queue of stopped pids and use kill(pid, SIGCONT)
				// to continue it if user enters fg or bg (don't wait if bg, but keep process in queue)
				if (-1 == waitpid(pid, &status, WUNTRACED))
					perror("wait");
				if (errno != 0 && errno != EACCES && errno != ENOEXEC && errno != ENOENT)
					exit(-1);

				if (0 == strcmp(argv[0], "cd")) { // cd: PARENT PROCESS
					if (realcomsP.at(i).size() < 2 || -1 == chdir(argv[1])) // need a param (others ignored)
						perror("rshell: cd");
				}
				if (WIFSTOPPED(status)) {
					stoppedpids.push_back(pid);
					cout << "[" << stoppedpids.size() << "]+ Stopped\t\t" << argv[0] << endl;
				}
			}
		}

		if (setPipe) {
			for (int i = 0; i < 2; i++) {
				if (-1 == close(fdpipe[i])) {
					perror("close pipe");
					exit(-1);
				}
			}
		}

		for (unsigned int i = 0; i < rdts.v_savedP.size(); i++) {
			if (-1 == dup2(rdts.v_savedP.at(i), rdts.v_pfd.at(i))) {	// Restore saved fds
				perror("dup2");
				exit(-1);
			}
			if (-1 == close(rdts.v_savedP.at(i))) {	// Restore saved fds
				perror("close");
				exit(-1);
			}
		}

		comPr.deleteCStrings(commands);
		//rdts.closeCurrFDs();
	}

	return 0;
}

<commit_msg>Finished fg implementation<commit_after>#include <iostream>
#include <boost/tokenizer.hpp>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <list>
#include "comParse.h"
#include "redirect.h"
#include "digit.h"

using namespace boost;
using namespace std;

void input_sigHandler(int param) {
	if (param == SIGINT) {
		char currentDir[512];
		if (NULL == (getcwd(currentDir, 512))) { // Currently outputs error that directory is too long. Don't like this FIXME
			perror("getcwd");
			currentDir[0] = '\0';
		}
		cout << '\n' << currentDir << " $ " << flush;
	}
	if (param == SIGTSTP) {}
}

void quit_sigHandler(int param) {
	if (param == SIGINT) {
		cout << endl;
	}
}

int main() {
	list<int> stopped_pids;
	signal (SIGTSTP, input_sigHandler); // Ctrl C

	while (true) {
		signal(SIGINT, input_sigHandler);
		char** argv = NULL;
		vector<char*> commands;
		comParse comPr;

		char currentDir[512]; // Current Working Directory
		if (NULL == (getcwd(currentDir, 512))) { // Currently outputs error that directory is too long. Don't like this FIXME
			perror("getcwd");
			currentDir[0] = '\0';
		}

		do {
			cout << currentDir << " $ " << flush;

			string input = "";
			getline(cin, input);	// Get command

			commands = comPr.parseLine(input);
		} while (commands.size() == 1); // if empty line, repeat req for command. sz 1 = just NULL delim.

		bool doExec = true; // If false, do not fork and exec command

		//MASSIVE SECTION OF CODE DEDICATED TO PARSING FOR SEMI, PIPES
		//-------------------------------------------------------------------------
		int timeout = 0;
		vector<char*> realcom; // command + arguments - input redirection & semicolon
		vector< vector<char*> > realcomsP;
		vector<bool> isPipe;
		bool setPipe = false; // pipe will only be made if needed
		argv = &commands.at(0);
		redirect rdts;	// contains redirection flags and values

		for (unsigned int i = 0; i < commands.size() - 1; i++) { // NULL at end
			char* c = commands.at(i);
			// if semicolon or pipe, split command into two
			if ( strcmp(commands.at(i), ";") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;
				timeout = 1;

				realcom.push_back(NULL);
				realcomsP.push_back(realcom);
				realcom.clear();
				isPipe.push_back(false);
			} else if (strcmp(c, "|") == 0) {
				delete[] commands.at(i);
				argv[i] = NULL;
				timeout = 1;

				realcom.push_back(NULL);
				realcomsP.push_back(realcom);
				realcom.clear();
				isPipe.push_back(true);

				setPipe = true;
			} else if (strchr(c, '<') != NULL) {
				int fd = -1;
				// FIXME: Currently assumes 1 digit fd. K for assignment.
				// Convert char to int with - 48
				if (isdigit(commands.at(i)[0])) fd = commands.at(i)[0] - 48; // num before operator

				delete[] commands.at(i);
				argv[i] = NULL;

				timeout = 2;	// do not push this or next arg
				rdts.v_opfd.push_back( pair<int,int>(I_REDIRECT, fd) ); // op, fd

				if ( !(i + 1 < commands.size()) ) {	// next arg must exist (filename)
					cerr << "rshell: syntax error at <\n";
					doExec = false;
					break;
				}
				rdts.v_ind.push_back(i + 1); // index
			} else if (strchr(c, '>') != NULL) {
				int fd = -1;
				// FIXME: Currently assumes 1 digit fd. K for assignment.
				// Convert char to int with - 48
				if (isdigit(commands.at(i)[0])) fd = commands.at(i)[0] - 48; // num before operator

				if (strstr(commands.at(i), ">>>") != NULL) {
					cerr << "rshell: syntax error at >\n";
					doExec = false;
					break;
				}
				else if (strstr(commands.at(i), ">>") != NULL)
					rdts.v_opfd.push_back( pair<int,int>(ORD_APPEND, fd) ); // op, fd
				else {
					rdts.v_opfd.push_back( pair<int,int>(O_REDIRECT, fd) ); // op, fd
//					c = commands.at(i - 1);
//					if (i - 1 < 0 || strcmp( c, ":" ) == 0) // should truncate file if :>
//						realcom.pop_back();
				}

				delete[] commands.at(i);
				argv[i] = NULL;

				timeout = 2;	// do not push this or next arg

				if ( !(i + 1 < commands.size() - 1) ) {	// next arg must exist (filename)
					cerr << "rshell: syntax error at <";
					exit(-1);
				}
				rdts.v_ind.push_back(i + 1); // index
			}

			if (timeout != 0) timeout--;
			else realcom.push_back(commands.at(i));
		}

		realcom.push_back(NULL);
		realcomsP.push_back(realcom);
		isPipe.push_back(false);

		// Test produces weird results but gdb says contents are correct...wth?
		//for (unsigned int i = 0; i < realcomsP.size(); i++) {
		//	cerr << i << ": ";
		//	for (unsigned int j = 0; j < realcomsP.at(i).size(); j++) {
		//		cerr << realcomsP.at(1).at(j) << ' ';
		//	}
		//	cerr << endl;
		//}
		//exit(0);

		/* Confusing as heck, let me explain!
		 * char const* means pointer to a constant char
		 * so char const* const means a const pointer to a const char
		 * and char const* const* means a const pointer to a const char pointer!
		 * (Read declarations from right to left to make it make sense -
		 *  char const* = POINTER (*) to a CONST CHAR)
		 */
		//char const* const* c_arr = &commands[0]; // even if Not necessary: keep as really cool discovery!

		if (doExec != false) doExec = setRdct(argv, rdts);
		//if (rdts.doO_Rdct) doExec = redirection(argv, rdts, STDOUT_FILENO);
		//if (rdts.doE_Rdct) doExec = redirection(argv, rdts, STDERR_FILENO);
		char ex[5] = "exit";
		int fdpipe[2];
		int savefdpipe[2];
		bool resetPipe = false;

		if (setPipe) {
			if (-1 == pipe(fdpipe)) {
				perror("pipe");
				exit(-1);
			}
		}

		signal(SIGINT, quit_sigHandler);

		for (unsigned int i = 0; doExec && i < realcomsP.size(); i++) {
			argv = &realcomsP.at(i).at(0); // initialize to char**

			if (strcmp(argv[0], ex) == 0) {
				comPr.deleteCStrings(commands); // Has heap allocated memory, always should be dealt with
				return 0; // EXIT command
			}

			int pid = fork();
			if (pid == -1) {	// Error in fork
				perror("error in fork");
				comPr.deleteCStrings(commands);
				exit(-1);
			} else if (pid == 0) {	// child Process
				// cd/fg (Do in parent process)
				if (0 == strcmp(argv[0], "cd") || 0 == strcmp(argv[0], "fg")) exit(0);

				// Default signals for ctrl z, ctrl c
				signal(SIGINT, SIG_DFL);
				signal(SIGTSTP, SIG_DFL);

				// If piping, this command outputs to pipe
				if (isPipe.at(i)) {
					// move output's fd
					if (-1 == (savefdpipe[1] = dup(STDOUT_FILENO))) {
						perror("dup w/ pipe");
						exit(-1);
					}
					// place pipe in output
					if (-1 == dup2(fdpipe[1], STDOUT_FILENO)) {
						perror("dup w/ pipe");
						exit(-1);
					}
				}

				char* cpathvar = getenv("PATH");
				if (cpathvar == NULL) {
					perror("getenv");
					exit(-1);
				}

				string pathvar = cpathvar;
				typedef boost::tokenizer<char_separator<char> > tokenizer;
				char rm_delim[2] = {':', '\0'}; // null is delim, not char for filtering
				char_separator<char> charSep(rm_delim); // should ignore only whitespace
				tokenizer parse(pathvar, charSep);

				errno = 0;

				for (tokenizer::iterator it = parse.begin(); it != parse.end(); it++) {
					string s = *it + "/";
					s += argv[0];
					if (-1 != execv(s.c_str(), argv)) {
						break;
					}
				}

				if (errno != 0) { // perror for execv
					// All children need to exit! (forgot this)
					if (errno == EACCES) { // Access denied
						perror(argv[0]);
					} else if (errno == ENOEXEC) { // Not Executable
						perror(argv[0]);
					} else if (errno == ENOENT) { // Does not exist
						perror(argv[0]);
					} else {
						perror("error in execv");
					}

					exit(-1);
				}

				// if last command piped, next command's input is from pipe
				if (isPipe.at(i)) {
					// put output back in place
					if (-1 == dup2(savefdpipe[1],STDOUT_FILENO)) {
						perror("dup w/ pipe");
						exit(-1);
					}
					// close newly dup'ed output
					if (-1 == (close(savefdpipe[1]))) {
						perror("close w/ pipe");
						exit(-1);
					}
					// save input
					if (-1 == (savefdpipe[0] = dup(STDIN_FILENO))) {
						perror("dup w/ pipe");
						exit(-1);
					}
					// put pipe into input
					if (-1 == dup2(fdpipe[0],STDIN_FILENO)) {
						perror("dup w/ pipe");
						exit(-1);
					}

					resetPipe = true;
				} else if (resetPipe) {
					// put input back in place
					if (-1 == dup2(savefdpipe[0],STDIN_FILENO)) {
						perror("dup w/ pipe");
						exit(-1);
					}
					// close newly dup'ed input
					if (-1 == (close(savefdpipe[0]))) {
						perror("dup w/ pipe");
						exit(-1);
					}

					resetPipe = false;
				}

				exit(0);
			} else if (pid > 0) { // parent!

				int status;

				// Ctrl Z: WUNTRACED is an option that lets the parent wait for processes that
				// have stopped too, not just terminated.
				// If stopped, add its pid to a queue of stopped pids and use kill(pid, SIGCONT)
				// to continue it if user enters fg or bg (don't wait if bg, but keep process in queue)
				if (-1 == waitpid(pid, &status, WUNTRACED))
					perror("wait");
				if (errno != 0 && errno != EACCES && errno != ENOEXEC && errno != ENOENT)
					exit(-1);
				if (WIFSTOPPED(status)) {
					stopped_pids.push_back(pid);
					cout << "[" << stopped_pids.size() << "]+ Stopped\t\t" << argv[0] << endl;
				}

				// cd
				if (0 == strcmp(argv[0], "cd")) { // cd: PARENT PROCESS
					if (realcomsP.at(i).size() < 2 || -1 == chdir(argv[1])) // need a param (others ignored)
						perror("rshell: cd");
				}

				// fg
				int waiting_pid = -1;
				if (0 == strcmp(argv[0], "fg")) {
					if (stopped_pids.empty()) {
						cerr << "rshell: fg: " << argv[1] << ": no such job";
						exit(-1);
					}
					int stoppedID = stopped_pids.size() ? stopped_pids.size() : 1; // default, most recent or 1
					int sizePids = stopped_pids.size();

					//cout << endl << "stoppedID = " << stoppedID << endl
					//	<< "sizePids = " << sizePids << endl;

					// If fg has argument (id of stopped process)
					// vector composed of <fg> <arg> <null> (at least 3)
					if (realcomsP.at(i).size() > 2) {
						if (-1 != (stoppedID = toDigit(argv[1])) && sizePids >= stoppedID) {

							// get element at stoppedID - 1
							list<int>::iterator it = stopped_pids.begin();
							for (int i = 0; i < stoppedID - 1; i++, it++);
							int pidStopped = *it;

							// continue process, wait
							if (-1 != kill(pidStopped, SIGCONT)) {
								waiting_pid = pidStopped;
								stopped_pids.erase(it);
							} else perror("rshell: fg"); // No such job

						} else cerr << "rshell: fg: " << argv[1] << ": no such job";
					}

					// Default fg
					else {
						// continue process, wait
						if (-1 != kill(stopped_pids.back(), SIGCONT)) {
							waiting_pid = stopped_pids.back();
							stopped_pids.pop_back();
						} else {
							string s = "rshell: fg";
							s +=  argv[1];
							perror(s.c_str()); // No such job
						}
					}
				}

				if (waiting_pid != -1) {
					if (-1 == waitpid(waiting_pid, &status, WUNTRACED))
						perror("wait");
					if (WIFSTOPPED(status)) {
						stopped_pids.push_back(pid);
						cout << "[" << stopped_pids.size() << "]+ Stopped\t\t" << argv[0] << endl;
					}
				}
			}
		}

		if (setPipe) {
			for (int i = 0; i < 2; i++) {
				if (-1 == close(fdpipe[i])) {
					perror("close pipe");
					exit(-1);
				}
			}
		}

		for (unsigned int i = 0; i < rdts.v_savedP.size(); i++) {
			if (-1 == dup2(rdts.v_savedP.at(i), rdts.v_pfd.at(i))) {	// Restore saved fds
				perror("dup2");
				exit(-1);
			}
			if (-1 == close(rdts.v_savedP.at(i))) {	// Restore saved fds
				perror("close");
				exit(-1);
			}
		}

		comPr.deleteCStrings(commands);
		//rdts.closeCurrFDs();
	}

	return 0;
}

<|endoftext|>


<commit_before>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <numeric>
#include <sched.h>
#include <string.h>
#include <sstream>
#include <sys/mman.h>
#include <sys/rusage.h>
#include <vector>

#include <rttest.h>

// Global variables
struct rttest_params _rttest_params;
struct rttest_sample_buffer _rttest_sample_buffer;
struct rttest_results _rttest_results;

static inline bool timespec_gt(const struct timespec *t1,
    const struct timespec *t2)
{
  if (t1->tv_sec > t2->tv_sec)
  {
    return true;
  }
  if (t1->tv_sec < t2->tv_sec)
  {
    return false;
  }
  return t1->tv_nsec > t2->tv_nsec;
}

static inline void normalize_timespec(struct timespec *t)
{
  // TODO: maybe could use some work
  while (t->tv_nsec >= NSEC_PER_SEC) {
    t->tv_nsec -= NSEC_PER_SEC;
    t->tv_sec++;
  }
}

static inline void add_timespecs(const struct timespec *t1,
    const struct timespec *t2,
    struct timespec *dst)
{
  dst->tv_sec = t1->tv_sec + t2->tv_sec;
  dst->tv_nsec = t1->tv_nsec + t2->tv_nsec;
  normalize_timespec(dst);
}

static inline bool subtract_timespecs(const struct timespec *t1,
    const struct timespec *t2,
    struct timespec *dst)
{
  if (timespec_gt(t2, t1))
  {
    return subtract_timespecs(t2, t1, dst);
  }

  dst->tv_sec = t1->tv_sec - t2->tv_sec;
  dst->tv_nsec = t1->tv_nsec - t2->tv_nsec;

  normalize_timespec(dst);
  return true;
}

static inline unsigned long timespec_to_long(const struct timespec *t)
{
  return t->tv_sec * NSEC_PER_SEC + t->tv_nsec;
}

int rttest_record_missed_deadline(struct timespec *deadline,
    struct timespec *result_time, int iteration)
{
  struct timespec jitter;
  subtract_timespecs(result_time, deadline, &jitter);
  // Record jitter
  if (iteration > _rttest_sample_buffer.buffer_size)
  {
    return -1;
  }
  _rttest_sample_buffer.missed_deadlines[iteration] = true;
  _rttest_sample_buffer.latency_samples[iteration] = -timespec_to_long(&jitter);
  return 0;
}

int rttest_record_jitter(struct timespec *deadline,
    struct timespec *result_time, int iteration)
{
  if (timespec_gt(result_time, deadline))
  {
    // missed a deadline
    return rttest_record_missed_deadline(deadline, result_time, iteration);
  }
  struct timespec jitter;
  subtract_timespecs(deadline, result_time, &jitter);
  // Record jitter
  if (iteration > _rttest_sample_buffer.buffer_size)
  {
    return -1;
  }
  _rttest_sample_buffer.missed_deadlines[iteration] = false;
  _rttest_sample_buffer.latency_samples[iteration] = timespec_to_long(&jitter);
  return 0;
}

int rttest_read_args(int argc, char** argv)
{
  //parse arguments
}

int rttest_init(unsigned long iterations, struct timespec update_period,
    size_t sched_policy, int sched_priority, int lock_memory, size_t stack_size,
    int plot, int write, char *filename)
{
  _rttest_params.iterations = iterations;
  _rttest_params.update_period = update_period;
  _rttest_params.sched_policy = sched_policy;
  _rttest_params.sched_priority = sched_priority;
  _rttest_params.lock_memory = lock_memory;
  _rttest_params.stack_size = stack_size;
  _rttest_params.plot = plot;
  _rttest_params.write = write;
  _rttest_params.filename = filename;

  _rttest_sample_buffer.latency_samples =
      (long *) std::malloc(iterations*sizeof(unsigned long));
  memset(_rttest_sample_buffer.latency_samples, 0,
      iterations*sizeof(long));
  _rttest_sample_buffer.missed_deadlines =
      (bool *) std::malloc(iterations*sizeof(bool));
  memset(_rttest_sample_buffer.missed_deadlines, 0, iterations*sizeof(bool));
  _rttest_sample_buffer.buffer_size = iterations;
}

int rttest_spin(void *(*user_function)(void *), void *args)
{
  return rttest_spin_period(user_function, args, &_rttest_params.update_period,
      _rttest_params.iterations);
}

int rttest_spin_period(void *(*user_function)(void *), void *args,
    const struct timespec *update_period, const unsigned long iterations)
{
  struct timespec wakeup_time, current_time;
  clock_gettime(0, &current_time);
  wakeup_time = current_time;

  for (int i = 0; i < iterations; i++)
  {
    // Plan the next shot
    add_timespecs(&wakeup_time, update_period, &wakeup_time);
    clock_gettime(0, &current_time);
    if (timespec_gt(&current_time, &wakeup_time))
    {
      // Missed a deadline before we could sleep! Record it
      rttest_record_missed_deadline(&wakeup_time, &current_time, i);
    }
    else
    {
      clock_nanosleep(0, TIMER_ABSTIME, &wakeup_time, NULL);
      clock_gettime(0, &current_time);

      rttest_record_jitter(&wakeup_time, &current_time, i);
    }

    user_function(args);
  }

  return 0;
}

int rttest_schedule_wakeup(void *(*user_function)(void *), void *args,
    struct timespec absolute_wakeup)
{
  struct timespec current_time;
  clock_gettime(0, &current_time);
  if (timespec_gt(&current_time, &absolute_wakeup))
  {
    // Missed a deadline before we could sleep! Record it
    rttest_record_missed_deadline(&absolute_wakeup, &current_time, -1);
  }
  else
  {
    clock_nanosleep(0, TIMER_ABSTIME, &absolute_wakeup, NULL);
    clock_gettime(0, &current_time);

    rttest_record_jitter(&absolute_wakeup, &current_time, -1);
  }

  user_function(args);

  return 0;
}

int rttest_lock_memory()
{
  return mlockall(MCL_CURRENT | MCL_FUTURE);
}

int rttest_prefault_stack_size(const size_t stack_size)
{
  unsigned char stack[stack_size];
  memset(stack, 0, stack_size);
  // TODO: catch errors, maybe verify memset return value points to stack?
  return 0;
}

int rttest_prefault_stack()
{
  return rttest_prefault_stack_size(_rttest_params.stack_size);
}

int rttest_set_thread_default_priority()
{
  return rttest_set_sched_priority(_rttest_params.sched_priority,
      _rttest_params.sched_policy);
}

int rttest_set_sched_priority(size_t sched_priority, int policy)
{
  struct sched_param param;

  param.sched_priority = sched_priority;

  // note that sched_setscheduler can set the priority of an arbitrary process
  return sched_setscheduler(0, policy, &param);
}

int rttest_calculate_statistics(struct rttest_results *results)
{
  if (results == NULL)
  {
    return -1;
  }

  std::vector<long> jitter_dataset;
  jitter_dataset.assign(_rttest_sample_buffer.latency_samples,
      _rttest_sample_buffer.latency_samples + _rttest_sample_buffer.buffer_size);

  std::vector<long> latency_dataset(jitter_dataset.size());
  std::copy_if(jitter_dataset.begin(), jitter_dataset.end(),
      latency_dataset.begin(),
      [](long sample){ return sample < 0; } );

  std::vector<bool> missed_deadlines_data;
  missed_deadlines_data.assign(_rttest_sample_buffer.missed_deadlines,
      _rttest_sample_buffer.missed_deadlines + _rttest_sample_buffer.buffer_size);

  results->min_latency = *std::min_element(latency_dataset.begin(), latency_dataset.end());
  results->max_latency = *std::max_element(latency_dataset.begin(), latency_dataset.end());
  results->mean_latency = std::accumulate(latency_dataset.begin(),
      latency_dataset.end(), 0.0) / latency_dataset.size();
  double sq_sum = std::inner_product(latency_dataset.begin(), latency_dataset.end(),
      latency_dataset.begin(), 0.0);
  results->latency_stddev =
      std::sqrt(sq_sum / latency_dataset.size() - results->mean_latency * results->mean_latency);

  results->min_jitter = *std::min_element(jitter_dataset.begin(), jitter_dataset.end());
  results->max_jitter = *std::max_element(jitter_dataset.begin(), jitter_dataset.end());
  results->mean_jitter = std::accumulate(jitter_dataset.begin(),
      jitter_dataset.end(), 0.0) / jitter_dataset.size();
  sq_sum = std::inner_product(jitter_dataset.begin(), jitter_dataset.end(),
      jitter_dataset.begin(), 0.0);
  results->jitter_stddev =
      std::sqrt(sq_sum / jitter_dataset.size() - results->mean_jitter * results->mean_jitter);

  results->missed_deadlines = std::count(missed_deadlines_data.begin(),
      missed_deadlines_data.end(), true);
  results->early_deadlines = missed_deadlines_data.size() - results->missed_deadlines;
  return 0;
}

std::string rttest_results_to_string(struct rttest_results *results)
{
  if (!results)
  {
    return "ERROR: rttest got NULL results string!";
  }
  std::stringstream sstring;

  sstring << "rttest statistics:" << std::endl;
  sstring << "  - Missed deadlines: " << results->missed_deadlines << std::endl;
  sstring << "  - Early deadlines: " << results->early_deadlines << std::endl;
  sstring << std::endl;
  sstring << "  Latency (time after deadline was missed):" << std::endl;
  sstring << "    - Min: " << results->min_latency << std::endl;
  sstring << "    - Max: " << results->max_latency << std::endl;
  sstring << "    - Mean: " << results->mean_latency << std::endl;
  sstring << "    - Standard deviation: " << results->latency_stddev << std::endl;
  sstring << std::endl;
  sstring << "  Jitter (scheduling variation for early or missed deadlines):" << std::endl;
  sstring << "    - Min: " << results->min_jitter << std::endl;
  sstring << "    - Max: " << results->max_jitter << std::endl;
  sstring << "    - Mean: " << results->mean_jitter << std::endl;
  sstring << "    - Standard deviation: " << results->jitter_stddev << std::endl;

  return sstring.str();
}

int rttest_finish()
{
  // Print statistics to screen
  rttest_calculate_statistics(&_rttest_results);
  std::cout << rttest_results_to_string(&_rttest_results);

  if (_rttest_sample_buffer.latency_samples != NULL)
  {
    free(_rttest_sample_buffer.latency_samples);
  }

  if (_rttest_sample_buffer.missed_deadlines != NULL)
  {
    free(_rttest_sample_buffer.missed_deadlines);
  }
  return 0;
}

int rttest_write_results()
{
  // Format:
  // iteration  timestamp  latency  missed_deadline? (1/0)

  if (!_rttest_params.write)
  {
    return -1;
  }

  if (_rttest_sample_buffer.latency_samples == NULL)
  {
    return -1;
  }

  if (_rttest_sample_buffer.missed_deadlines == NULL)
  {
    return -1;
  }

  return 0;
}

int rttest_plot()
{
  if (!_rttest_params.plot)
  {
    return -1;
  }
  return 0;
}
<commit_msg>Implemented file writing<commit_after>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <numeric>
#include <fstream>
#include <sched.h>
#include <string.h>
#include <sstream>
#include <sys/mman.h>
#include <sys/rusage.h>
#include <vector>

#include <rttest.h>

// Global variables
struct rttest_params _rttest_params;
struct rttest_sample_buffer _rttest_sample_buffer;
struct rttest_results _rttest_results;

static inline bool timespec_gt(const struct timespec *t1,
    const struct timespec *t2)
{
  if (t1->tv_sec > t2->tv_sec)
  {
    return true;
  }
  if (t1->tv_sec < t2->tv_sec)
  {
    return false;
  }
  return t1->tv_nsec > t2->tv_nsec;
}

static inline void normalize_timespec(struct timespec *t)
{
  // TODO: maybe could use some work
  while (t->tv_nsec >= NSEC_PER_SEC) {
    t->tv_nsec -= NSEC_PER_SEC;
    t->tv_sec++;
  }
}

static inline void add_timespecs(const struct timespec *t1,
    const struct timespec *t2,
    struct timespec *dst)
{
  dst->tv_sec = t1->tv_sec + t2->tv_sec;
  dst->tv_nsec = t1->tv_nsec + t2->tv_nsec;
  normalize_timespec(dst);
}

static inline bool subtract_timespecs(const struct timespec *t1,
    const struct timespec *t2,
    struct timespec *dst)
{
  if (timespec_gt(t2, t1))
  {
    return subtract_timespecs(t2, t1, dst);
  }

  dst->tv_sec = t1->tv_sec - t2->tv_sec;
  dst->tv_nsec = t1->tv_nsec - t2->tv_nsec;

  normalize_timespec(dst);
  return true;
}

static inline unsigned long timespec_to_long(const struct timespec *t)
{
  return t->tv_sec * NSEC_PER_SEC + t->tv_nsec;
}

int rttest_record_missed_deadline(struct timespec *deadline,
    struct timespec *result_time, int iteration)
{
  struct timespec jitter;
  subtract_timespecs(result_time, deadline, &jitter);
  // Record jitter
  if (iteration > _rttest_sample_buffer.buffer_size)
  {
    return -1;
  }
  _rttest_sample_buffer.missed_deadlines[iteration] = true;
  _rttest_sample_buffer.latency_samples[iteration] = -timespec_to_long(&jitter);
  return 0;
}

int rttest_record_jitter(struct timespec *deadline,
    struct timespec *result_time, int iteration)
{
  if (timespec_gt(result_time, deadline))
  {
    // missed a deadline
    return rttest_record_missed_deadline(deadline, result_time, iteration);
  }
  struct timespec jitter;
  subtract_timespecs(deadline, result_time, &jitter);
  // Record jitter
  if (iteration > _rttest_sample_buffer.buffer_size)
  {
    return -1;
  }
  _rttest_sample_buffer.missed_deadlines[iteration] = false;
  _rttest_sample_buffer.latency_samples[iteration] = timespec_to_long(&jitter);
  return 0;
}

int rttest_read_args(int argc, char** argv)
{
  //parse arguments
}

int rttest_init(unsigned long iterations, struct timespec update_period,
    size_t sched_policy, int sched_priority, int lock_memory, size_t stack_size,
    int plot, int write, char *filename)
{
  _rttest_params.iterations = iterations;
  _rttest_params.update_period = update_period;
  _rttest_params.sched_policy = sched_policy;
  _rttest_params.sched_priority = sched_priority;
  _rttest_params.lock_memory = lock_memory;
  _rttest_params.stack_size = stack_size;
  _rttest_params.plot = plot;
  _rttest_params.write = write;
  _rttest_params.filename = filename;

  _rttest_sample_buffer.latency_samples =
      (long *) std::malloc(iterations*sizeof(unsigned long));
  memset(_rttest_sample_buffer.latency_samples, 0,
      iterations*sizeof(long));
  _rttest_sample_buffer.missed_deadlines =
      (bool *) std::malloc(iterations*sizeof(bool));
  memset(_rttest_sample_buffer.missed_deadlines, 0, iterations*sizeof(bool));
  _rttest_sample_buffer.buffer_size = iterations;

  return 0;
}

int rttest_spin(void *(*user_function)(void *), void *args)
{
  return rttest_spin_period(user_function, args, &_rttest_params.update_period,
      _rttest_params.iterations);
}

int rttest_spin_period(void *(*user_function)(void *), void *args,
    const struct timespec *update_period, const unsigned long iterations)
{
  struct timespec wakeup_time, current_time;
  clock_gettime(0, &current_time);
  wakeup_time = current_time;

  for (int i = 0; i < iterations; i++)
  {
    // Plan the next shot
    add_timespecs(&wakeup_time, update_period, &wakeup_time);
    clock_gettime(0, &current_time);
    if (timespec_gt(&current_time, &wakeup_time))
    {
      // Missed a deadline before we could sleep! Record it
      rttest_record_missed_deadline(&wakeup_time, &current_time, i);
    }
    else
    {
      clock_nanosleep(0, TIMER_ABSTIME, &wakeup_time, NULL);
      clock_gettime(0, &current_time);

      rttest_record_jitter(&wakeup_time, &current_time, i);
    }

    user_function(args);
  }

  return 0;
}

int rttest_schedule_wakeup(void *(*user_function)(void *), void *args,
    struct timespec absolute_wakeup)
{
  struct timespec current_time;
  clock_gettime(0, &current_time);
  if (timespec_gt(&current_time, &absolute_wakeup))
  {
    // Missed a deadline before we could sleep! Record it
    rttest_record_missed_deadline(&absolute_wakeup, &current_time, -1);
  }
  else
  {
    clock_nanosleep(0, TIMER_ABSTIME, &absolute_wakeup, NULL);
    clock_gettime(0, &current_time);

    rttest_record_jitter(&absolute_wakeup, &current_time, -1);
  }

  user_function(args);

  return 0;
}

int rttest_lock_memory()
{
  return mlockall(MCL_CURRENT | MCL_FUTURE);
}

int rttest_prefault_stack_size(const size_t stack_size)
{
  unsigned char stack[stack_size];
  memset(stack, 0, stack_size);
  // TODO: catch errors, maybe verify memset return value points to stack?
  return 0;
}

int rttest_prefault_stack()
{
  return rttest_prefault_stack_size(_rttest_params.stack_size);
}

int rttest_set_thread_default_priority()
{
  return rttest_set_sched_priority(_rttest_params.sched_priority,
      _rttest_params.sched_policy);
}

int rttest_set_sched_priority(size_t sched_priority, int policy)
{
  struct sched_param param;

  param.sched_priority = sched_priority;

  // note that sched_setscheduler can set the priority of an arbitrary process
  return sched_setscheduler(0, policy, &param);
}

int rttest_calculate_statistics(struct rttest_results *results)
{
  if (results == NULL)
  {
    return -1;
  }

  std::vector<long> jitter_dataset;
  jitter_dataset.assign(_rttest_sample_buffer.latency_samples,
      _rttest_sample_buffer.latency_samples + _rttest_sample_buffer.buffer_size);

  std::vector<long> latency_dataset(jitter_dataset.size());
  std::copy_if(jitter_dataset.begin(), jitter_dataset.end(),
      latency_dataset.begin(),
      [](long sample){ return sample < 0; } );

  std::vector<bool> missed_deadlines_data;
  missed_deadlines_data.assign(_rttest_sample_buffer.missed_deadlines,
      _rttest_sample_buffer.missed_deadlines + _rttest_sample_buffer.buffer_size);

  results->min_latency = *std::min_element(latency_dataset.begin(), latency_dataset.end());
  results->max_latency = *std::max_element(latency_dataset.begin(), latency_dataset.end());
  results->mean_latency = std::accumulate(latency_dataset.begin(),
      latency_dataset.end(), 0.0) / latency_dataset.size();
  double sq_sum = std::inner_product(latency_dataset.begin(), latency_dataset.end(),
      latency_dataset.begin(), 0.0);
  results->latency_stddev =
      std::sqrt(sq_sum / latency_dataset.size() - results->mean_latency * results->mean_latency);

  results->min_jitter = *std::min_element(jitter_dataset.begin(), jitter_dataset.end());
  results->max_jitter = *std::max_element(jitter_dataset.begin(), jitter_dataset.end());
  results->mean_jitter = std::accumulate(jitter_dataset.begin(),
      jitter_dataset.end(), 0.0) / jitter_dataset.size();
  sq_sum = std::inner_product(jitter_dataset.begin(), jitter_dataset.end(),
      jitter_dataset.begin(), 0.0);
  results->jitter_stddev =
      std::sqrt(sq_sum / jitter_dataset.size() - results->mean_jitter * results->mean_jitter);

  results->missed_deadlines = std::count(missed_deadlines_data.begin(),
      missed_deadlines_data.end(), true);
  results->early_deadlines = missed_deadlines_data.size() - results->missed_deadlines;
  return 0;
}

std::string rttest_results_to_string(struct rttest_results *results)
{
  if (!results)
  {
    return "ERROR: rttest got NULL results string!";
  }
  std::stringstream sstring;

  sstring << "rttest statistics:" << std::endl;
  sstring << "  - Missed deadlines: " << results->missed_deadlines << std::endl;
  sstring << "  - Early deadlines: " << results->early_deadlines << std::endl;
  sstring << std::endl;
  sstring << "  Latency (time after deadline was missed):" << std::endl;
  sstring << "    - Min: " << results->min_latency << std::endl;
  sstring << "    - Max: " << results->max_latency << std::endl;
  sstring << "    - Mean: " << results->mean_latency << std::endl;
  sstring << "    - Standard deviation: " << results->latency_stddev << std::endl;
  sstring << std::endl;
  sstring << "  Jitter (scheduling variation for early or missed deadlines):" << std::endl;
  sstring << "    - Min: " << results->min_jitter << std::endl;
  sstring << "    - Max: " << results->max_jitter << std::endl;
  sstring << "    - Mean: " << results->mean_jitter << std::endl;
  sstring << "    - Standard deviation: " << results->jitter_stddev << std::endl;

  return sstring.str();
}

int rttest_finish()
{
  // Print statistics to screen
  rttest_calculate_statistics(&_rttest_results);
  std::cout << rttest_results_to_string(&_rttest_results);

  if (_rttest_sample_buffer.latency_samples != NULL)
  {
    free(_rttest_sample_buffer.latency_samples);
  }

  if (_rttest_sample_buffer.missed_deadlines != NULL)
  {
    free(_rttest_sample_buffer.missed_deadlines);
  }
  return 0;
}

int rttest_write_results()
{

  if (!_rttest_params.write)
  {
    return -1;
  }

  if (_rttest_sample_buffer.latency_samples == NULL)
  {
    return -1;
  }

  if (_rttest_sample_buffer.missed_deadlines == NULL)
  {
    return -1;
  }

  std::ofstream fstream(_rttest_params.filename, std::ios::out);

  if (!fstream.is_open())
  {
    return -1;
  }

  // Format:
  // iteration  timestamp (ns)  latency  missed_deadline? (1/0)
  fstream << "iteration timestamp latency missed_deadline" << std::endl;
  for (unsigned long i = 0; i < _rttest_sample_buffer.buffer_size; ++i)
  {
    fstream << i << " " << timespec_to_long(&_rttest_params.update_period) * i
            << " " << _rttest_sample_buffer.latency_samples[i] << " "
            << _rttest_sample_buffer.missed_deadlines[i] << std::endl;
  }

  fstream.close();

  return 0;
}

int rttest_plot()
{
  if (!_rttest_params.plot)
  {
    return -1;
  }
  return 0;
}
<|endoftext|>


<commit_before>/*****************************************************************************
*  Copyright 2011 Sergey Shekyan
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* *****************************************************************************/

/*****
 * Author: Sergey Shekyan sshekyan@qualys.com
 *
 * Slow HTTP attack  vulnerability test tool
 *  http://code.google.com/p/slowhttptest/
 *****/

#include <ctime>
#include <errno.h>
#include <execinfo.h>
#include <stdarg.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "slowlog.h"

namespace {
static FILE* log_file = NULL;
static FILE* csv_file = NULL;
static FILE* html_file = NULL;
int current_log_level;

void print_html_header() {
  fprintf(html_file,
      "\r\n \
      \r\n \
      \r\n \
      \r\n \
      \r\n \
      \r\n \

\r\n \
      \r\n \
      ");
}

void dispose_of_log(int param) {
  if (log_file && log_file != stdout) {
    fclose(log_file);
  }
  if(csv_file) {
    fclose(csv_file);
  }
  if(html_file) {
    print_html_footer();
    fflush(html_file);
    fclose(html_file);
  }
  exit(1);
}

void print_call_stack() {
  static void* buf[64];
  const int depth = backtrace(buf, sizeof(buf)/sizeof(buf[0]));
  backtrace_symbols_fd(buf, depth, fileno(stdout));
  if (stdout != log_file) {
    backtrace_symbols_fd(buf, depth, fileno(log_file));
  }
}
}

namespace slowhttptest {
void slowlog_init(int debug_level, const char* file_name, bool need_stats) {
  log_file = file_name == NULL ? stdout : fopen(file_name, "w");
  if(!log_file) {
    printf("Unable to open log file %s for writing: %s", file_name,
           strerror(errno));
  }
  if(need_stats) {
    time_t rawtime;
    struct tm * timeinfo;
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    char csv_file_name[32] = {0};
    char html_file_name[32] = {0};
    strftime(csv_file_name, 22, "slow_%H%M%Y%m%d.csv", timeinfo);
    strftime(html_file_name, 22, "slow_%H%M%Y%m%d.html", timeinfo);
    csv_file = fopen(csv_file_name , "w");
    if(!csv_file) {
      printf("Unable to open csv file %s for writing: %s\n",
             csv_file_name,
             strerror(errno));
    } else {
      fprintf(csv_file, "Seconds,Error,Closed,Pending,Connected\n");
    }
    html_file = fopen(html_file_name , "w");
    if(!html_file) {
      printf("Unable to open html file %s for writing: %s\n",
          html_file_name,
          strerror(errno));
    } else {
      print_html_header();
    }
  }
  signal(SIGINT, &dispose_of_log);
  current_log_level = debug_level;
}

void check(bool f, const char* message) {
  if (!f) {
    fprintf(log_file, "%s\n", message);
    fflush(log_file);
    print_call_stack();
    exit(1);
  }
}

void log_fatal(const char* format, ...) {
  const time_t  now = time(NULL);
  char ctimebuf[32];
  const char* buf = ctime_r(&now, ctimebuf);

  fprintf(log_file, "%-.24s FATAL:", buf);

  va_list va;
  va_start(va, format);
  vfprintf(log_file, format, va);
  va_end(va);
  fflush(log_file);
  print_call_stack();
  exit(1);
}

void dump_csv(const char* format, ...) {
  va_list va;
  va_start(va, format);
  vfprintf(csv_file, format, va);
  fflush(csv_file);
  va_end(va);
}

void dump_html(const char* format, ...) {
  va_list va;
  va_start(va, format);
  vfprintf(html_file, format, va);
  fflush(html_file);
  va_end(va);

}

void slowlog(int lvl, const char* format, ...) {
  if(lvl <= current_log_level) {
    const time_t now = time(NULL);
    char ctimebuf[32];
    const char* buf = ctime_r(&now, ctimebuf);

    fprintf(log_file, "%-.24s:", buf);

    va_list va;
    va_start(va, format);
    vfprintf(log_file, format, va);
    va_end(va);
  }
}

}  // namespace slowhttptest
<commit_msg>added atexit<commit_after>/*****************************************************************************
*  Copyright 2011 Sergey Shekyan
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* *****************************************************************************/

/*****
 * Author: Sergey Shekyan sshekyan@qualys.com
 *
 * Slow HTTP attack  vulnerability test tool
 *  http://code.google.com/p/slowhttptest/
 *****/

#include <ctime>
#include <errno.h>
#include <execinfo.h>
#include <stdarg.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "slowlog.h"

namespace {
static FILE* log_file = NULL;
static FILE* csv_file = NULL;
static FILE* html_file = NULL;
int current_log_level;

void print_html_header() {
  fprintf(html_file,
      "\r\n \
      \r\n \
      \r\n \
      \r\n \
      \r\n \
      \r\n \

\r\n \
      \r\n \
      ");
}

void dispose_of_log(int param) {
  if (log_file && log_file != stdout) {
    fclose(log_file);
  }
  if(csv_file) {
    fclose(csv_file);
  }
  if(html_file) {
    print_html_footer();
    fflush(html_file);
    fclose(html_file);
  }
  exit(1);
}

void wrapper_dispose_of_log() {
  dispose_of_log(0);
}

void print_call_stack() {
  static void* buf[64];
  const int depth = backtrace(buf, sizeof(buf)/sizeof(buf[0]));
  backtrace_symbols_fd(buf, depth, fileno(stdout));
  if (stdout != log_file) {
    backtrace_symbols_fd(buf, depth, fileno(log_file));
  }
}
}

namespace slowhttptest {
void slowlog_init(int debug_level, const char* file_name, bool need_stats) {
  log_file = file_name == NULL ? stdout : fopen(file_name, "w");
  if(!log_file) {
    printf("Unable to open log file %s for writing: %s", file_name,
           strerror(errno));
  }
  if(need_stats) {
    time_t rawtime;
    struct tm * timeinfo;
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    char csv_file_name[32] = {0};
    char html_file_name[32] = {0};
    strftime(csv_file_name, 22, "slow_%H%M%Y%m%d.csv", timeinfo);
    strftime(html_file_name, 22, "slow_%H%M%Y%m%d.html", timeinfo);
    csv_file = fopen(csv_file_name , "w");
    if(!csv_file) {
      printf("Unable to open csv file %s for writing: %s\n",
             csv_file_name,
             strerror(errno));
    } else {
      fprintf(csv_file, "Seconds,Error,Closed,Pending,Connected\n");
    }
    html_file = fopen(html_file_name , "w");
    if(!html_file) {
      printf("Unable to open html file %s for writing: %s\n",
          html_file_name,
          strerror(errno));
    } else {
      print_html_header();
    }
  }
  atexit(&wrapper_dispose_of_log);
  signal(SIGINT, &dispose_of_log);
  current_log_level = debug_level;
}

void check(bool f, const char* message) {
  if (!f) {
    fprintf(log_file, "%s\n", message);
    fflush(log_file);
    print_call_stack();
    exit(1);
  }
}

void log_fatal(const char* format, ...) {
  const time_t  now = time(NULL);
  char ctimebuf[32];
  const char* buf = ctime_r(&now, ctimebuf);

  fprintf(log_file, "%-.24s FATAL:", buf);

  va_list va;
  va_start(va, format);
  vfprintf(log_file, format, va);
  va_end(va);
  fflush(log_file);
  print_call_stack();
  exit(1);
}

void dump_csv(const char* format, ...) {
  va_list va;
  va_start(va, format);
  vfprintf(csv_file, format, va);
  fflush(csv_file);
  va_end(va);
}

void dump_html(const char* format, ...) {
  va_list va;
  va_start(va, format);
  vfprintf(html_file, format, va);
  fflush(html_file);
  va_end(va);

}

void slowlog(int lvl, const char* format, ...) {
  if(lvl <= current_log_level) {
    const time_t now = time(NULL);
    char ctimebuf[32];
    const char* buf = ctime_r(&now, ctimebuf);

    fprintf(log_file, "%-.24s:", buf);

    va_list va;
    va_start(va, format);
    vfprintf(log_file, format, va);
    va_end(va);
  }
}

}  // namespace slowhttptest
<|endoftext|>


<commit_before>/* socket.cpp                                 -*- C++ -*-
   Rémi Attab (remi.attab@gmail.com), 18 Nov 2013
   FreeBSD-style copyright and disclaimer apply

   Socket abstraction implementation
*/

#include "socket.h"
#include "utils.h"
#include "lockless/tls.h"

#include <array>
#include <string>
#include <cstring>
#include <cassert>
#include <netdb.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

namespace slick {

/******************************************************************************/
/* ADDRESS                                                                    */
/******************************************************************************/

Address::
Address(const std::string& hostPort)
{
    size_t pos = hostPort.find(':');
    assert(pos != std::string::npos);

    host = hostPort.substr(0, pos);
    port = stoi(hostPort.substr(pos + 1));
}

Address::
Address(struct sockaddr* addr)
{
    socklen_t addrlen;
    int family = addr->sa_family;

    if (family == AF_INET) addrlen = sizeof(struct sockaddr_in);
    else if (family == AF_INET6) addrlen = sizeof(struct sockaddr_in6);
    else assert(false);

    *this = Address(addr, addrlen);
}

Address::
Address(struct sockaddr* addr, socklen_t addrlen)
{
    std::array<char, 256> host;
    std::array<char, 256> service;

    int res = getnameinfo(
            addr, addrlen,
            host.data(), host.size(),
            service.data(), service.size(),
            NI_NUMERICHOST | NI_NUMERICSERV);
    SLICK_CHECK_ERRNO(!res, "Address.getnameinfo");

    this->host = std::string(host.data());
    this->port = atoi(service.data());
}

/******************************************************************************/
/* INTERFACE IT                                                               */
/******************************************************************************/

struct InterfaceIt
{
    InterfaceIt(const char* host, Port port) :
        first(nullptr), cur(nullptr)
    {
        struct addrinfo hints;
        std::memset(&hints, 0, sizeof hints);

        hints.ai_flags = !host ? AI_PASSIVE : 0;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        assert(!host || port);
        std::string portStr = std::to_string(port);

        int ret = getaddrinfo(host, portStr.c_str(), &hints, &first);
        if (ret) {
            SLICK_CHECK_ERRNO(ret != EAI_SYSTEM, "InterfaceIt.getaddrinfo");
            throw std::logic_error("error: " + std::to_string(ret));
        }

        cur = first;
    }

    ~InterfaceIt()
    {
        if (first) freeaddrinfo(first);
    }

    explicit operator bool() const { return cur; }
    void operator++ () { cur = cur->ai_next; }
    void operator++ (int) { cur = cur->ai_next; }

    const struct addrinfo& operator* () const { return *cur; }
    const struct addrinfo* operator-> () const { return cur; }

private:
    struct addrinfo* first;
    struct addrinfo* cur;
};

/******************************************************************************/
/* SOCKET                                                                     */
/******************************************************************************/

Socket::
Socket(Socket&& other) noexcept : fd_(other.fd_)
{
    other.fd_ = -1;
}

Socket&
Socket::
operator=(Socket&& other) noexcept
{
    if (this == &other) return *this;

    fd_ = other.fd_;
    other.fd_ = -1;

    return *this;
}

Socket
Socket::
connect(const Address& addr)
{
    Socket socket;
    assert(addr);

    for (InterfaceIt it(addr.chost(), addr.port); it; it++) {

        int fd = ::socket(it->ai_family, it->ai_socktype | SOCK_NONBLOCK, it->ai_protocol);
        if (fd < 0) continue;

        FdGuard guard(fd);

        int ret = ::connect(fd, it->ai_addr, it->ai_addrlen);
        if (ret < 0 && errno != EINPROGRESS) continue;

        socket.fd_ = guard.release();
        break;
    }

    if (socket) socket.init();
    return std::move(socket);
}

Socket
Socket::
connect(const std::vector<Address>& addrs)
{
    for (const auto& addr : addrs) {
        Socket socket = connect(addr);
        if (socket) return std::move(socket);
    }
    return Socket();
}

Socket
Socket::
accept(int fd)
{
    Socket socket;

    socklen_t addrlen = 0;
    struct sockaddr addr;
    std::memset(&addr, 0, sizeof(addr));

    socket.fd_ = accept4(fd, &addr, &addrlen, SOCK_NONBLOCK);
    if (socket.fd_ < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
        return std::move(socket);
    SLICK_CHECK_ERRNO(socket.fd_ >= 0, "Socket.accept");

    socket.init();
    return std::move(socket);
}

void
Socket::
init()
{
    int val = true;
    int ret = setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val);
    SLICK_CHECK_ERRNO(!ret, "Socket.setsockopt.TCP_NODELAY");
}

Socket::
~Socket()
{
    if (fd_ < 0) return;

    // There's no error checking because there's not much we can do if they fail
    shutdown(fd_, SHUT_RDWR);
    close(fd_);
}

int
Socket::
error() const
{
    int error = 0;
    socklen_t errlen = sizeof error;

    int ret = getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &errlen);
    SLICK_CHECK_ERRNO(!ret, "Socket.getsockopt.error");

    return error;
}

void
Socket::
throwError() const
{
    int err = error();
    if (err) throw std::runtime_error(checkErrnoString(err, "Socket.error"));
}

/******************************************************************************/
/* PASSIVE SOCKET                                                             */
/******************************************************************************/

PassiveSockets::
PassiveSockets(Port port, int flags)
{
    for (InterfaceIt it(nullptr, port); it; it++) {

        int fd = socket(it->ai_family, it->ai_socktype | flags, it->ai_protocol);
        if (fd < 0) continue;

        FdGuard guard(fd);

        int ret = bind(fd, it->ai_addr, it->ai_addrlen);
        if (ret < 0) continue;

        ret = listen(fd, 1U << 8);
        if (ret < 0) continue;

        fds_.push_back(guard.release());
    }

    if (fds_.empty()) throw std::runtime_error("ERROR: no valid interface");
}

PassiveSockets::
~PassiveSockets()
{
    for (int fd : fds_) close(fd);
}

/******************************************************************************/
/* NETWORK INTERFACES                                                         */
/******************************************************************************/

std::vector<Address>
networkInterfaces(bool excludeLoopback)
{
    std::vector<Address> result;
    unsigned include = IFF_UP | IFF_RUNNING;
    unsigned exclude = excludeLoopback ? IFF_LOOPBACK : 0;

    struct ifaddrs* it;
    int ret = getifaddrs(&it);
    SLICK_CHECK_ERRNO(!ret, "networkInterfaces.getifaddrs");
    auto addrsGuard = guard([=] { freeifaddrs(it); });

    for(; it; it = it->ifa_next) {

        if (!it->ifa_addr) continue;
        if (it->ifa_flags & exclude) continue;
        if ((it->ifa_flags & include) != include) continue;

        int family = it->ifa_addr->sa_family;
        if (family != AF_INET && family != AF_INET6) continue;

        result.emplace_back(it->ifa_addr);
    }

    return std::move(result);
}

} // slick

<commit_msg>Remove the flags from PassiveSockets (assume non-block always).<commit_after>/* socket.cpp                                 -*- C++ -*-
   Rémi Attab (remi.attab@gmail.com), 18 Nov 2013
   FreeBSD-style copyright and disclaimer apply

   Socket abstraction implementation
*/

#include "socket.h"
#include "utils.h"
#include "lockless/tls.h"

#include <array>
#include <string>
#include <cstring>
#include <cassert>
#include <netdb.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

namespace slick {

/******************************************************************************/
/* ADDRESS                                                                    */
/******************************************************************************/

Address::
Address(const std::string& hostPort)
{
    size_t pos = hostPort.find(':');
    assert(pos != std::string::npos);

    host = hostPort.substr(0, pos);
    port = stoi(hostPort.substr(pos + 1));
}

Address::
Address(struct sockaddr* addr)
{
    socklen_t addrlen;
    int family = addr->sa_family;

    if (family == AF_INET) addrlen = sizeof(struct sockaddr_in);
    else if (family == AF_INET6) addrlen = sizeof(struct sockaddr_in6);
    else assert(false);

    *this = Address(addr, addrlen);
}

Address::
Address(struct sockaddr* addr, socklen_t addrlen)
{
    std::array<char, 256> host;
    std::array<char, 256> service;

    int res = getnameinfo(
            addr, addrlen,
            host.data(), host.size(),
            service.data(), service.size(),
            NI_NUMERICHOST | NI_NUMERICSERV);
    SLICK_CHECK_ERRNO(!res, "Address.getnameinfo");

    this->host = std::string(host.data());
    this->port = atoi(service.data());
}

/******************************************************************************/
/* INTERFACE IT                                                               */
/******************************************************************************/

struct InterfaceIt
{
    InterfaceIt(const char* host, Port port) :
        first(nullptr), cur(nullptr)
    {
        struct addrinfo hints;
        std::memset(&hints, 0, sizeof hints);

        hints.ai_flags = !host ? AI_PASSIVE : 0;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        assert(!host || port);
        std::string portStr = std::to_string(port);

        int ret = getaddrinfo(host, portStr.c_str(), &hints, &first);
        if (ret) {
            SLICK_CHECK_ERRNO(ret != EAI_SYSTEM, "InterfaceIt.getaddrinfo");
            throw std::logic_error("error: " + std::to_string(ret));
        }

        cur = first;
    }

    ~InterfaceIt()
    {
        if (first) freeaddrinfo(first);
    }

    explicit operator bool() const { return cur; }
    void operator++ () { cur = cur->ai_next; }
    void operator++ (int) { cur = cur->ai_next; }

    const struct addrinfo& operator* () const { return *cur; }
    const struct addrinfo* operator-> () const { return cur; }

private:
    struct addrinfo* first;
    struct addrinfo* cur;
};

/******************************************************************************/
/* SOCKET                                                                     */
/******************************************************************************/

Socket::
Socket(Socket&& other) noexcept : fd_(other.fd_)
{
    other.fd_ = -1;
}

Socket&
Socket::
operator=(Socket&& other) noexcept
{
    if (this == &other) return *this;

    fd_ = other.fd_;
    other.fd_ = -1;

    return *this;
}

Socket
Socket::
connect(const Address& addr)
{
    Socket socket;
    assert(addr);

    for (InterfaceIt it(addr.chost(), addr.port); it; it++) {

        int flags = SOCK_NONBLOCK;
        int fd = ::socket(it->ai_family, it->ai_socktype | flags, it->ai_protocol);
        if (fd < 0) continue;

        FdGuard guard(fd);

        int ret = ::connect(fd, it->ai_addr, it->ai_addrlen);
        if (ret < 0 && errno != EINPROGRESS) continue;

        socket.fd_ = guard.release();
        break;
    }

    if (socket) socket.init();
    return std::move(socket);
}

Socket
Socket::
connect(const std::vector<Address>& addrs)
{
    for (const auto& addr : addrs) {
        Socket socket = connect(addr);
        if (socket) return std::move(socket);
    }
    return Socket();
}

Socket
Socket::
accept(int fd)
{
    Socket socket;

    socklen_t addrlen = 0;
    struct sockaddr addr;
    std::memset(&addr, 0, sizeof(addr));

    socket.fd_ = accept4(fd, &addr, &addrlen, SOCK_NONBLOCK);
    if (socket.fd_ < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
        return std::move(socket);
    SLICK_CHECK_ERRNO(socket.fd_ >= 0, "Socket.accept");

    socket.init();
    return std::move(socket);
}

void
Socket::
init()
{
    int val = true;
    int ret = setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val);
    SLICK_CHECK_ERRNO(!ret, "Socket.setsockopt.TCP_NODELAY");
}

Socket::
~Socket()
{
    if (fd_ < 0) return;

    // There's no error checking because there's not much we can do if they fail
    shutdown(fd_, SHUT_RDWR);
    close(fd_);
}

int
Socket::
error() const
{
    int error = 0;
    socklen_t errlen = sizeof error;

    int ret = getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &errlen);
    SLICK_CHECK_ERRNO(!ret, "Socket.getsockopt.error");

    return error;
}

void
Socket::
throwError() const
{
    int err = error();
    if (err) throw std::runtime_error(checkErrnoString(err, "Socket.error"));
}

/******************************************************************************/
/* PASSIVE SOCKET                                                             */
/******************************************************************************/

PassiveSockets::
PassiveSockets(Port port)
{
    for (InterfaceIt it(nullptr, port); it; it++) {

        int flags = SOCK_NONBLOCK;
        int fd = socket(it->ai_family, it->ai_socktype | flags, it->ai_protocol);
        if (fd < 0) continue;

        FdGuard guard(fd);

        int ret = bind(fd, it->ai_addr, it->ai_addrlen);
        if (ret < 0) continue;

        ret = listen(fd, 1U << 8);
        if (ret < 0) continue;

        fds_.push_back(guard.release());
    }

    if (fds_.empty()) throw std::runtime_error("ERROR: no valid interface");
}

PassiveSockets::
~PassiveSockets()
{
    for (int fd : fds_) close(fd);
}

/******************************************************************************/
/* NETWORK INTERFACES                                                         */
/******************************************************************************/

std::vector<Address>
networkInterfaces(bool excludeLoopback)
{
    std::vector<Address> result;
    unsigned include = IFF_UP | IFF_RUNNING;
    unsigned exclude = excludeLoopback ? IFF_LOOPBACK : 0;

    struct ifaddrs* it;
    int ret = getifaddrs(&it);
    SLICK_CHECK_ERRNO(!ret, "networkInterfaces.getifaddrs");
    auto addrsGuard = guard([=] { freeifaddrs(it); });

    for(; it; it = it->ifa_next) {

        if (!it->ifa_addr) continue;
        if (it->ifa_flags & exclude) continue;
        if ((it->ifa_flags & include) != include) continue;

        int family = it->ifa_addr->sa_family;
        if (family != AF_INET && family != AF_INET6) continue;

        result.emplace_back(it->ifa_addr);
    }

    return std::move(result);
}

} // slick

<|endoftext|>