I am trying to implement a simple shell. Everything else works fine except for the error handling.
When I try to do execute an invalid command like "cat ff", in which "ff" does not exist, I got this:
The expected behavior should be like the third one "catt f". It must start with "ERROR:" and then the error message, which means it should be "ERROR:cat: ff: No such file or directory"
How should I modify my code to achieve that? Thanks in advance!
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <errno.h>
#include <sys/wait.h>
using namespace std;
int main(){
int pid;
int status;
char *cmd[] = {"cat", "ff", NULL};
if ((pid = fork()) == 0){
if (execvp(cmd[0], cmd) == -1){
cout << "ERROR:" << strerror(errno) << '\n';
}
}else if (pid == -1){
cout << "ERROR:" << strerror(errno) << '\n';
}else{
waitpid(pid, &status, 0);
cout << "Status: " << status << '\n';
}
}
The status here isn't very much necessary here. It is just my attempt to figure out whether it comes before that error message. I am super new to this and I am very confused and lost. Please forgive me if I did anything unnecessarily.
the second line cat: ff: No suche file... is an error output to stderr pipe written by cat command. If you want to suppress this you need to redirect stderr pipe. The execution of your shell command "cat" was successful, so it's handled through your last else condition. You need to check there for e.g. status 256 "no such file" and then print the error yourself.
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <errno.h>
#include <sys/wait.h>
#include <cstdio>
using namespace std;
int main(int argc, char** argv){
int pid;
int status;
char *cmd[] = {"cat", "ff", NULL};
int pipes[2];
pipe(pipes);
if ((pid = fork()) == 0){
dup2(pipes[1], STDERR_FILENO);
if (execvp(cmd[0], cmd) == -1){
cout << "ERROR:" << strerror(errno) << '\n';
}
}else if (pid == -1){
cout << "ERROR:" << strerror(errno) << '\n';
}else{
waitpid(pid, &status, 0);
if(256 == status) {
cout << "ERROR: ";
char buffer[100];
int count = read(pipes[0], buffer, sizeof(buffer)-1);
if (count >= 0) {
buffer[count] = 0;
cout << buffer;
cout << endl;
} else {
cout << "IO Error" << endl;
}
} else {
cout << "Status: " << status << '\n';
}
}
}
Related
I am currently trying to run simple tests with the Stockfish chess engine in C++ to get the best possible move. I am starting Stockfish as a child process and want to use the input and output streams to pass on the desired UCI commands, which I am exchanging with the parent process via a pipe. However, when I run the code, Stockfish just starts and nothing else happens. Typing in the UCI commands in the CLI then works, but that's obviously not the goal. What am I doing wrong?
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
void sig_handler(int sig) {}
int main() {
int fd1[2];
if (pipe(fd1) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
int pid = fork();
if (pid == 0) {
execl("./stockfish", "./stockfish", nullptr);
close(fd1[1]);
char position[60];
read(fd1[0], position, 60);
close(fd1[0]);
cout << "position " << position << endl;
cout << "go" << endl;
sleep(5000);
cout << "stop" << endl;
string move;
cin >> move;
exit(0);
}
else {
close(fd1[0]);
cout << "Parent process" << endl;
string line = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
write(fd1[1], line.c_str(), strlen(line.c_str()) + 1);
close(fd1[1]);
signal(SIGINT, &sig_handler);
wait(NULL);
cout << "Parent done" << endl;
}
return 0;
}
Thanks to #TedLyngmo and some research about the Boost library, it was very easy to implement:
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
using namespace std;
namespace bp = boost::process;
int main() {
bp::ipstream is;
bp::opstream os;
bp::child c("./stockfish", bp::std_in < os, bp::std_out > is);
os << "uci" << endl;
os << "isready" << endl;
os << "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" << endl;
os << "go depth 30" << endl;
string line;
string move_string;
while (getline(is, line)) {
if (!line.compare(0, 8, "bestmove")) {
move_string = line;
break;
}
}
// Delete the "bestmove" part of the string and get rid of any trailing characters divided by space
move_string = move_string.substr(9, move_string.size()-9);
vector<string> mv;
boost::split(mv, move_string, boost::is_any_of(" "));
cout << "Stockfish move: " << mv.at(0) << endl;
return 0;
}
I'm working on a C++ project that requires a client to send a file path and search string to a server. The server then searches through the provided file for the search string and prints the whole line for each instance it finds the search string. Searching in its own program works fine, however, when I add it to the rest of my code it does not work.
Search on its own is as follows:
#include <iostream>
#include <fstream>
#include <string>
int main(int argc, char *argv[]) {
std::ifstream inFile;
std::string path = argv[1];//"dat/anna_karenina.txt";
inFile.open(path);
std::string searchStr = argv[2];//"you m";
std::string line = "";
int bytesSent = 0;
if(inFile.is_open()) {
while(std::getline(inFile, line)) {
if(line.find(searchStr, 0)!=std::string::npos) {
char sending[line.length()];
for(int i=0; i<line.length(); i++) {
sending[i]=line.at(i);
}
std::cout << line << std::endl;
bytesSent += line.length();
}
}
}
std::cout << "BYTES SENT: " << bytesSent << std::endl;
inFile.close();
return 1;
}
When I run this all lines are printed how they should be.
This is my server code:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <sys/sysinfo.h>
int main(int argc, char *argv[]) {
if(argc != 2) {
std::cout << "Need domain socket file name" << std::endl;
exit(-1);
}
struct sockaddr_un server;
char buffer[32];
unlink(argv[1]);
int serverSock = socket(AF_UNIX, SOCK_STREAM, 0);
if(serverSock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
std::cout << "SERVER CONNECTED" << std::endl;
size_t maxClients = get_nprocs_conf()-1;
memset(&server, 0, sizeof(server));
server.sun_family = AF_UNIX;
strncpy(server.sun_path, argv[1], sizeof(server.sun_path)-1);
int success = bind(serverSock, (const struct sockaddr *) &server,
sizeof(struct sockaddr_un));
if(success < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
success = listen(serverSock, maxClients);
if(success < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
while(true) {
std::cout << "Waiting for clients" << std::endl;
int clientSock = accept(serverSock, nullptr, nullptr);
if(clientSock < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
std::cout << "CLIENT CONNECTED" << std::endl;
std::string path="";
std::string searchStr="";
char fileBuff[32];
char searchBuff[32];
memset(fileBuff, 0, 32);
success = read(clientSock, fileBuff, 32);
if(success < 0) {
perror("read");
exit(EXIT_FAILURE);
}
for(int i=0; i<(sizeof(fileBuff)/sizeof(char)); i++) {
path+=fileBuff[i];
}
std::cout << path << std::endl;
if(path.empty()) {
std::cout << "No path to file given" << std::endl;
exit(1);
}
memset(searchBuff, 0, 32);
success = read(clientSock, searchBuff, 32);
if(success < 0) {
perror("read");
exit(EXIT_FAILURE);
}
for(int j=0; j<(sizeof(searchBuff)/sizeof(char)); j++) {
searchStr+=searchBuff[j];
}
std::cout << searchStr << std::endl;
if(searchStr.empty()) {
std::cout << "No search string given" << std::endl;
exit(1);
}
std::ifstream inFile;
inFile.open(path);
std::string line = "";
int bytesSent = 0;
if(inFile.is_open()) {
//std::cout << "ok" << std::endl;
std::cout << searchStr << std::endl;
while(std::getline(inFile, line)) {
// std::cout << "ok" << std::endl;
if(line.find(searchStr, 0)!=std::string::npos) {
/std::cout << "ok" << std::endl;
char sending[line.length()];
//memset(sending, 0, sizeof(sending)/sizeof(char));
for(int k=0; k<(sizeof(sending)/sizeof(char)); k++) {
/std::cout << "ok" << std::endl;
sending[k]=line.at(k);
}
std::cout << line << std::endl;
bytesSent += line.length();
}
}
}
std::cout << "BYTES SENT: " << bytesSent << std::endl;
inFile.close();
close(clientSock);
}
return 1;
}
When I run the server and client code, the client does as it should, and so does the server, until if(line.find(searchStr, 0)!=std::string::npos) { at which it acts as if the search string is not found anywhere on any line and prints no lines and says the bytes sent is 0. I don't understand why the server cannot search for the string but the search method on its own can. I fairly sure the search string is correct; when I print it out it is what it needs to be. I'm just confused as to what I'm doing wrong in the server code for it to not enter the if statement.
If you're confused as to what bytes sent is, the server is supposed to send all the lines found to the client for it to print and count how many bytes it's sending. I don't need the server to print the lines. I am just doing it now so I can see that the lines are actually being found.
I'm supposed to use get_nprocs_conf() to get the number of execution contexts on my machine. I'm doing this because I am coding a server and client to interact with each other, and the server may only host get_nprocs_conf()-1 clients. Before I add code to my server to wait for an opening, I want to figure out this issue.
I'm running this code on a virtual machine because I'm using Linux and my desktop is Windows, and when I use said code above, my maximum number of clients is 0, meaning that get_nprocs_conf() only returns 1. Is this because I'm using a virtual machine and for some reason it only can use one execution context, or am I misunderstanding and my computer only has one execution context?
Provided below are my server and client programs.
Server Code:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <sys/sysinfo.h>
#include <vector>
#include <cstring>
#include <arpa/inet.h>
//#define BUFFER_SIZE 32
//May need to replace -1 with EXIT_FAILURE in all the exits
int main(int argc, char *argv[]) {
if(argc != 2) {
std::cout << "Need domain socket file name" << std::endl;
exit(-1);
}
struct sockaddr_un server;
char buffer[32];
unlink(argv[1]);
int serverSock = socket(AF_UNIX, SOCK_STREAM, 0);
if(serverSock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
std::clog << "SERVER STARTED" << std::endl;
size_t maxClients = get_nprocs_conf()-1;
std::clog << "\t" << "MAX CLIENTS: " << maxClients << std::endl;
memset(&server, 0, sizeof(server));
server.sun_family = AF_UNIX;
strncpy(server.sun_path, argv[1], sizeof(server.sun_path)-1);
int success = bind(serverSock, (const struct sockaddr *) &server,
sizeof(struct sockaddr_un));
if(success < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
success = listen(serverSock, maxClients);
if(success < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
while(true) {
std::cout << "Waiting for clients" << std::endl;
int clientSock = accept(serverSock, nullptr, nullptr);
if(clientSock < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
std::clog << "CLIENT CONNECTED" << std::endl;
std::string path="";
std::string searchStr="";
char fileBuff[32];
char searchBuff[32];
memset(fileBuff, 0, 32);
success = read(clientSock, fileBuff, 32);
if(success < 0) {
perror("read");
exit(EXIT_FAILURE);
}
path = fileBuff;
std::cout << path << std::endl;
if(path.empty()) {
std::cout << "No path to file given" << std::endl;
exit(1);
}
memset(searchBuff, 0, 32);
success = read(clientSock, searchBuff, 32);
if(success < 0) {
perror("read");
exit(EXIT_FAILURE);
}
searchStr = searchBuff;
std::cout << searchStr << std::endl;
if(searchStr.empty()) {
std::cout << "No search string given" << std::endl;
exit(1);
}
std::ifstream inFile;
inFile.open(path);
std::string line = "";
int bytesSent = 0;
std::vector<std::string> allLines;
if(inFile.is_open()) {
while(std::getline(inFile, line)) {
if(line.find(searchStr, 0)!=std::string::npos) {
allLines.push_back(line);
//std::cout << line << std::endl;
}
}
}
//Sending over entire length of vector containing all lines to be sent.
long length = htonl(allLines.size());
success = write(clientSock, &length, sizeof(length));
if(success < 0) {
perror("write");
exit(EXIT_FAILURE);
}
for(int b=0; b<allLines.size(); b++) {
length = htonl(allLines[b].length());
success = write(clientSock, &length, sizeof(length));
if(success < 0) {
perror("write");
exit(EXIT_FAILURE);
}
success = write(clientSock, allLines[b].data(), allLines[b].length());
if(success < 0) {
perror("write");
exit(EXIT_FAILURE);
}
bytesSent += allLines[b].length();
}
//char end[] = {'\n'};
//write(clientSock, end, sizeof(char));
std::cout << "BYTES SENT: " << bytesSent << std::endl;
inFile.close();
close(clientSock);
}
//return 0;
}
Client Code:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include <fstream> //Don't think I'll need this
#include <errno.h>
#include <sys/un.h>
#include <sstream>
#include <arpa/inet.h>
#include <vector>
int main(int argc, char *argv[]){
if(argc != 4) {
std::cout << "Need domain socket file name, file path and name, " <<
"and search string" << std::endl;
exit(-1);
}
struct sockaddr_un client;
char buffer[64]; //Prof had 64 for client may need to change to 32 to match
int clientSock = socket(AF_UNIX, SOCK_STREAM, 0);
if(clientSock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
std::cout << "socket connected" << std::endl;
memset(&client, 0, sizeof(struct sockaddr_un));
client.sun_family = AF_UNIX;
strncpy(client.sun_path, argv[1], sizeof(client.sun_path)-1);
int connectClient = connect(clientSock, (const struct sockaddr *)&client,
sizeof(struct sockaddr_un));
if(connectClient < 0) {
fprintf(stderr, "The server is not working.\n");
exit(EXIT_FAILURE);
}
std::cout << "client connected" << std::endl;
//char arg2[] = {*argv[2],'\n'};
std::string path = argv[2];
std::cout << "Path: " << path << std::endl;
connectClient = write(clientSock, argv[2], path.length());
if(connectClient < 0) {
perror("write");
exit(EXIT_FAILURE);
}
std::string search = argv[3];
std::cout << "Search String: " << search << std::endl;
connectClient = write(clientSock, argv[3], search.length());
if(connectClient < 0) {
perror("write");
exit(EXIT_FAILURE);
}
//int servRet;
int lineCount=0;
int bytes_received=0;
//std::string line = "";
char length[sizeof(int)];
//std::string leng = "";
int num=0;
std::stringstream ss;
std::vector<std::string> allLines;
long size = 0;
read(clientSock, &size, sizeof(size));
size = ntohl(size);
for(int a=0; a<size; ++a) {
long length = 0;
std::string line = "";
connectClient = read(clientSock, &length, sizeof(length));
length = ntohl(length);
while(0 < length) {
char buffer[1024];
connectClient = read(clientSock, buffer, std::min<unsigned long>(sizeof(buffer),length));
line.append(buffer, connectClient);
length-=connectClient;
}
allLines.push_back(line);
lineCount++;
std::cout << lineCount << "\t" << line << std::endl;
bytes_received += line.length();
}
std::cout << "BYTES RECEIVED: " << bytes_received << std::endl;
close(clientSock);
return 0;
}
Right now everything in the server and client work as they should. I'm just hesitating to add code that waits for an execution context to open for another client to be read because it seems like it would never accept any clients since the sole execution context is being used by the server. Any clarification on my issue or on if I'm just using get_nprocs_conf() incorrectly would be greatly appreciated.
I tried creating two different visual c++ console applications for Inter Process Communication(IPC). The Build for both codes is successful.But,when I try to debug it, I get exception like this "Exception thrown at 0x00007FFF168E1657 (vcruntime140d.dll) in FileMapServer_Parent.exe: 0xC0000005: Access violation writing location 0x0000000000000000"
//PARENT PROCESS:
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <Tlhelp32.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <cstring>
#include <cctype>
#include <fstream>
using namespace std
int main()
{
cout << "\t\t.....FILEMAPPING SERVER or PARENT....." << endl;
cout << endl;
//Local Variable Definitions
HANDLE hFileMap;
BOOL bResult;
PCHAR lpBuffer = NULL;
char Buffer[256] = "Hello From File Map Server";
size_t szBuffer = size(Buffer);
// STEP 1 : Create File Map
hFileMap = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
256,
L"LOCAL\\MyFileMap);
if (hFileMap == FALSE)
{
cout << "CreateFileMapping Failed & Error Number - " << GetLastError() << endl;
}
else
cout << "CreateFileMapping Success - " << endl;
// STEP 2 : Map View of File
lpBuffer = (PCHAR)MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
256);
if (lpBuffer == NULL)
{
cout << "MapViewOf File Failes & Error No - " << GetLastError() << endl;
}
else
cout << "MapViewOf File Success " << endl;
//STEP 3 : Copy Memory Function
CopyMemory(lpBuffer,Buffer,szBuffer);
//STEP 4 : Unmap View Of File
bResult = UnmapViewOfFile(lpBuffer);
if (bResult == FALSE)
{
cout << "UnMapViewOfFile Failed & Error No - " << GetLastError() << endl;
}
else
cout << "UnMapViewOfFile FSuccess - " << endl;
system("PAUSE");
return 0;
}
Exception while running parent process:
[1]: https://i.stack.imgur.com/bomQB.png
//CHILD PREOCESS:
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <Tlhelp32.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <cstring>
#include <cctype>
#include <fstream>
using namespace std;
int main()
{
cout << "\t\t.....FILEMAPPING CLIENT or CHILD....." << endl;
cout << endl;
// Local Variable Definitions
HANDLE hFileMap;
BOOL bResult;
PCHAR lpBuffer = NULL;
// STEP 1 : OpenFileMapping
hFileMap = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
FALSE,
L"LOCAL\\MyFileMap");
if(hFileMap == NULL)
{
cout << "OpenFileMap Failed & error - " << GetLastError() << endl;
}
else
cout << "OpenFileMap success " << endl;
//STEP 2 : MapViewOfFile
lpBuffer = (PCHAR)MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
256);
if (lpBuffer == NULL)
{
cout << "MapViewOf File Failes & Error No - " << GetLastError() << endl;
}
else
cout << "MapViewOf File Success " << endl;
//STEP 3 : Reading the data from File Map Object
cout << "DATA READING FROM PARENT PROCESS--->" <<lpBuffer<< endl;
//STEP 4 : UnMapViewOfFile
bResult = UnmapViewOfFile(lpBuffer);
if (bResult == FALSE)
{
cout << "UnMapViewOfFile Failed & Error No - " << GetLastError() << endl;
}
else
cout << "UnMapViewOfFile FSuccess - " << endl;
//STEP 5 : Close Handle
CloseHandle(hFileMap);
system("PAUSE");
return 0;
}
The problem is here:
hFileMap = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
256,
L"LOCAL\\MyFileMap");
From the documentation of Kernel object namespaces they are case sensitive so you must change LOCAL to Local for this to work.
In addition to the "Global\" prefix, client processes can use the "Local\" prefix to explicitly create an object in their session namespace. These keywords are case sensitive.
https://msdn.microsoft.com/en-us/library/aa382954(v=vs.85).aspx
While debugging this issue I also changed the code to exit if the mapping fails:
if (hFileMap == NULL)
{
cout << "CreateFileMapping Failed & Error Number - " << GetLastError() << endl;
return -1;
}
else
cout << "CreateFileMapping Success - " << endl;
This avoided the crash with the original "LOCAL" (mapping failed).
When using a FIFO on a single process, it looks like after both ends have been opened and one is then closed, it is not possible to reuse the FIFO. Any attempt to reopen the closed end fails or the returned file descriptor is useless.
Is it possible to work around this behavior, or do we have to keep both ends of the FIFO open until we are absolutely sure we don't need it anymore?
Here is some test code that shows and attempt to reopen a closed write end of a FIFO:
#include <iostream>
#include <cstdio>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
using namespace std;
int main(int argc, const char **argv)
{
cout << "Creating an instance of a named pipe..." << endl;
mode_t prevMask = umask(0);
if (mknod("my_pipe", S_IFIFO | 0666, 0))
return -1;
umask(prevMask);
cout << "Opening Read end..." << endl;
int fdM = open("my_pipe", O_RDONLY | O_NONBLOCK);
if (fdM == -1)
return -1;
cout << "Opening Write end..." << endl;
int fdS = open("my_pipe", O_WRONLY | O_NONBLOCK);
if (fdS == -1)
return -1;
cout << "Sending data to pipe..." << endl;
const char *data = "Hello my friend!";
ssize_t NbOfBytesWritten = write(fdS, data, strlen(data));
if (NbOfBytesWritten < 0)
return -1;
cout << "Number of bytes sent: " << NbOfBytesWritten << endl;
cout << "Closing Write end..." << endl;
if (close(fdS))
return -1;
cout << "Reopening Write end..." << endl;
fdS = open("my_pipe", O_WRONLY | O_NONBLOCK);
if (fdS == -1)
{
cout << "open() - failed("<< errno << "): " << strerror(errno) << '.';
remove("my_pipe");
return -1;
}
cout << "Sending some more data to pipe..." << endl;
data = "What's up?";
NbOfBytesWritten = write(fdS, data, strlen(data));
if (NbOfBytesWritten < 0)
return -1;
cout << "Number of bytes sent: " << NbOfBytesWritten << endl;
cout << "Reading data from pipe..." << endl;
char buff[128];
ssize_t numBytesRead = read(fdM, buff, 127);
if (NbOfBytesWritten < 0)
return -1;
buff[numBytesRead] = '\0'; // null terminate the string
cout << "Number of bytes read: " << numBytesRead << endl;
cout << "Message: " << buff << endl;
cout << "Closing Write end..." << endl;
if (close(fdS))
return -1;
cout << "Closing Read end..." << endl;
if (close(fdM))
return -1;
cout << "Deleting pipe..." << endl;
if (remove("my_pipe"))
return -1;
return 0;
}
Here is the output:
Creating an instance of a named pipe...
Opening Read end...
Opening Write end...
Sending data to pipe...
Number of bytes sent: 16
Closing Write end...
Reopening Write end...
open() - failed(6): No such device or address.
I also tested similar code trying to reopen a closed read end (While the write end was kept open). In that case the open() function succeed, but the read() function using the file descriptor returned by open() fails with:
Communication error on send. (70)
EDIT:
I'm using CYGWIN.
You code works fine on Linux. I think the issue you are running into is that named pipes on CYGWIN don't work very well and fail to follow POSIX semantics. See FIFO (named pipe) is broken on Cygwin. Likely the same problem you have.