freyfrey01
freyfrey01

Reputation: 103

Reading file descriptor size from popen command for socket programming in C

I have seen similar questions asked but I seek a more specific way of how to correctly do it. I am creating a client and a server UDP FTP service where the client has options of

Currently, all implementations work except for my ls as I am trying to ensure the contents of "ls" are less than my buffer size so I know whether to receive/send continually. From what I have learned with research, I am getting an error because you cannot do ftell on a popen file descriptor given it is a pipe rather than the full contents of the popen command. Looking for help on how my implementation should be changed (mainly with doing ls -l on the server and sending the contents of it to the client).

My current code is as follows

FILE *fp=popen("ls -l","r");  
      fseek(fp, 0L, SEEK_END);
      long numbytes = ftell(fp);
      fseek(fp, 0L, SEEK_SET);    
      char tmp[SO_SNDBUF];
      sprintf(tmp, "%d", numbytes); 
      n = sendto(sockfd, tmp, strlen(tmp), 0, (struct sockaddr *) &clientaddr, clientlen); //send ls file size
      if (n < 0) 
        error("ERROR in sendto");  
      if(numbytes<SO_SNDBUF)
      {
        bzero(buf, SO_SNDBUF);
        fread(buf, sizeof(char), numbytes, fp);
        n = sendto(sockfd, buf, numbytes, 0, (struct sockaddr *) &clientaddr, clientlen);
        if (n < 0) 
          error("ERROR in sendto");
      }
      else
      {
        //outgoing file is larger than buffer size
        long totalSent = 0;
        while(totalSent<numbytes)
        {
          bzero(buf, SO_SNDBUF);
          if((numbytes-totalSent)<SO_SNDBUF)
          {
            fread(buf, sizeof(char), numbytes-totalSent, fp);
            n = sendto(sockfd, buf, numbytes-totalSent, 0, (struct sockaddr *) &clientaddr, clientlen); //send ls contents
            if (n < 0) 
              error("ERROR in sendto");
          }
          else
          {
            fread(buf, sizeof(char), SO_SNDBUF, fp);
            n = sendto(sockfd, buf, SO_SNDBUF, 0, (struct sockaddr *) &clientaddr, clientlen); //send ls contents
            if (n < 0) 
              error("ERROR in sendto");
          }
          totalSent+=n;
        }
      }
      pclose(fp);   

The main portion of code that would need modifying is: To correctly get how many bytes ls -l is returning

FILE *fp=popen("ls -l","r");  
      fseek(fp, 0L, SEEK_END);
      long numbytes = ftell(fp);
      fseek(fp, 0L, SEEK_SET);    
      char tmp[SO_SNDBUF];
      sprintf(tmp, "%d", numbytes); 

Upvotes: 1

Views: 334

Answers (2)

hyde
hyde

Reputation: 62797

Since you are going to read it all in anyway, you could simply do this, if you are on a platform where getdelim exists, which you probably are if you are using ls command:

FILE *fp = popen("ls -l","r");
char *buf = NULL;
size_t buf_size = 0;
ssize_t result = getdelim(&buf, &buf_size, EOF, fp);
if (result < 0) {
    perror("reading output of popen ls");
    // any other error handling
} else {
    // result contains size of entire ls output
    // buf contains the actual data
    // buf_size is useful only, if you want to re-use buf
}
free(buf); // if buf is NULL, does nothing

A note about using getdelim to get the entire file: If reading an actual file from disk, it may be too large to fit in memory. But here we are reading output of ls command, which for any practical purposes can't be that large.

Upvotes: 1

How about:

system("ls -l > ls.txt");
FILE *fp = fopen("ls.txt", "r");
//...

If it is not too much to create a temporary file. The advantage is, that you have your content ready (mainly the size).

The other solution would be (as suggested) to read from popen line by line into an array of strings and calculating the total amount of bytes while doing so. The disadvantage here is memory allocation (but we read it into memory anyway).

Upvotes: 1

Related Questions