LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 11-21-2008, 04:21 AM   #1
christyyim
Member
 
Registered: Oct 2008
Location: earth
Distribution: Ubuntu 'Gutsy Gibbon'
Posts: 32

Rep: Reputation: 15
how to get the updated result continuously by using popen command in c language?


I'm now doing a project regarding the link traffic. In my project i try to use popen command to get the number of bytes i received in c programming and i successfully did so.
However here comes my problem, when i want to process the data I obtained, I found that this command keep giving me the same value without updating it. Here I post the code i roughly did in order to process the data.

Code:
#include <stdio.h>
#include <stdlib.h> /* required for atoi */

long int calculate();

main()
{

long int a[3];
long int b[5];
int i,j;

for (j=0; j<5; j++)
{
b[j]=0;
	for(i=0 ; i<3; i++)
	{
	a[i]=calculate();
	b[j]+= a[i];
	}
b[j]=b[j]/3;
printf("The number of bytes is: %d\n", b[j]);
}

}

long int calculate()
{
  FILE *fp;
  char line[130];			/* line of easa!from unix command*/
  int i;
 
  fp = popen("ifconfig eth0 |grep bytes|cut -c20-35", "r");		/* Issue the command.	*/

					/* Read a line	*/
  while ( fgets( line, sizeof line, fp))
  {
    printf("%s", line);		/* %s means string */
    i = atoi(line);
  }
  pclose(fp);
  
 return i;
}

Does anyone please help me figure out my problems or correct me if there is error on my code since i still a newbie to c language and linux world.

Thanks in advanced.
 
Old 11-22-2008, 11:51 PM   #2
osvaldomarques
Member
 
Registered: Jul 2004
Location: Rio de Janeiro - Brazil
Distribution: Conectiva 10 - Conectiva 8 - Slackware 9 - starting with LFS
Posts: 519

Rep: Reputation: 34
Hi christyyim,

I looked at your code and couldn't see any mistake. The reason for identical reads may be the speed which the loop is executed. Another reason could be traffic on the network.

I would suggest you to use "atol" instead of "atoi" for the conversion of the read values and also declare the variable "i" as long into the "calculate" function. However, I don't think this is the problem.

Osvaldo.
 
Old 11-23-2008, 07:34 AM   #3
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Tried you program, got a couple of warnings from gcc's "-Wall" option, fixed them. And it worked.

As Osvaldo already pointed out, your loop is running full speed. So there is probably little netwerk traffic within each loop iteration. Also your program will use max load on your CPU. That said, when I tried it while downloading a linux kernel source I did see increasing numbers, even inside the inner loop. Note that it depends on the download speeds.

Anyway, I addded a one-second delay within the inner loop. I also changed the command line, so it does not use 'ifconfig' to read and parse the /prov/net/dev file. IMHO it would be better to do the entire reading and parsing of /proc/net/dev inside the C-program, avoiding running a external command with popen to do that.

Code:
#include <stdio.h>
#include <stdlib.h> /* required for atoi() */
#include <unistd.h> /* required for sleep() */

long int calculate();

int main()
{
    long int a[3];
    long int b[5];
    int i, j;

    for (j = 0; j < 5; j++) {
        b[j] = 0;
        for (i = 0; i < 3; i++) {
            a[i] = calculate();
            b[j] += a[i];
            sleep(1);  /* one second delay */
        }
        b[j] = b[j] / 3;
        printf("The number of bytes is: %ld\n", b[j]); /* %ld for long int */
    }
    return 0;
}

long int calculate()
{
    FILE *fp;
    char line[130];
    long int i;

    fp = popen("sed -n 's/^ *eth0:\\([0-9]*\\).*/\\1/p' /proc/net/dev", "r");
    while (fgets(line, sizeof line, fp)) {
        printf("%s", line); /* %s means string */
        i = atol(line); /* convert to long int instead of int */
    }
    pclose(fp);
    return i;
}
 
Old 11-25-2008, 11:33 AM   #4
christyyim
Member
 
Registered: Oct 2008
Location: earth
Distribution: Ubuntu 'Gutsy Gibbon'
Posts: 32

Original Poster
Rep: Reputation: 15
sorry for my late reply and thanks osvaldomarques and Hko for your precious opinion.

I'll try to understand how the new command "sed" (for me,) works.

btw, does anyone please explain to me the following?
Quote:
so it does not use 'ifconfig' to read and parse the /prov/net/dev file. IMHO it would be better to do the entire reading and parsing of /proc/net/dev inside the C-program, avoiding running a external command with popen to do that.
i not really understand the meaning of "parse" and what is the /prov/net/dev file? Where is the file located? why it is better to use the command "sed" than "popen"?

sorry for my silly question
 
Old 11-25-2008, 09:44 PM   #5
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,541

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
Quote:
Originally Posted by christyyim View Post
sorry for my late reply and thanks osvaldomarques and Hko for your precious opinion.

I'll try to understand how the new command "sed" (for me,) works.

btw, does anyone please explain to me the following?

i not really understand the meaning of "parse" and what is the /prov/net/dev file? Where is the file located? why it is better to use the command "sed" than "popen"?

sorry for my silly question
The file is /proc/net/dev (not /prov/...).

What Hko was stating was that you should consider opening the /proc/net/dev file, and reading it line by line until you find the desired network interface (e.g. eth0), then continue to parse that same line to obtain the number of bytes received on that interface.

Whether this is more efficient or not is really not an issue if you decide to idle for a second or more waiting for the data (bytes received) to be updated.

Here's some code I threw together real quick:
PHP Code:
#include <stdio.h>
#include <unistd.h>
#include <string.h>


void displayReceivedBytes(chardata)
{
  
charbytesRcvd strtok(strchr(data':')+1" ");
  
printf("bytes received = %s\n"bytesRcvd);
}


int main()
{
  
FILEfp fopen("/proc/net/dev""r");

  if (
fp)
  {
    
int lineNumForEth0 0;

    for (;;)
    {
      
char line[256] = {0};

      if (
lineNumForEth0 == 0)
      {
        
// search for the line that contains eth0 data
        
while (fgets(linesizeof(line), fp))
        {
          ++
lineNumForEth0;

          if (
strstr(line"eth0"))
          {
            break;
          }
        }
      }
      else
      {
        
// skip ahead now that we know where eth0 is located at
        
for (int i 0lineNumForEth0; ++ifgets(linesizeof(line), fp);
      }

      
displayReceivedBytes(line);

      
sleep(1);

      
fp freopen("/proc/net/dev""r"fp);
    }
  }

  return 
0;

 
Old 11-27-2008, 08:59 AM   #6
christyyim
Member
 
Registered: Oct 2008
Location: earth
Distribution: Ubuntu 'Gutsy Gibbon'
Posts: 32

Original Poster
Rep: Reputation: 15
Hi dwhitney67,

thanks for your reply, it's very helpful. And from the reply from you all, i just know that not only can get the number of bytes received from ifconfig but can get from /proc/net/dev file as well.

Btw, about the source code from Hko, i can't get the received signal bytes.
What i get is just 0. I not really understand the sed command
 
Old 11-27-2008, 11:15 AM   #7
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
Originally Posted by christyyim View Post
Btw, about the source code from Hko, i can't get the received signal bytes.
What i get is just 0. I not really understand the sed command
Are you trying the sed command from the shell? Then you need to replace all double backslashes (\\) with one (\). On my computer:
Code:
bash$ sed -n 's/^ *eth0:\([0-9]*\).*/\1/p' /proc/net/dev
36073545
This is so because in C backslashes in string constants are used for newlines ("\n"), tabs ("\t") and so on. So to have a single backslash in a C string constant you need two of them.

About the sed command: sed edits text according to "regular expressions" ('grep' also uses regular expressions). By default sed outputs all lines of text it reads, whether sed has changed it or not. The -n option tells sed to output a line only when a 'p' command applies (the last 'p' in the sed command I posted).

The sed-command s/this/that/p" means:

If a line matches with this (a regular expression, a text-pattern), then substitute it with that. (the 's' in 's/../../' stands for "substitute")

And if the substitution happened, print the changed line to stdout (because of the 'p' in 's/../../p', which only makes sense in combination with the -n option. See above).

A line from /proc/net/dev looks like this:
Code:
  eth0:36170276   37885    0    0    0     0          0         0  5131160   34436    0    0    0     0       0          0
And we want sed to print this line if it is about eth0, and replace the entire line with only the digits sed finds right after "eth0:".

The text-pattern "this" to match was:
Code:
^ *eth0:\([0-9]*\).*
  • '^' means: start of the line
  • <space> followed by '*' means: any number of spaces.
  • 'eth0:' means just 'eth0:'.
  • '[0-9]*' means: any number of digit characters (0,1,2,3...9)
  • '.*' means: any number of any character. So this matches all the rest of the line we are not interested in. This is needed because we want the 's/../../' command to replace (substitute) the entire line.

Note that the digits part of the regular expression pattern ('[0-9]*') is between '\(' and '\)'. That means sed should "remember" that part.

The second part of the s/../../ command ("that") will replace the line of text if the pattern ("this") matches. This is simply: '\1' which means: the matched part of the line of text between the first pair of '\(' and '\)'.

I found it difficult to explain how sed works. But I hope it he1ps a bit. Play with sed, it can be very handy sometimes.

To find some sed tutorials google for something like "sed tutorial regexp".

Have fun!

Last edited by Hko; 11-27-2008 at 11:22 AM.
 
Old 11-27-2008, 12:29 PM   #8
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
My preferred way to read the number of bytes recieved on a network interface would be to do the enitre job in C itself. That is without any external programs like sed or ifconfig-grep-cut. Personally I feel if I run command lines from C I'm on the wrong track and should have made a shell script instead of creating a C program ;-)

In this case, it is even quite easy and the code gets even simpler to understand without the sed/cut/grep/ifconfig stuff.

Something like this:
Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define PROCFILE "/proc/net/dev"

long int get_received_bytes();

int main()
{
    long int a[3];
    long int b[5];
    int i, j;

    for (j = 0; j < 5; j++) {
        b[j] = 0;
        for (i = 0; i < 3; i++) {
            a[i] = get_received_bytes("eth0");
            b[j] += a[i];
            sleep(1);  /* one second delay */
        }
        b[j] = b[j] / 3;
        printf("The number of bytes is: %ld\n", b[j]);
    }
    return 0;
}
          

long int get_received_bytes(const char *device)
{
    FILE *fp;
    char line[130];
    char *numstr;

    if ((fp = fopen(PROCFILE, "r")) == NULL) {
        perror("Could not open" PROCFILE);
        return 0L;
    }
    while (fgets(line, sizeof line, fp) != NULL) {
        if ((numstr = strstr(line, device)) != NULL) {
            numstr += strlen(device) + 1;  /* +1 to skip the colon (:) */
            return atol(numstr);
        }
    }
    fclose(fp);
    return 0L;
}

Last edited by Hko; 11-27-2008 at 12:35 PM.
 
Old 11-28-2008, 11:33 AM   #9
christyyim
Member
 
Registered: Oct 2008
Location: earth
Distribution: Ubuntu 'Gutsy Gibbon'
Posts: 32

Original Poster
Rep: Reputation: 15
Hi Hko,

Thanks a lot. After you explain part by part about the sed command, I understand a lot. However, although this command display the number of bytes received in the terminal, but when I compiled the C program, it gives me warning "unknown escape sequence '\)'". Can you please explain to me what's the problem?

Actually i encountered this problem as well when i write this program like in my 1st post. At first, my terminal command is

Code:
ifconfig eth0 |grep bytes|cut -d":" -f2|cut -d" " -f1
it successfully displayed the number of received bytes. But when i tried to compile using gcc, it can't recognise the space in the command cut -d" " -f1


After that i only changed to command

Code:
ifconfig eth0 |grep bytes|cut -c20-35
since atoi() command will ignore the extra alphabet.

Hence i wonder how this will occur. Thanks.
 
Old 11-29-2008, 06:32 AM   #10
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
Originally Posted by christyyim View Post
but when I compiled the C program, it gives me warning "unknown escape sequence '\)'". Can you please explain to me what's the problem?
You forgot to double the backslash before the ')'. Also when I tried it again, I noticed I still had a bug in the sed command: In /proc.net/dev there are (somtimes?) space between 'eth0:' and the digits so in the regular expression there need to be added another <space>* combination. These were not matched in the code I posted. (thought I had tested it, but seems not to e true..)

So, one more time :-)
Code:
# in the shell:
bash$ sed -n 's/^ *eth0: *\([0-9]*\).*/\1/p' /proc/net/dev
Code:
/* In C: */
fp = popen("sed -n 's/^ *eth0: *\\([0-9]*\\).*/\\1/p' /proc/net/dev", "r");
                       added--^^
Quote:
Originally Posted by christyyim View Post
Actually i encountered this problem as well when i write this program like in my 1st post. At first, my terminal command is

Code:
ifconfig eth0 |grep bytes|cut -d":" -f2|cut -d" " -f1
it successfully displayed the number of received bytes. But when i tried to compile using gcc, it can't recognise the space in the command cut -d" " -f1
Right. That is because C uses double quotes around strings. In C, you can put two string constants after eachother and the compiler will paste (concatenate) them together.

This:
Code:
printf("Hello" "World");
is exactly the same as:
Code:
printf("HelloWorld");
So to have double quotes inside a string in C, you need to escape them with a backslash to prevent the C compiler to 'see' them:
Code:
fp = popen("ifconfig eth0 |grep bytes|cut -d\":\" -f2|cut -d\" \" -f1");
Or use single quotes:
Code:
fp = popen("ifconfig eth0 |grep bytes|cut -d':' -f2|cut -d' ' -f1");

Last edited by Hko; 12-02-2008 at 02:27 AM.
 
Old 11-29-2008, 11:04 AM   #11
christyyim
Member
 
Registered: Oct 2008
Location: earth
Distribution: Ubuntu 'Gutsy Gibbon'
Posts: 32

Original Poster
Rep: Reputation: 15
Yeah~~ Thanks Hko. But this also let me knows that there is still a long way for me to master C. I'll strive to improve my knowledge about C. Thanks everyone
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
system-config-language gives a result that is not recognized by locale -a. saurabh parli Linux - Newbie 0 08-30-2007 07:18 AM
python - getting command result Hewson Programming 1 08-01-2007 06:22 PM
continuously run a command? slinky2004 Linux - Newbie 2 11-04-2005 10:03 PM
Strange ip from the result of tracert command mrpc_cambodia Linux - Networking 4 01-25-2005 06:49 AM
BASH Command Result redhatnoob Programming 2 08-19-2004 06:25 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 02:11 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration