I am on Windows 10, and want to use boost process to start a child. When the child's working directory is too long, I get an exception:
CreateProcess failed: The directory name is invalid.
I wrote a test program to debug this:
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <iostream>
#include <iomanip>
#include <thread>
#include <string>
#include <filesystem>
namespace bp = boost::process;
void test(const std::filesystem::path& wdir)
{
boost::asio::io_context io;
bp::child child;
try
{
std::filesystem::create_directories(wdir);
std::cout << "exists " << std::filesystem::exists(wdir) << " len " << wdir.generic_wstring().size() << '\n';
std::cout << "start proc\n";
child = bp::child(
"C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe",
std::vector<std::string> { "ls" },
io,
boost::process::start_dir = LR"(\\?\)" + wdir.generic_wstring());
std::this_thread::sleep_for(std::chrono::seconds{ 3 });
child.wait();
}
catch (const std::exception& e)
{
std::cout << "EXCEPTION " << e.what() << "\n";
}
std::cout << "done\n";
}
int main()
{
std::cout << "short path\n";
test("D:/tmp/10378020400asdfasdfqw4retf");
std::cout << "\nlong path\n";
test("D:/tmp/10378020400826168668/unicode/qwe/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiertzertz/aaaqqqwwwiiiiertzertz/eertz/"
"iiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiii"
"iiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiii"
"iiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"
"i/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqw"
"wwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiii"
"iiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiii"
"iiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiii"
"iiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/a"
"aaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwi"
"iiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiii"
"iiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiii"
"iiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiii"
"iiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaq"
"qqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiii"
"iiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii/aaaqqqwwwiiiiiiiiiiiii"
"iiiiiiiiiiiiiiiiiii/");
return 0;
}
I get this output:
short path
exists 1 len 33
start proc
done
long path
exists 1 len 2177
start proc
EXCEPTION CreateProcess failed: The directory name is invalid.
done
It seems the problem comes from the internally called CreateProcessW.
The documentation does not mention any limit for lpCurrentDirectory, and also allows UNC paths (hence I tried adding the prefix \\?\), but it does not make any difference whether I use the UNC syntax or not.
My questions are:
Is this an inherent limitation of Windows?
Is there any way to circumvent this limitation using boost?
Is there any way to circumvent this limitation using some other Win32 API function?
What I tired: using normal and UNC paths.
I'm trying to port a program from Windows to Linux.
I encountered a problem when I found out that there isn't a "real" ReadProcessMemory counterpart on Linux; I searched for an alternative and I found ptrace, a powerful process debugger.
I quickly coded two small console applications in C++ to test ptrace, before using it in the program.
TestApp
This is the tracee; it keeps printing two integers every 50 milliseconds while increasing their value by 1 every time.
#include <QCoreApplication>
#include <QThread>
#include <iostream>
using namespace std;
class Sleeper : public QThread
{
public:
static void usleep(unsigned long usecs){QThread::usleep(usecs);}
static void msleep(unsigned long msecs){QThread::msleep(msecs);}
static void sleep(unsigned long secs){QThread::sleep(secs);}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int value = 145;
int i = 0;
do {
cout << "i: " << i << " " << "Value: " << value << endl;
value++;
i++;
Sleeper::msleep(50);
} while (true);
return a.exec();
}
MemoryTest
This is the tracer; it asks for the process name and retrieves the PID using the command pidof -s, then ptrace attaches to the process and retrieves the memory address' value every 500 milliseconds, for 10 times.
#include <QCoreApplication>
#include <QThread>
#include <iostream>
#include <string>
#include <sys/ptrace.h>
#include <errno.h>
using namespace std;
class Sleeper : public QThread
{
public:
static void usleep(unsigned long usecs){QThread::usleep(usecs);}
static void msleep(unsigned long msecs){QThread::msleep(msecs);}
static void sleep(unsigned long secs){QThread::sleep(secs);}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
char process_name[50];
cout << "Process name: ";
cin >> process_name;
char command[sizeof(process_name) + sizeof("pidof -s ")];
snprintf(command, sizeof(command), "pidof -s %s", process_name);
FILE* shell = popen(command, "r");
char pidI[sizeof(shell)];
fgets(pidI, sizeof(pidI), shell);
pclose(shell);
pid_t pid = atoi(pidI);
cout << "The PID is " << pid << endl;
long status = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
cout << "Status: " << status << endl;
cout << "Error: " << errno << endl;
unsigned long addr = 0x012345; // Example address, not the true one
int i = 0;
do {
status = ptrace(PTRACE_PEEKDATA, pid, addr, NULL);
cout << "Status: " << status << endl;
cout << "Error: " << errno << endl;
i++;
Sleeper::msleep(500);
} while (i < 10);
status = ptrace(PTRACE_DETACH, pid, NULL, NULL);
cout << "Status: " << status << endl;
cout << "Error: " << errno << endl;
return a.exec();
}
Everything works fine, but TestApp is paused (SIGSTOP) until ptrace detaches from it.
Also, when it attaches to the process, the status is 0 and the error is 2; the first time it tries to retrieve the memory address value it fails with status -1 and error 3. Is it normal?
Is there a way to prevent ptrace from sending the SIGSTOP signal to the process?
I already tried using PTRACE_SEIZE instead of PTRACE_ATTACH, but it doesn't work: status -1 and error 3.
Update: Using Sleeper in MemoryTest before the "do-while" loop fixes the problem of the first memory address value retrieval, even if the value of seconds, milliseconds or microseconds is 0. Why?
After a lot of research I'm pretty sure that there isn't a way to use ptrace without stopping the process.
I found a real ReadProcessMemory counterpart, called process_vm_readv, which is much more simple.
I'm posting the code in the hope of helping someone who is in my (previous) situation.
Many thanks to mkrautz for his help coding MemoryTest with this beautiful function.
#include <QCoreApplication>
#include <QThread>
#include <sys/uio.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <iostream>
using namespace std;
class Sleeper : public QThread
{
public:
static void usleep(unsigned long usecs){QThread::usleep(usecs);}
static void msleep(unsigned long msecs){QThread::msleep(msecs);}
static void sleep(unsigned long secs){QThread::sleep(secs);}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
char process_name[50];
cout << "Process name: ";
cin >> process_name;
char command[sizeof(process_name) + sizeof("pidof -s ")];
snprintf(command, sizeof(command), "pidof -s %s", process_name);
FILE* shell = popen(command, "r");
char pidI[sizeof(shell)];
fgets(pidI, sizeof(pidI), shell);
pclose(shell);
pid_t pid = atoi(pidI);
cout << "The PID is " << pid << endl;
if (pid == 0)
return false;
struct iovec in;
in.iov_base = (void *) 0x012345; // Example address, not the true one
in.iov_len = 4;
uint32_t foo;
struct iovec out;
out.iov_base = &foo;
out.iov_len = sizeof(foo);
do {
ssize_t nread = process_vm_readv(pid, &out, 1, &in, 1, 0);
if (nread == -1) {
fprintf(stderr, "error: %s", strerror(errno));
} else if (nread != in.iov_len) {
fprintf(stderr, "error: short read of %li bytes", (ssize_t)nread);
}
cout << foo << endl;
Sleeper::msleep(500);
} while (true);
return a.exec();
}
Davide,
Have you had a look at the /proc filesystem? It contains memory map files that can be used to peek at the full process space. You can also write in the space to set a breakpoint. There is a wealth of other information in /proc as well.
The PTRACE_CONT command can be used to continue a process. Generally, the target will be paused with a PTRACE_ATTACH when the debugger attaches.
The man page says PTRACE_SIEZE should not pause the process. What flavor and version of Linux are you using? PTRACE_SIEZE has been around for quite awhile so I'm not sure why you are having trouble there.
I note the addr value is set to 0x12345. Is this a valid address in the target space? Or was that just an example? How is the stack address of interest (&value) communicated between the two processes?
I'm not too sure about the return codes. Generally a 0 means all is well, the errno may just be a hangover value from the last error.
--Matt
I have a problem when i use mysql connector c++ and visual studio on the debug mode. It compiles just fine. My algorithm works on release mode. When i switch to debug mode, he complies but he crash while running.
I believe that he crash each time i use res->getString() .
I m using visual studio 2013, mysql connector.C++ 1.1 . I already added the dependencies
C:\Program Files\MySQL\Connector.C++ 1.1\lib\debug
C:\Program Files\Boost SDK;
C:\Program Files\MySQL\Connector.C++ 1.1\include
mysqlcppconn.lib and mysqlcppconn.dll
CPPCONN_PUBLIC_FUNC= ;HAVE_INT8_T=1 ( on the processor definition)
run time library : /MDd ( it the only think that works )
I copied mysqlcppconn.lib and mysqlcppconn.dll in the debug directory of my project
with this configuration my programm works on release mode ( changing Connector.C++ 1.1\lib\debug to Connector.C++ 1.1\lib\opt ).
In the debug mode, he says Debug Assertion failed :
File: f:\dd\vctools\crt\crtw32\misc\dbgheap.c
Line 1322
Expression : _CrtIsValidHeapPointer( pUserData)
Press Rety to debug the application
then i had to choose one buttom ( Ignore; Restart ; leave ). When i press Ignore he continue to run i have the first results but he crashs again
This is my code section
{
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <memory>
#include <string>
/* MySQL Connector/C++ specific headers */
#include <cppconn/driver.h>
#include <cppconn/connection.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/resultset.h>
#include <cppconn/metadata.h>
#include <cppconn/resultset_metadata.h>
#include <cppconn/exception.h>
#include <cppconn/warning.h>
#include "mysql_connection.h"
using namespace std;
using namespace sql;
int main(int argc, const char **argv)
{
const char* str_DataBaseMarketData_Server = "root";
const char* str_DataBaseMarketData_User = "root";
const char* str_DataBaseMarketData_Password ="root";
const char* str_DataBaseMarketData_Database ="bd test";
cout << "Connector/C++ tutorial framework..." << endl;
cout << endl;
string fld;
try {
sql::Driver* driver = get_driver_instance();
std::auto_ptr<sql::Connection> con(driver->connect(str_DataBaseMarketData_Server, str_DataBaseMarketData_User, str_DataBaseMarketData_Password));
con->setSchema(str_DataBaseMarketData_Database);
std::auto_ptr<sql::Statement> stmt(con->createStatement());
sql::SQLString *data = new SQLString();
std::auto_ptr<sql::ResultSet> res(stmt->executeQuery("SELECT * FROM news "));
//ResultSetMetaData *res_meta = res->getMetaData();
//cout << res_meta->getColumnDisplaySize( 1) << endl << endl;
while (res->next()){
fld = res->getString(2).c_str();
}
}
catch (sql::SQLException &e) {
/*
The MySQL Connector/C++ throws three different exceptions:
- sql::MethodNotImplementedException (derived from sql::SQLException)
- sql::InvalidArgumentException (derived from sql::SQLException)
- sql::SQLException (derived from std::runtime_error)
*/
cout << "# ERR: SQLException in " << __FILE__;
cout << "(" << __FUNCTION__ << ") on line " << __LINE__ << endl;
/* Use what() (derived from std::runtime_error) to fetch the error message */
cout << "# ERR: " << e.what();
cout << " (MySQL error code: " << e.getErrorCode();
cout << ", SQLState: " << e.getSQLState() << " )" << endl;
return EXIT_FAILURE;
}
cout << "Done." << endl;
return EXIT_SUCCESS;
}
}
This things turns me crazy because when i replace getString() by anything else, it works sweetly.Could you help me please ?
I m running under a 32 bit.
I have a simple program that makes a directory when it is executed:
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(){
if(int a = mkdir("abc",0700)){
std::cout << "Failed to create: " << a << std::endl;
}
else{
std::cout << "Created." << std::endl;
}
}
It behaves differently for two different use cases:
Running the compiled binary through Terminal
Output: Created.
Launching this program via Finder with double click.
Output: Failed to create: -1
How do I make this so that launching this program via Finder creates the folder abc without using Cocoa framework (compiles with g++ only)?
Thanks to Wooble for pointing it out in the comment section that the problem is due to the working directory. When I launched it through Finder, the current working directory was my home directory.
Below is how I address the problem:
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libproc.h>
int main(int argc, char** argv){
// Gets and prints out the current directory
char cwd[1024];
getcwd(cwd, sizeof(cwd));
std::cout << "Current Working Directory: " << cwd << std::endl;
// Above is not necessary
// Changes working directory to directory that contains executable
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
if(proc_pidpath (getpid(), pathbuf, sizeof(pathbuf)) > 0){ // Gets the executable path
std::string s(pathbuf);
s = s.substr(0,s.find_last_of('/')); // Removes executable name from path
std::cout << "Executable Path: " << s << std::endl;
chdir(s.c_str()); // Changes working directory
}
if(int a = mkdir("abc",0700)){
std::cout << "Failed to create: " << a << std::endl;
}
else{
std::cout << "Created." << std::endl;
}
}
im trying to communicate a pthread with a process, using pipes, for a college proyect. i make a struct with the pipes and i pass that structure to the pthread so it can listen on the pipe[0], and on the rest of the code i try to send a string to that running pthread.
Here is my code:
#include <unistd.h>
#include <string>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include <pthread.h>
using namespace std;
struct Pipefd{
int pipe[2];
string name;
};
void* listenProcess(void* x){
Pipefd* pf = reinterpret_cast<Pipefd*>(x);
close(0);
dup(pf->pipe[0]);
//here i try to see if the struct i send is ok, but this is not printed.
cout << "pf.name: " << pf->name << endl;
string recive;
while(getline(cin,recive)){
cout << "recive: " << recive << endl;
}
cout << "Problem with getline" << endl;
}
int main(int argc, char *argv[]) {
Pipefd myPipe;
myPipe.name = "Test";
pipe(myPipe.pipe);
void* test = reinterpret_cast<void*>(&myPipe);
pthread_t tid;
pthread_create(&tid,NULL, &listenProcess,test);
close(1);
dup(myPipe.pipe[1]);
cout << "This is a message" << endl;
pthread_join(tid,NULL);
}
if someone can reply me with some ideas of how to make this work it would be awesome, if not, thank you for your time.