Use the answer in the question: simultaneous read and write to child's stdio using boost.process,
I refactored the code and hybridized the new method using the Boost library. I've been successful in making a pipes connection with Stockfish, but this is also where I get errors I've never seen before, not even Google helps.
Here is what I have tried:
#include <stdio.h>
#include <time.h>
#include <string>
#include <memory.h>
#include <unistd.h>
#include <iostream>
#include <stddef.h>
#include <execinfo.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fstream>
#include </usr/local/include/backtrace.h>
#include </usr/local/include/backtrace-supported.h>
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <boost/process/async.hpp>
#include <vector>
#include <iomanip>
#include <stdlib.h>
#include <string.h>
using namespace std;
namespace bp = boost::process;
using boost::system::error_code;
using namespace std::chrono_literals;
string errDetails = "Error Details: ";
void delay(int number_of_seconds) {
int ms = 1000 * number_of_seconds;
clock_t start_time = clock();
while (clock() < start_time + ms)
;
}
static void full_write(int fd, const char* buf, size_t len) {
while (len > 0) {
ssize_t ret = write(fd, buf, len);
if ((ret == -1) && (errno != EINTR)) {
break;
}
buf += (size_t) ret;
len -= (size_t) ret;
}
}
void print_backtrace() {
static const char start[] = "--------BACKTRACE--------\n\n";
static const char end[] = "-------------------------\n\n";
void *bt[1024];
int bt_size;
char **bt_syms;
int i;
bt_size = backtrace(bt, 1024);
bt_syms = backtrace_symbols(bt, bt_size);
full_write(STDERR_FILENO, start, strlen(start));
full_write(STDERR_FILENO, errDetails.c_str(), strlen(errDetails.c_str()));
for (i = 1; i < bt_size; i++) {
size_t len = strlen(bt_syms[i]);
full_write(STDERR_FILENO, bt_syms[i], len);
full_write(STDERR_FILENO, "\n", 1);
}
full_write(STDERR_FILENO, end, strlen(end));
free(bt_syms);
}
void abort_application() {
size_t memLeakCount, staticMemLeakCount;
uint64_t memLeakSize, staticMemLeakSize;
for (int i = 0; i < 3; i++) {
/**
* Delay
*/
delay(1);
}
print_backtrace();
abort();
}
inline bool stockfish_check_exists(const std::string& name) {
struct stat buffer;
return (stat(name.c_str(), &buffer) == 0);
}
int main() {
std::future<std::string> data;
boost::asio::io_service svc;
bp::async_pipe in{svc}, out{svc};
string proc = "";
char command[64];
string output = "";
if (stockfish_check_exists("stockfish")) {
proc = "stockfish"; } else {
errDetails = "Stockfish not found!\n\n";
abort_application();
}
std::string const program_dir = proc;
auto on_exit = [](int code, std::error_code ec) {
std::cout << "Exited " << code << "(" << ec.message() << ")\n";
};
bp::child process(proc, bp::std_in < in, svc);
boost::asio::streambuf recv_buffer;
std::cout << "uci send" << std::endl;
boost::asio::async_write(in, boost::asio::buffer("uci\n"),
[&](boost::system::error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << "\n" << std::endl;
in.close();
}
);
std::cout << "isready send" << std::endl;
boost::asio::async_write(in, boost::asio::buffer("isready\n"),
[&](boost::system::error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << "\n" << std::endl;
in.close();
}
);
cout << "Enter your command: ";
cin >> command;
cout << "Your command is: " << command << endl;
if (strcmp(command, "quit") == 0) {
cout << "Quiting......." << endl;
boost::asio::async_write(in, boost::asio::buffer("quit"),
[&](boost::system::error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << std::endl;
in.close();
cout << "Engine quit!" << endl;
}
);
}
svc.run();
return 0;
}
To make it easier to follow, I left out std::std_out > out at the line:
bp::child process(proc, bp::std_in < in, svc);
so that the engine results are immediately displayed in the Terminal window, so I'll know if I've gone astray. And this is when I discovered the strange thing
When I launch the application, it outputs on Terminal as follows:
[2022-01-14 20:25:55]
duythanh#DuyThanhs-MacBook-Pro:/Volumes/Data/ChessGUI$ ./ChessGUI
uci send
isready send
Enter your command: Stockfish 120122 by the Stockfish developers (see AUTHORS file)
id name Stockfish 120122
id author the Stockfish developers (see AUTHORS file)
option name Debug Log File type string default
option name Threads type spin default 1 min 1 max 512
option name Hash type spin default 16 min 1 max 33554432
option name Clear Hash type button
option name Ponder type check default false
option name MultiPV type spin default 1 min 1 max 500
option name Skill Level type spin default 20 min 0 max 20
option name Move Overhead type spin default 10 min 0 max 5000
option name Slow Mover type spin default 100 min 10 max 1000
option name nodestime type spin default 0 min 0 max 10000
option name UCI_Chess960 type check default false
option name UCI_AnalyseMode type check default false
option name UCI_LimitStrength type check default false
option name UCI_Elo type spin default 1350 min 1350 max 2850
option name UCI_ShowWDL type check default false
option name SyzygyPath type string default <empty>
option name SyzygyProbeDepth type spin default 1 min 1 max 100
option name Syzygy50MoveRule type check default true
option name SyzygyProbeLimit type spin default 7 min 0 max 7
option name Use NNUE type check default true
option name EvalFile type string default nn-ac07bd334b62.nnue
uciok
Unknown command: isready
Contrasting with the code above, the two commands were sent through pipes. is uci and isready, this is fine. The first uci command runs successfully, but the isready command, instead of returning readyok, it returns:
Unknown command: isready
I keep trying to type quit, which sends a quit command to the pipe as the exit engine, and it also fails:
Your command is: quit
Quiting.......
Write: 5
Write: 9
Unknown command: quit
Write: 5
Engine quit!
The program will then exit with the engine. I'm still wondering what was going on at the time, but the clues are really hazy as to what was going on behind the scenes.
Please help me. Any help is highly appreciated. Thank you so much everyone
UPDATE: The error continued when Unknown Command: Quit appeared. I typed these commands in Terminal while running Stockfish directly through Terminal, they work as a result, but my program still can't
You are printing to cout as if the async operations happen immediately. That's not the case. The async operations only happen when the io service runs.
svc.run();
Is at the very end of your code. So no async_ operation ever completes (or even starts) before that.
Other problems:
Your out async pipe is never used (not even connected). It's unclear to me how you intend to communicate with the child process that way.
In fairness, you only every write to the child process, so maybe you're not at all interested in the output. (But then perhaps recv_buffer can be deleted just as well).
Your buffers include the terminating NUL characters. (asio::buffer("uci\n") sends {'u','c','i','\n','\0'}). That's going to mess up the child processes's parsing.
You do in.close() in response to every single async_write completion. This guarantees that subsequent writes never can happen, as you closed the pipe.
Then when you send quit you fail to include the '\n' as well
You are reading into a char[64] with operator>> which makes no sense at all. Maybe you are using c++20 (so width of 64 might be assumed) but you never set a width. Most likely you would want to read into a string instead.
However, doing so cannot accept commands with whitespace (because std::ios::skipws is set by default). So, likely you wanted std::getline instead...
The fact that you include a boatload of C headers makes me think you're porting some C code (badly). That's also exemplified by the strcmp use and others, e.g. no need to use ::stat
Don't use using namespace std; (Why is "using namespace std;" considered bad practice?)
Don't use global variables (errDetails)
Don't use loops to wait for a time delay
No need to manually print backtraces. Instead, use Boost:
void abort_application(std::string const& errDetails) {
std::cerr << errDetails << "\n";
std::cerr << boost::stacktrace::stacktrace{} << std::endl;
std::this_thread::sleep_for(3s);
abort();
}
Existing Stockfish Client: Playing Games
You're in luck: I have a written full demo using stockfish on this site: Interfacing with executable using boost in c++.
This example shows how to correctly await and parse expected replies from the child process(es).
You will note that I chose coroutines for the async version:
Just for completeness, I thought I'd try an asynchronous implementation. Using the default Asio callback style this could become unwieldy, so I thought to use Boost Coroutine for the stackful coroutines. That makes it so the implementation can be 99% similar to the synchronous version
Just for comparison, here's what your code should look like if you didn't use coroutines:
Fixing Up Your Code
Live On Coliru
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/stacktrace/stacktrace.hpp>
#include <chrono>
#include <iomanip>
#include <iostream>
namespace bp = boost::process;
using boost::system::error_code;
using namespace std::literals;
static void abort_application(std::string const& errDetails) {
std::cerr << errDetails << "\n";
std::cerr << boost::stacktrace::stacktrace{} << std::endl;
std::this_thread::sleep_for(3s);
abort();
}
inline static bool stockfish_check_exists(std::string& name) {
return boost::filesystem::exists(name);
}
int main() {
boost::asio::io_service svc;
bp::async_pipe in{svc};
std::string proc = "/usr/games/stockfish";
if (!stockfish_check_exists(proc)) {
abort_application("Stockfish not found!");
}
auto on_exit = [](int code, std::error_code ec) {
std::cout << "Exited " << code << "(" << ec.message() << ")\n";
};
bp::child process(proc, bp::std_in < in, svc, bp::on_exit = on_exit);
std::function<void()> command_loop;
std::string command_buffer;
command_loop = [&] {
std::cout << "Enter your command: " << std::flush;
// boost::asio::streambuf recv_buffer;
if (getline(std::cin, command_buffer)) {
std::cout << "Your command is: " << command_buffer << std::endl;
command_buffer += '\n';
async_write( //
in, boost::asio::buffer(command_buffer),
[&](error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << " (" << ec.message() << ")" << std::endl;
if (command_buffer == "quit\n") {
std::cout << "Quiting......." << std::endl;
// in.close();
std::cout << "Engine quit!" << std::endl;
} else {
command_loop(); // loop
}
});
}
};
std::cout << "uci send" << std::endl;
async_write(
in, boost::asio::buffer("uci\n"sv),
[&](error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << "\n" << std::endl;
std::cout << "isready send" << std::endl;
async_write(in, boost::asio::buffer("isready\n"sv),
[&](error_code ec, size_t n) {
std::cout << "Write: " << n << std::endl;
command_loop(); // start command loop
});
});
svc.run(); // only here any of the operations start
}
Prints, e.g.
Or if Stockfish is in fact installed:
I have the following simple program :
#include <cryptlib.h>
#include "sha.h"
#include <sha3.h>
#include <filters.h>
#include <hex.h>
#include <beast/core/detail/base64.hpp>
using namespace CryptoPP;
using namespace boost::beast::detail::base64;
int main(int argc, char** argv) {
if (argc < 2) {
std::cout << "missing argument 1 : password";
return 0;
}
std::string password = std::string(argv[1]);
byte digest[SHA3_256::DIGESTSIZE];
SHA3 digestAlgo = SHA3_256();
std::cout << "going to calculate the digest\n";
digestAlgo.Update((const byte*) password.data(), password.size());
std::cout << "updated...\n";
digestAlgo.Final(digest);
std::cout << "calculated the digest\n";
char* b64encodedHash = (char*)malloc(sizeof(byte)*1000);
encode(b64encodedHash, digest, sizeof(byte)*1000);
std::cout << "password hashed : " << b64encodedHash << "\n";
return 1;
}
When I run it the text : "going to calculate the digest" is output on the command line and the program does not continue. It hangs.
Does anyone know why ? I am trying to follow the examples on the Crypto++ wiki, and this is very similar to theirs.
After the Final call I want to base64 encode the digest, you can remove that part, it uses a boost header file.
Thanks,
Regards
Change the line
SHA3 digestAlgo = SHA3_256();
to
SHA3_256 digestAlgo;
I am writing a packet sniffer in C++ utilizing streams instead of printf() to store and create output. The problem I've run into is that recvfrom() seems to fail and return -1 when I have two or more statements that generate output using a stream.
If I comment one of the two output generating statements, the program runs fine. Through trial and error, I've found that by removing the std::setw() from the std::cout statement, it will work correctly and display both the packet and the "beef" message.
Any ideas or help would be much appreciated as I am at a loss and considering reverting back to using printf() since it never had this problem (and is faster than a stream). I admit, this is really the first time I have ever used ostringstream and I may be using it incorrectly.
My simplified source code:
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <linux/if_packet.h>
std::string BufferInHex( unsigned char * buffer, int length )
{
std::ostringstream out;
for( int i = 0; i < length; i ++ ) {
if( i % 16 == 0 && i != 0 ) {
out << "\n";
}
else if( i % 8 == 0 && i != 0 ) {
out << " ";
}
out << std::hex;
out << std::setfill('0') << std::setw(2) << static_cast<unsigned>(buffer[i]) << " ";
out << std::dec;
}
return out.str();
}
int main( void )
{
struct sockaddr_ll saddr = {0};
socklen_t saddr_size = sizeof(saddr);
unsigned char packet[1500] = {0};
int sockFd = socket( AF_PACKET, SOCK_RAW, htons(ETH_P_ALL) );
if( sockFd < 0 ) {
std::cerr << "Error creating socket!\n";
return 1;
}
int data_size = recvfrom( sockFd, packet, sizeof( packet ), 0, (struct sockaddr*)&saddr, &saddr_size );
if( data_size == -1 ) {
std::cerr << "Error in recvfrom()\n";
return 2;
}
std::cout << std::hex;
std::cout << std::setw( 8 ) << ntohs( 0xADDE ) << "\n";
std::cout << std::dec;
std::cout << BufferInHex( packet, data_size ) << "\n";
return 0;
}
It is being compiled with g++ on Centos 6.4 kernel 2.6.32 using the following command:
g++ sniff.cpp -o sniff -Wall
Thanks for any ideas or help,
Jeremiah
I searched for examples on how to create a simple multithreaded app that does something similar to this:
#include <iostream>
using namespace std;
int myConcurrentFunction( )
{
while( 1 )
{
cout << "b" << endl;
}
}
int main( )
{
// Start a new thread for myConcurrentFunction
while( 1 )
{
cout << "a" << endl;
}
}
How can I get the above to output a and b "randomly" by starting a new thread instead of just calling myConcurrentFunction normally?
I mean: What is the minimal code for it? Is it really only one function I have to call? What files do I need to include?
I use MSVC 2010, Win32
The easiest is _beginthread. Just focus on how they create the thread in their example, it's not as complicated as it seems at a first glance.
#include <iostream>
#include <process.h>
using namespace std;
void myConcurrentFunction(void *dummy)
{
while( 1 )
{
cout << "b" << endl;
}
}
int main( )
{
_beginthread(myConcurrentFunction, 0, NULL);
while( 1 )
{
cout << "a" << endl;
}
}
It is more complicated than that. For one, the thread function must return a DWORD, and take an LPVOID parameters.
Take a look at the code from MSDN for more details.
BTW, why thread when you just need random sprinkiling of 'a' & 'b'.
int randomSprinkling()
{
char val[2]={'a','b'};
int i = 0;
while( ++i < 100 )
{
std::cout << val[rand()%2] << std::endl;
}
return 0;
}
Hi i am checking the GUID of SqlClass which is in my Test.dll But it does not give success it failed with value... Whatis wrong in this code.
#include <windows.h>
#include <iostream>
using namespace std;
int main() {
HKEY hk;
long n = RegOpenKeyEx(HKEY_CLASSES_ROOT,TEXT("\\CLSID\\SqlClass"),
0,KEY_QUERY_VALUE, &hk );"
if ( n == ERROR_SUCCESS ) {
cout << "OK" << endl;
}
else {
cout << "Failed with value " << n << endl;
}
}
I tried like this also RegOpenKeyEx(HKEY_CLASSES_ROOT,TEXT("\CLSID\46A951AC-C2D9-48e0-97BE-91F3C9E7B065"),
0,KEY_QUERY_VALUE, &hk )
THIS CODE WORKS FINE
#include < windows.h >
# include < iostream >
using namespace std;
int main() {
HKEY hk;
long n = RegOpenKeyEx(HKEY_CLASSES_ROOT,
TEXT("\\CLSID\\{46A951AC-C2D9-48e0-97BE-91F3C9E7B065}"),
0,KEY_QUERY_VALUE, &hk );"
if ( n == ERROR_SUCCESS ) {
cout << "OK" << endl;
}
else {
cout << "Failed with value " << n << endl;
}
}
I've never seen anything other than a GUID under CLSID, so the key probably doesn't exist. Look in that node under regedit to see what I mean.
What was the failure code, n? You can look this up in two ways
Put the number into the "Error Lookup" tool in Visual Studio's Tools menu.
Call FormatMessage on n, which gives you the text associated with that error.