Need help with stat()

Need help with stat()

Post by Paul Cantalup » Sun, 16 Apr 2000 04:00:00



Hello,

    I am trying to use the function stat() to determine when the last
time a file has been accessed.  The man pages on stat() say that the
attribute of the struct stat, st_atime, is updated when a read() occurs
on the file.  For me, this is not working.  Below is the code.  I am
able to use st_mtime to determine when the file was last modified but I
cannot get the accessed attribute of stat to update.  Thank you in
advance for any help that you can provide.

Paul

/* program to try to show that reading from a file will update
the stat attribute, st_atime */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>   /* open */
#include <unistd.h>  /* read */
#define MAXSTRING 1024
extern int errno;

void main() {

   char outbuf[MAXSTRING], inbuf[MAXSTRING];
   int spnum, nr, nw;   /* return val of sprintf, return val of read(),
write() */
   int fdstat;
   char *fname = "stattest.txt";
   struct stat my_stat;
   time_t now;       /* 32 bit integer to hold time */
   int response;

   if((fdstat = open (fname, O_RDWR | O_CREAT, S_IREAD | S_IWRITE)) < 0)

      printf("error on open\n");    /* get a descriptor */

   while (1) {
      stat (fname, &my_stat);          /* get details on the file */
      printf("\nLast accessed %d\n", my_stat.st_atime);
      printf("Last modified %d\n\n", my_stat.st_mtime);

      printf("Would you like to make a change to the file? ");
      response = getc(stdin); getc(stdin);

      now = time(NULL);
      if (response == 'Y' || response == 'y') {             /* modify
the file */
         lseek (fdstat, 0L, SEEK_END);
         spnum = sprintf(outbuf, "Time is %s\tsec = %d\n\n",
ctime(&now), now);
         if ((nw = write(fdstat, outbuf, spnum)) != spnum)
/* write the time to the file */
            printf("error on write: %s\n", strerror(errno));
         printf("Modifying file\n");
      }
      else if (response == 'q' || response == 'Q')
       break;

      printf("Would you like to access the the file? ");
      response = getc(stdin); getc(stdin);

      if (response =='Y' || response == 'y') {             /* access the

file */
         lseek (fdstat, 0L, SEEK_SET);
         if ((nr = read(fdstat, inbuf, 10)) < 0) {           /* just
read an arbitrary amount */
          printf("error on read: %s\n", strerror(errno));
            break;
         }
         inbuf[nr] = NULL;
         printf("Accessing file\n");
         printf("inbuf = %s\n", inbuf);
      }
      else if (response == 'q' || response == 'Q')
       break;
   }

Quote:}

 
 
 

Need help with stat()

Post by Floyd Davidso » Tue, 18 Apr 2000 04:00:00



>Hello,

>    I am trying to use the function stat() to determine when the last
>time a file has been accessed.  The man pages on stat() say that the
>attribute of the struct stat, st_atime, is updated when a read() occurs
>on the file.  For me, this is not working.  Below is the code.  I am
>able to use st_mtime to determine when the file was last modified but I
>cannot get the accessed attribute of stat to update.  Thank you in
>advance for any help that you can provide.

>Paul

>/* program to try to show that reading from a file will update
>the stat attribute, st_atime */
>#include <stdio.h>
>#include <sys/stat.h>
>#include <sys/types.h>
>#include <fcntl.h>   /* open */
>#include <unistd.h>  /* read */

You need one more include:

#include <stdlib.h>

Quote:>#define MAXSTRING 1024
>extern int errno;

>void main() {

int main(void) {

Quote:>   char outbuf[MAXSTRING], inbuf[MAXSTRING];
>   int spnum, nr, nw;   /* return val of sprintf, return val of read(),
>write() */
>   int fdstat;
>   char *fname = "stattest.txt";
>   struct stat my_stat;
>   time_t now;       /* 32 bit integer to hold time */

The comment may or may not be true.  The reason for using time_t
is to hide the details of exactly what it is.  You don't know if
that is a 32 bit integer or not.  And you don't care either!

Quote:>   int response;

>   if((fdstat = open (fname, O_RDWR | O_CREAT, S_IREAD | S_IWRITE)) < 0)

>      printf("error on open\n");    /* get a descriptor */

You test to see if it fails, and notifiy the user... and then go
right ahead and execute the rest of the code as if nothing had
gone wrong!  Add this:

       exit(EXIT_FAILURE);

Quote:

>   while (1) {
>      stat (fname, &my_stat);          /* get details on the file */
>      printf("\nLast accessed %d\n", my_stat.st_atime);
>      printf("Last modified %d\n\n", my_stat.st_mtime);

The type of my_stat.st_mtime is size_t, which like time_t is
another abstraction to hide an unknown type.  But printf() does
have to know what it is getting, so it needs a cast to something
specific.  You have told printf that it will be a type int, so
it should be cast to that.  That will most likely be fine for
this program, but you might be better off in the general case
using this:

 printf("\nLast accessed %lu\n", (unsigned long) my_stat.st_atime);
 printf("Last modified %lu\n\n", (unsigned long) my_stat.st_mtime);

Quote:>      printf("Would you like to make a change to the file? ");
>      response = getc(stdin); getc(stdin);

What happens if the user types in "yes" and hits return...
(hint: it blows away your program.)  At the end of this article
I've included a function you can use here to fetch the response.

Quote:>      now = time(NULL);
>      if (response == 'Y' || response == 'y') {             /* modify
>the file */
>         lseek (fdstat, 0L, SEEK_END);

This works, but it is going to be confusing when you run the
program.  You are going to append each change to the end of the
file.  Later you read and print out the first few characters of
the file, which as soon as you get it to work you'll no doubt
want to see more of... but it will never change.  I would use:

 lseek (fdstat, 0L, SEEK_SET);

Quote:>         spnum = sprintf(outbuf, "Time is %s\tsec = %d\n\n",
>ctime(&now), now);

Here you'll need to cast "now" to something useful.  I would
change it to an unsigned long and change the format specifier,
but you could cast it to an int if your machine is using 32 bit
ints and it will probably be OK for a few more years.

Quote:>         if ((nw = write(fdstat, outbuf, spnum)) != spnum)
>/* write the time to the file */
>            printf("error on write: %s\n", strerror(errno));

Once again, if this fails you make no effort to halt the
program.  Add an exit here.

Quote:>         printf("Modifying file\n");
>      }
>      else if (response == 'q' || response == 'Q')
>       break;

>      printf("Would you like to access the the file? ");
>      response = getc(stdin); getc(stdin);

>      if (response =='Y' || response == 'y') {             /* access the

>file */
>         lseek (fdstat, 0L, SEEK_SET);
>         if ((nr = read(fdstat, inbuf, 10)) < 0) {           /* just
>read an arbitrary amount */

Why not have that read out spnum bytes?

Quote:>          printf("error on read: %s\n", strerror(errno));
>            break;

This time you did exit on error!

Quote:>         }
>         inbuf[nr] = NULL;

Ouch.  NULL is a pointer, not a char.  

 inbuf[nr] = '\0';

Quote:>         printf("Accessing file\n");
>         printf("inbuf = %s\n", inbuf);
>      }
>      else if (response == 'q' || response == 'Q')
>       break;
>   }

Since you'll have changed main to properly return an int, it
should:

 return EXIT_SUCCESS;

Quote:>}

Here is a function you can use to fetch a response from the
user.  It will grab an entire line of text, skip any leading
white space, and return the first printable character while
discarding everything else.  That first printable character must
fall within the size specified for the input buffer, and if
there is more whitespace than that in front of it it will be
missed.

It returns the first alpha character, otherwise...
  It returns EOF on error or end of file.  
  It returns a newline if a blank line. or
     a line with no alpha characters is entered.

Input stops when a newline is read or end of file is detected.

#include <stdio.h>
#include <ctype.h>

int
respond(void)
{
  int ch, junk;

  while (( ch = getchar()) != EOF) {
    if (ch == '\n') {
      break;
    }
    if (isalpha(ch)) {
      /* flush the stdio input buffer */
      while ((junk = getchar()) != EOF && junk != '\n') {
        /* empty loop */
      }
      break;
    }
  }
  return ch;

Quote:}

  Floyd

--

Ukpeagvik (Barrow, Alaska)

 
 
 

Need help with stat()

Post by Barry Margoli » Tue, 18 Apr 2000 04:00:00




Did your monstrous critique of the program ever answer the original
question about why st_atime isn't being updated?

The answer to the original question may be that the file is on an NFS
server and the page is already in the local machine's buffer.  The server
only updates the access time when it receives a read request from the
network; if the client already has it buffered, it doesn't need to send
another read request.

--

Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

 
 
 

Need help with stat()

Post by Floyd Davidso » Tue, 18 Apr 2000 04:00:00






>Did your monstrous critique of the program ever answer the original
>question about why st_atime isn't being updated?

The code as originally posted produced 10 warnings from gcc in
only 61 lines of code.  There is no way to be sure *what* the
program results meant.

Quote:>The answer to the original question may be that the file is on
>an NFS server and the page is already in the local machine's
>buffer.  The server only updates the access time when it
>receives a read request from the network; if the client already
>has it buffered, it doesn't need to send another read request.

That is the most likely true, but given the original code, and
the accompanying comments, it is difficult to know.  The
question he asked was how to correctly *use* the stat()
function, not how to interpet the results.  Once he has a
functionally correct program, then knowing what the results mean
becomes useful information.  

It is nice that you have provided him with the answer to his
next question, which I did not.

--

Ukpeagvik (Barrow, Alaska)

 
 
 

Need help with stat()

Post by Barry Margoli » Tue, 18 Apr 2000 04:00:00





>That is the most likely true, but given the original code, and
>the accompanying comments, it is difficult to know.  The
>question he asked was how to correctly *use* the stat()
>function, not how to interpet the results.  Once he has a
>functionally correct program, then knowing what the results mean
>becomes useful information.  

He specifically asked how to use it to get the *access* time; that sure
seems like asking about how to interpret the results.  He said his program
was working for modification times, and presumably all your comments apply
just as well to an analogous program that has been s/st_atime/st_mtime/'ed.

--

Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

 
 
 

Need help with stat()

Post by Nate Eldredg » Tue, 18 Apr 2000 04:00:00






> Did your monstrous critique of the program ever answer the original
> question about why st_atime isn't being updated?

> The answer to the original question may be that the file is on an NFS
> server and the page is already in the local machine's buffer.  The server
> only updates the access time when it receives a read request from the
> network; if the client already has it buffered, it doesn't need to send
> another read request.

Another question: Does st_atime work at all (i.e. is it ever different
from st_mtime)?  Some filesystems don't support it at all.  Most of
these are not really Unix filesystems (FAT, for instance).  So make
sure this is on a sane fs.

--

Nate Eldredge

 
 
 

Need help with stat()

Post by Paul Cantalup » Tue, 18 Apr 2000 04:00:00


Hello,

    Thank you for your input into my program.  I am indeed working on an NFS.  I
moved my program to the /tmp folder and it works fine there.  Funny thing is that I
*have* to recompile the source code *in* the /tmp folder for the st_atime member to
be updated.  In other words, I cannot just "mv" my program to /tmp.  Does anybody
have any idea why this is so?

Thanks again

Paul

 
 
 

Need help with stat()

Post by Floyd Davidso » Tue, 18 Apr 2000 04:00:00





>>That is the most likely true, but given the original code, and
>>the accompanying comments, it is difficult to know.  The
>>question he asked was how to correctly *use* the stat()
>>function, not how to interpet the results.  Once he has a
>>functionally correct program, then knowing what the results mean
>>becomes useful information.  

>He specifically asked how to use it to get the *access* time; that sure
>seems like asking about how to interpret the results.  He said his program
>was working for modification times, and presumably all your comments apply
>just as well to an analogous program that has been s/st_atime/st_mtime/'ed.

The original code invoked undefined behavior in so many
different places that if it did happen to print anything out
correctly it was an accident.  There might indeed be other
useful assistance, but demonstrating how to use stat() correctly
and how to correctly print the results is not unhelpful either,
eh?

--

Ukpeagvik (Barrow, Alaska)

 
 
 

Need help with stat()

Post by Barry Margoli » Wed, 19 Apr 2000 04:00:00




Quote:>Thank you for your input into my program.  I am indeed working on an NFS.  I
>moved my program to the /tmp folder and it works fine there.  Funny thing
>is that I
>*have* to recompile the source code *in* the /tmp folder for the st_atime
>member to
>be updated.  In other words, I cannot just "mv" my program to /tmp.  Does anybody
>have any idea why this is so?

The mv command leaves access and modification times unchanged, since it's
just renaming the file.  Even if you're moving to a different file system,
which requires it to copy and delete the original file, it resets the atime
and mtime of the new file back to the original file's values to maintain
the illusion.

--

Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.