How to Read the Results of a System() Call in C++

How do I read the results of a system() call in C++?

Why would std::malloc() fail?

The obvious reason is "because std::ftell() returned a negative signed number, which was then treated as a huge unsigned number".

According to the documentation, std::ftell() returns -1 on failure. One obvious reason it would fail is that you cannot seek in a pipe or FIFO.

There is no escape; you cannot know the length of the command output without reading it, and you can only read it once. You have to read it in chunks, either growing your buffer as needed or parsing on the fly.

But, of course, you can simply avoid the whole issue by directly using the system call df probably uses to get its information: statvfs().

How to read all of STDOUT produced by system() call into a socket written in C?

The way your code is sending and reading strings is not sufficient.

TCP is a byte stream. There is no 1-to-1 relationship between sends and reads. As such, the sender MUST either:

  1. send the string length before sending the string's data.
  2. send a unique terminator after the string data.

And the receiver MUST either:

  1. read the length then read the specified amount of data.
  2. read until the terminator is reached.

Also, send()/write() and recv()/read() can return fewer bytes than requested, so they need to be called in loops (or, in the case of recv(), you can use the MSG_WAITALL flag).

Try something more like this instead:

// common functions ...

bool sendRaw(int sock, void *data, size_t len)
{
char *ptr = (char*) data;
while (len > 0) {
int sent = send(sock, ptr, len, MSG_NOSIGNAL);
if (sent < 0) return false;
ptr += sent;
len -= sent;
}
return true;
}

int recvRaw(int sock, void *data, size_t len)
{
char *ptr = (char*) data;
while (len > 0) {
int recvd = recv(sock, ptr, len, MSG_NOSIGNAL);
if (recvd <= 0) return recvd;
ptr += recvd;
len -= recvd;
}
return 1;
}

bool sendUInt32(int sock, uint32_t value)
{
value = htonl(value);
return sendRaw(sock, &value, sizeof(value));
}

uint32_t recvUInt32(int sock)
{
uint32_t value;
if (recvRaw(sock, &value, sizeof(value)) <= 0) return -1;
return ntohl(value);
}

bool sendString(int sock, const char *str)
{
uint32_t len = strlen(str);
if (!sendUInt32(sock, len)) return false;
return sendRaw(sock, str, len);

/* alternatively:
return sendRaw(sock, str, strlen(len) + 1);
*/
}

/*
bool grow(char **str, size_t *cap, size_t stepBy)
{
size_t newcap = cap + stepBy;
char *newstr = (char*) realloc(*str, newcap);
if (!newstr) return false;
*str = newstr;
*cap = newcap;
return true;
}
*/

char* recvString(int sock)
{
uint32_t len = recvUInt32(sock);
if (len == -1) return NULL;

char *str = (char*) malloc(len+1);
if (!str) return NULL;

if (recvRaw(sock, str, len) <= 0){
free(str);
return NULL;
}

str[len] = '\0';
return str;

/* alternatively:

char ch, *str = NULL;
size_t len = 0, cap = 0;

do{
if (recvRaw(sock, &ch, 1) <= 0){
free(str);
return NULL;
}

if (ch == '\0') break;

if (len == cap){
if (!grow(&str, &cap, 256)){
free(str);
return NULL;
}
}

str[len++] = ch;
}
while (1);

if (len == cap){
if (!grow(&str, &cap, 1)){
free(str);
return NULL;
}
}

str[len] = '\0';
return str;
*/
}
// server ...

char *command;

while ((command = recvString(sock)) != NULL){
// ...
system(command);
free(command);

// read from command's stdout until finished ...

if (!sendString(sock, output, outputLength)) break;
}
// client ...

if (sendString(sock, command)){
char *output = recvString(sock);
if (output){
//print the output somewhere
free(output);
}
}

Alternatively, if you don't know the length of the command's response ahead of time, and/or don't want to buffer it all in a single memory buffer, then you can read it in chunks, sending each chunk as you go, eg:

// common functions, see above ...

typedef struct _chunk
{
uint8_t size;
char data[256];
} chunk;

bool sendChunk(int sock, const chunk *chk)
{
uint8_t size = chk ? chk->size : 0;
if (!sendRaw(sock, &size, 1)) return false;
if (chk) return sendRaw(sock, chk->data, size);
return true;
}

bool recvChunk(int sock, chunk *chk)
{
if (recvRaw(sock, &(chk->size), 1) <= 0) return false;
if (chk->size) return recvRaw(sock, chk->data, chk->size);
return true;
}
// server ...

bool sendOutput(int sock)
{
chunk chk;
int size;

do{
// read from command's stdout ...
size = read(..., chk.data, sizeof(chk.data));
if (size <= 0) break;

chk.size = (uint8_t) size;
if (!sendChunk(sock, &chk)) return false;
}
while(1);

// tell client the data is finished ...
return sendChunk(sock, NULL);
}

char *command;

while ((command = recvString(sock)) != NULL){
// ...
system(command);
free(command);
if (!sendOutput(sock)) break;
}
// client ...

if (sendString(sock, command)){
chunk chk;
do{
if (!recvChunk(sock, &chk)) break;
if (chk.size == 0) break;
//print the chk.data somewhere
}
while (1);
}

Get system command output in C program

Ok, I was confused in my other answer. In any case, the philosophy in this answer is the same. You can use directly the popen function.

Then you have something like this:

int numOfCPU;
FILE *fp = popen("grep -c ^processor /proc/cpuinfo", "r");

fscanf(fp, "%d", &numOfCPU);
pclose(fp);

I hope it will be useful.

I can't figure this out the System call read function in c. ( K&R c language book)

Ok, well step by step. The read function you're calling is actually a wrapper to a low level system cal probably called sys_read. Usually the C standard library provide wrappers to all system calls.

Why a while loop?

read function returns the number of bytes read by the function. This isn't necessary all the bytes the file contains. You need a while loop to keep reading the file in order to be able to completely process it. If both versions worked (with and without loops) is probably (most definitely) because the file is small and you were able to store it completely in the array but what abaut 1GB file?

write function behaves more or less the same and you sometimes you need a while loop too to completely dump a buffer in a file.

Why printf doesn't print?

printf function usually buffer the data before dumping it to the console. Normally it wait until it sees an end of line character before dumping it so if you change your code to: printf("??\n"); you will probably see it on the screen.

One more thing, of course there's a lot more technical stuff about these (a lot) so if you really want to learn about read function (and any other Unix system call or C library function) you can consult the manual pages 2 and 3 (2 for system calls and 3 for library functions). read for example is documented in read(2). And if you want to go even deeper, go grab the source code to see how the really work :)

Hope this helps!

How do I execute a command and get the output of the command within C++ using POSIX?


#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>

std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}

Pre-C++11 version:

#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>

std::string exec(const char* cmd) {
char buffer[128];
std::string result = "";
FILE* pipe = popen(cmd, "r");
if (!pipe) throw std::runtime_error("popen() failed!");
try {
while (fgets(buffer, sizeof buffer, pipe) != NULL) {
result += buffer;
}
} catch (...) {
pclose(pipe);
throw;
}
pclose(pipe);
return result;
}

Replace popen and pclose with _popen and _pclose for Windows.



Related Topics



Leave a reply



Submit