?
Solved

tail c code

Posted on 2000-01-10
6
Medium Priority
?
896 Views
Last Modified: 2013-12-26
Where can I find c code for the UNIX 'tail' command?  Or something that works the same as 'tail -f'?

Thanks!
0
Comment
Question by:jeremyandrews
  • 3
  • 3
6 Comments
 
LVL 1

Accepted Solution

by:
Anju111599 earned 150 total points
ID: 2340201
Just use popen() to do a "tail -f" and read from the pipe.  

Try out the source below.  It tails a file until it finds the user-supplied search string.

Anju

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <libgen.h>
#include <sys/ioctl.h>


void main( int iArgCount, char *aArgList[] )
{
      char   szProgramName[128];
      fd_set sReadFDs;
      FILE   *pFile;
      int    iFileDescriptor;
      char   szBuffer[2048];
      char   *pBuffer;
      char   szSearch[2048];
      char   szFileName[128];
      char   szCommand[128];
      int    iSearchLength;
      int    iRetVal;
      struct timeval sTimeout;
      long   lBytesAvail;
      int    fFound = 0;

  strcpy( szProgramName, basename(aArgList[0]) );
      fprintf( stderr, "%s -- wait for a string from a file.\n", szProgramName );

  strcpy( szSearch, "junk." );
      strcpy( szFileName, "stdin" );

  if (iArgCount < 2)
    {
    fprintf( stderr, "usage:  %s inputfile [searchstring]\n", szProgramName );
    exit(1);
    }
  strcpy( szFileName, aArgList[1] );
  sprintf( szCommand, "tail -f %s\n", szFileName );
  pFile = popen( szCommand, "r" );
  if (pFile == NULL)
    {
    fprintf( stderr,
             "Couldn't open %s for reading:  %s\n",
             szFileName,
             strerror(errno)
           );
    exit(1);
    }

  if (iArgCount == 3)
    strncpy( szSearch, aArgList[2], sizeof(szSearch) );

  iSearchLength = strlen(szSearch);

  iFileDescriptor = fileno(pFile);

  printf( "   Waiting for: \"%s\"\n", szSearch );
  printf( "   From file:   \"%s\"\n", szFileName );
  while (!fFound)
    {
    sTimeout.tv_sec  = 5;
    sTimeout.tv_usec = 0;
    FD_ZERO( &sReadFDs );
    FD_SET( iFileDescriptor, &sReadFDs );
    iRetVal = select( FD_SETSIZE, &sReadFDs, NULL, NULL, &sTimeout );
    if (iRetVal == -1)
      {
      perror( "select failed" );
      exit(1);
      }
    if (FD_ISSET(iFileDescriptor, &sReadFDs))
      {
      iRetVal = ioctl( iFileDescriptor, FIONREAD, &lBytesAvail );
                  if (iRetVal)
                        {
                        fprintf( stderr, "ioctl:  %s\n", strerror(errno) );
                        }
                  if (lBytesAvail > 0L)
        {
                    while ((pBuffer = fgets( szBuffer, sizeof(szBuffer), pFile )))
          {
          printf( "." );
          if (strstr( szBuffer, szSearch ) != NULL)
            {
                                    fFound = 1;
                                    break;
                                    }
          }
        }
      }
    }

  printf( "\n" );
      printf( "Found.\n" );
  exit(0);
}
0
 

Author Comment

by:jeremyandrews
ID: 2341748
Thanks.  This is not the source code for tail, but it will serve my purpose.  I can easily adapt it to what I want to do.

(BTW:  If anyone out there knows where I can find the source for tail, I'm still interested.)

Thanks again!  :)
0
 
LVL 1

Expert Comment

by:Anju111599
ID: 2345087
Check the RedHat ftp site, or the gnu site.  You should be able to find it at either of them.

Anju
0
Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 

Author Comment

by:jeremyandrews
ID: 2345889
Thanks.  :)

I wrote what I needed.  Here's the code that worked:
------------------------------
#include <stdio.h>
#include <errno.h>

void main ( int iArgCount, char *aArgList[] )
{
  FILE    *pFile;
  char    szProgramName[128];
  char    szBuffer[2048];
  char    *pBuffer;
  char    szTailFile[2048];
  long    lFileLength;
  long    lBufferLength;

  strcpy( szProgramName, basename(aArgList[0]) );
  strcpy( szTailFile, "testfile" );                           /* Default file to tail */

  if (iArgCount == 2)                                         /* aArgList[1] is alternative file to tail */
  {
    strncpy ( szTailFile, aArgList[1], sizeof(szTailFile) );
  }
  else if (iArgCount > 2)                                     /* Too many arguments */
  {
    printf( "Usage: %s [filename]\n", szProgramName );
    exit(1);
  }

  if ( (pFile = fopen ( szTailFile, "r")) != NULL)            /* Attempt to open file to tail */
  {

    fseek ( pFile, 0L, SEEK_END );
    if ( (lFileLength = (ftell (pFile))) != -1L)                 /* Get pointer to end of file */
    {
      fclose ( pFile );

      while (1)
      {
        sleep (1);
        if ( (pFile = fopen ( szTailFile, "r")) != NULL)
        {
          fseek ( pFile, 0L, SEEK_END );
          if ( (lBufferLength = (ftell (pFile))) != -1L)
          {
            if (lFileLength < lBufferLength)
            {
              fseek ( pFile, lFileLength, SEEK_SET);
              while ( (pBuffer = fgets( szBuffer, sizeof(szBuffer), pFile )))
              {
                printf( "%s", pBuffer);
              }
              if ( (lFileLength = (ftell (pFile))) == -1L)
              {
                printf( "ERROR!\n" );
                exit(1);
              }
              fclose ( pFile );
            }
            else if (lFileLength > lBufferLength)
            {
              if ( (lFileLength = (ftell (pFile))) == -1L)
              {
                printf( "ERROR!\n" );
                exit(1);
              }
              fclose ( pFile );
            }
          }
        }
      }
    }
  }
  else
    printf( "Unable to open \"%s\": %s\n", szTailFile, strerror(errno) );
}
0
 
LVL 1

Expert Comment

by:Anju111599
ID: 2346824
Excellent!  Short, sweet, and to-the-point :)  Of course, I have an observation to make from a purist, nit-picking point of view ;)

The sleep and poll method makes your implementation slow to respond when the file is written to, which would be fine for 99% of the applications where one would want to tail a file.  The advantage to this approach is that the code to implement it is simple and easy to maintain.

The implementation I sent you contains a call to select to force the process to sleep until there is work to do. However, it responds very quickly when the file is written to, hence it is an event-driven approach rather than a polling approach.  It was written to tail a log file from another process to search for a specific string, then return so the script which launched it can start another process immediately.  The second process must interact with the first process which produced the log file, but can't be launched until a specific event has occurred.  This approach is more complicated and harder to maintain, but responds more quickly for applications where response time is important.

If you already knew all this, please excuse my boring comments.  But if you didn't, it was my hope to provide you with more tools for your arsenal of  problem-solving skills :)

Anju
0
 

Author Comment

by:jeremyandrews
ID: 2356527
Anju,

For my purpose, an up to 1 second delay is not a problem.  (I'm tailing an alarm file and sending the alarms via a UDP stream to our alarm server.)  

But then, it sounds as though your method would be more efficient as well, as your method wouldn't have to poll the system every second?  I'll take another look at your code and attempt to understand the call to select...  This is the first c program I've written in many years!

Thanks for your help and ideas!  I appreciate the comments entirely.  :)

-Jeremy
0

Featured Post

The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Load and Save to file, Document-View interaction inside the SDI. Continuing from the second article about sudoku.   Open the project in visual studio. From the class view select CSudokuDoc and double click to open the header …
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
If you are looking for an automated solution for backup single or multiple Office 365 user mailboxes to Outlook data file, then you can use Kernel Office 365 Backup & Restore tool. Go through the video to check out the steps to backup single or mult…

601 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question