Reputation: 328
I want to store system command output in a variable in a c program in Linux. In this context I need to run a long liner command in c program via popen() function. I have below long command which runs well in terminal.
awk '{if(l1){print ($2-l1)/1024"kB/s",($10-l2)/1024"kB/s"} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
It prints current download and upload speeds in terminal.
I have below prototype code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define COMMAND_LEN 128
#define DATA_SIZE 512
int main(int argc,char *argv[]){
FILE *pf;
char command[COMMAND_LEN];
char data[DATA_SIZE];
// Execute a command
sprintf(command, "awk '{if(l1){print ($2-l1)/1024"kB/s",($10-l2)/1024"kB/s"} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)");
// Setup our pipe for reading and execute our command.
pf = popen(command,"r");
if(!pf){
fprintf(stderr, "Could not open pipe for output.\n");
return;
}
// Grab data from process execution
fgets(data, DATA_SIZE , pf);
// Print grabbed data to the screen.
fprintf(stdout, "-%s-\n",data);
if (pclose(pf) != 0)
fprintf(stderr," Error: Failed to close command stream \n");
return 0;
}
I know that I must escape special characters such as backslashes or double quotes.
If I replace "awk '{if(l1){print ($2-l1)/1024"kB/s",($10-l2)/1024"kB/s"} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
" with "echo \"hello world\""
it runs well.
My question is :
How can I escape special characters in awk '{if(l1){print ($2-l1)/1024"kB/s",($10-l2)/1024"kB/s"} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
to make it work?
I tried many combination with no luck.
EDIT: As JoachimPileborg informed me about only escape character is double-quote, I removed those double quotes which is useless in linux command like
awk '{if(l1){print ($2-l1)/1024,($10-l2)/1024} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
but I still get
sh: 1: Syntax error: "(" unexpected
-c/net/dev)-
Error: Failed to close command stream
error.
EDIT2:
Pinetwig pointed out that the command is 140 bytes long while COMMAND_LEN. The value I assigned was 124. I increased that value to 1024, but the result isn't changed.
I think the error is related to <
pipe direction. When I remove the lines after it, it runs without errors. I don't know what happens here.
Upvotes: 2
Views: 1518
Reputation: 39316
Not an answer, but a side note:
Why use an external command, when you could process the /proc/net/dev
pseudofile directly? It is an userspace interface provided by the Linux kernel, and as such, not going to change in incompatible ways in the foreseeable future.
Personally, I'd use something like the following:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
struct interface {
struct interface *next;
unsigned long long rx_bytes;
unsigned long long rx_packets;
unsigned long long rx_errors;
unsigned long long rx_dropped;
unsigned long long rx_fifo;
unsigned long long rx_frame;
unsigned long long rx_compressed;
unsigned long long rx_multicast;
unsigned long long tx_bytes;
unsigned long long tx_packets;
unsigned long long tx_errors;
unsigned long long tx_dropped;
unsigned long long tx_fifo;
unsigned long long tx_collisions;
unsigned long long tx_carrier;
unsigned long long tx_compressed;
char name[];
};
void free_interfaces(struct interface *next)
{
while (next != NULL) {
struct interface *curr = next;
next = next->next;
curr->next = NULL;
curr->name[0] = '\0';
free(curr);
}
}
struct interface *list_interfaces(void)
{
struct interface *list = NULL;
struct interface *iface;
FILE *in;
unsigned long long field[16];
char *name, *next, *ends;
size_t i, namelen;
char *line = NULL;
size_t size = 0;
ssize_t len;
in = fopen("/proc/net/dev", "rb");
if (in == NULL)
return NULL; /* errno was set by fopen() */
while (1) {
len = getline(&line, &size, in);
if (len < (ssize_t)1)
break;
name = line;
while (*name == ' ')
name++;
ends = name;
while (*ends != '\0' && *ends != '\n' && *ends != ' ' && *ends != ':')
ends++;
if (*ends != ':' || ends == name)
continue;
namelen = (size_t)(ends - name);
next = ends + 1;
for (i = 0; i < 15; i++) {
ends = NULL;
errno = 0;
field[i] = strtoull(next, &ends, 0);
if (ends == NULL || ends == next || errno != 0) {
ends = NULL;
break;
}
next = ends;
}
if (ends == NULL)
continue;
iface = malloc(sizeof (struct interface) + namelen + 1);
if (iface == NULL) {
fclose(in);
free_interfaces(list);
errno = ENOMEM;
return NULL;
}
memcpy(iface->name, name, namelen);
iface->name[namelen] = '\0';
iface->rx_bytes = field[0];
iface->rx_packets = field[1];
iface->rx_errors = field[2];
iface->rx_dropped = field[3];
iface->rx_fifo = field[4];
iface->rx_frame = field[5];
iface->rx_compressed = field[6];
iface->rx_multicast = field[7];
iface->tx_bytes = field[8];
iface->tx_packets = field[9];
iface->tx_errors = field[10];
iface->tx_dropped = field[11];
iface->tx_fifo = field[12];
iface->tx_collisions = field[13];
iface->tx_carrier = field[14];
iface->tx_compressed = field[15];
iface->next = list;
list = iface;
}
free(line);
line = NULL;
if (ferror(in) || !feof(in)) {
fclose(in);
free_interfaces(list);
errno = EIO;
return NULL;
}
if (fclose(in)) {
free_interfaces(list);
errno = EIO;
return NULL;
}
errno = 0;
return list;
}
int main(void) {
struct interface *list, *curr;
list = list_interfaces();
if (!list) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
for (curr = list; curr != NULL; curr = curr->next)
printf("%s: %llu bytes, %llu packets sent; %llu bytes, %llu packets received.\n",
curr->name, curr->tx_bytes, curr->tx_packets, curr->rx_bytes, curr->rx_packets);
free_interfaces(list);
return EXIT_SUCCESS;
}
The following modified example outputs the network transfer rates once every five seconds, until it is interrupted or terminated (via INT (Ctrl+C, or TERM signal).
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
struct interface {
struct interface *next;
unsigned long long rx_bytes;
unsigned long long rx_packets;
unsigned long long rx_errors;
unsigned long long rx_dropped;
unsigned long long rx_fifo;
unsigned long long rx_frame;
unsigned long long rx_compressed;
unsigned long long rx_multicast;
unsigned long long tx_bytes;
unsigned long long tx_packets;
unsigned long long tx_errors;
unsigned long long tx_dropped;
unsigned long long tx_fifo;
unsigned long long tx_collisions;
unsigned long long tx_carrier;
unsigned long long tx_compressed;
char name[];
};
static volatile sig_atomic_t done = 0;
static void done_handler(int signum)
{
done = 1;
}
static int install_done(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = done_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) != -1)
errno = 0;
return errno;
}
void free_interfaces(struct interface *next)
{
while (next != NULL) {
struct interface *curr = next;
next = next->next;
curr->next = NULL;
curr->name[0] = '\0';
free(curr);
}
}
struct interface *find_interface(struct interface *list, const char *const name)
{
if (!name)
return NULL;
while (list != NULL)
if (!strcmp(list->name, name))
return list;
else
list = list->next;
return NULL;
}
struct interface *list_interfaces(void)
{
struct interface *list = NULL;
struct interface *iface;
FILE *in;
unsigned long long field[16];
char *name, *next, *ends;
size_t i, namelen;
char *line = NULL;
size_t size = 0;
ssize_t len;
in = fopen("/proc/net/dev", "rb");
if (in == NULL)
return NULL; /* errno was set by fopen() */
while (1) {
len = getline(&line, &size, in);
if (len < (ssize_t)1)
break;
name = line;
while (*name == ' ')
name++;
ends = name;
while (*ends != '\0' && *ends != '\n' && *ends != ' ' && *ends != ':')
ends++;
if (*ends != ':' || ends == name)
continue;
namelen = (size_t)(ends - name);
next = ends + 1;
for (i = 0; i < 15; i++) {
ends = NULL;
errno = 0;
field[i] = strtoull(next, &ends, 0);
if (ends == NULL || ends == next || errno != 0) {
ends = NULL;
break;
}
next = ends;
}
if (ends == NULL)
continue;
iface = malloc(sizeof (struct interface) + namelen + 1);
if (iface == NULL) {
fclose(in);
free_interfaces(list);
errno = ENOMEM;
return NULL;
}
memcpy(iface->name, name, namelen);
iface->name[namelen] = '\0';
iface->rx_bytes = field[0];
iface->rx_packets = field[1];
iface->rx_errors = field[2];
iface->rx_dropped = field[3];
iface->rx_fifo = field[4];
iface->rx_frame = field[5];
iface->rx_compressed = field[6];
iface->rx_multicast = field[7];
iface->tx_bytes = field[8];
iface->tx_packets = field[9];
iface->tx_errors = field[10];
iface->tx_dropped = field[11];
iface->tx_fifo = field[12];
iface->tx_collisions = field[13];
iface->tx_carrier = field[14];
iface->tx_compressed = field[15];
iface->next = list;
list = iface;
}
free(line);
line = NULL;
if (ferror(in) || !feof(in)) {
fclose(in);
free_interfaces(list);
errno = EIO;
return NULL;
}
if (fclose(in)) {
free_interfaces(list);
errno = EIO;
return NULL;
}
errno = 0;
return list;
}
static void set_timespec(struct timespec *const ptr, const double seconds)
{
if (ptr) {
if (seconds <= 0.0) {
ptr->tv_sec = 0;
ptr->tv_nsec = 0;
} else {
const long s = (long)seconds;
const long ns = (seconds - (double)s) * 1000000000.0;
ptr->tv_sec = s;
if (ns < 0L)
ptr->tv_nsec = 0L;
else
if (ns < 1000000000L)
ptr->tv_nsec = ns;
else
ptr->tv_nsec = 999999999L;
}
}
}
static double get_timespec(const struct timespec *const ptr)
{
if (ptr)
return (double)ptr->tv_sec + (double)ptr->tv_nsec / 1000000000.0;
else
return 0.0;
}
int main(void) {
struct interface *before, *after;
double interval = 5.0;
if (install_done(SIGINT) || install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
before = NULL;
after = list_interfaces();
if (!after) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
while (!done) {
struct interface *curr, *prev;
struct timespec req, rem;
double duration = interval;
double tx_rate, rx_rate;
set_timespec(&req, duration);
if (nanosleep(&req, &rem) == -1 && errno == EINTR)
duration -= get_timespec(&rem);
if (done)
break;
if (duration <= 0.0)
continue;
free_interfaces(before);
before = after;
after = list_interfaces();
if (!after) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
rx_rate = 0.0;
tx_rate = 0.0;
for (curr = after; curr != NULL; curr = curr->next) {
if (!strcmp(curr->name, "lo"))
continue;
prev = find_interface(before, curr->name);
if (prev) {
const double rx = ((double)curr->rx_bytes - (double)prev->rx_bytes) * 8.0 / 1024.0 / duration;
const double tx = ((double)curr->tx_bytes - (double)prev->tx_bytes) * 8.0 / 1024.0 / duration;
printf("%s: %9.0f kbits/s sent, %9.0f kbits/s received\n", curr->name, tx, rx);
rx_rate += rx;
tx_rate += tx;
}
}
printf("Total: %9.0f kbits/s sent, %9.0f kbits/s received\n\n", tx_rate, rx_rate);
fflush(stdout);
}
return EXIT_SUCCESS;
}
Upvotes: 1
Reputation: 241701
For what it's worth, here's a version of the function which does not need to spawn various processes to get the information:
/* feature-test macro needed for asprintf(3) */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Should be a command-line parameter */
#define COUNT 60
/* Open a statistic file */
FILE* open_stat(const char* iface, const char* stat) {
char* filename = NULL;
FILE* file;
if (asprintf(&filename,
"/sys/class/net/%s/statistics/%s",
iface, stat) < 0) {
perror("asprintf");
}
else {
file = fopen(filename, "r");
if (file)
setvbuf(file, NULL, _IONBF, 0);
else
perror(filename);
}
free(filename);
return file;
}
/* Read a statistic from a statistic file */
unsigned long read_stat(FILE* statfile) {
unsigned long value;
rewind(statfile);
int n = fscanf(statfile, "%lu", &value);
if (n != 1) { perror("scanf"); return -1; }
return value;
}
/* Sample main file */
int main(int argc, char** argv) {
const char* iface = "wlan0";
if (argc > 1) iface = argv[1];
FILE* recvf = open_stat(iface, "rx_bytes");
if (!recvf) exit(1);
FILE* xmitf = open_stat(iface, "tx_bytes");
if (!xmitf) exit(1);
unsigned long recv = read_stat(recvf);
unsigned long xmit = read_stat(xmitf);
for(int i = 0; i < COUNT; ++i) {
sleep(1);
unsigned long new_recv = read_stat(recvf);
unsigned long new_xmit = read_stat(xmitf);
printf("in: %6.3f kB/s, out: %6.3f kB/s\n",
(new_recv - recv) / 1024.0,
(new_xmit - xmit) / 1024.0);
recv = new_recv;
xmit = new_xmit;
}
fclose(recvf);
fclose(xmitf);
return 0;
}
Upvotes: 1
Reputation: 54505
The error message
sh: 1: Syntax error: "(" unexpected
comes from using a bash (or ksh) feature: the Process Substitution:
<(grep wlan0 /proc/net/dev)
(actually, two occurrences). For bash, if you begin a script without telling it that it is bash, it will refrain from honoring most of its extensions—such as this one. To work around that in the popen
command, you would have to wrap the whole command in a bash -c
and another level of quotes around the command itself.
Making that a separate shell script is probably simpler.
Upvotes: 1
Reputation: 695
The string "awk '{if(l1){print ($2-l1)/1024,($10-l2)/1024} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)"
seems well-escaped. However, it is 140 bytes long, while the buffer is 128 bytes long. I think you could be overwriting the buffer. Could you try increasing COMMAND_LEN?
Upvotes: 0