AIX:AHAFS ahaevent.c: Difference between revisions

From RoggeWiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
Line 276: Line 276:
   
   
     return(rc);
     return(rc);
}
}
   
   
int
int
main(int argc, char *argv[])
main(int argc, char *argv[])
{
{
     int    c, i = 0;
     int    c, i = 0;
     int    fd, rc, bytes, err=0, restart;
     int    fd, rc, bytes, err=0, restart;

Latest revision as of 15:26, 26 May 2015

/*
 * mon_modFile_event.c
 *
 * This program monitors for modifications to a file specified by the user.
 * Event occurrences are printed to the user.  If the file being monitored is
 * removed or renamed, it will be recreated and monitoring will continue.
 *
 * If the filesystem containing the file is unmounted, or the file itself is
 * overmounted, monitoring will cease and the program will exit.
 *
 * This program assumes the AIX Event Infrastructure file system has been
 * mounted on /aha.  To mount it, run:
 *      mkdir /aha
 *      mount -v ahafs /aha /aha
 *
 */ 

#include <stdio.h>
#include <sys/poll.h>
#include <sys/pollset.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <libgen.h>
#include <usersec.h>
#include <sys/ahafs_evProds.h>

#define RDWR_BUF_SIZE   4096

int    sequence_num;
char   *objName;
ushort objMode;
uid_t  objUid;
gid_t  objGid;

/* NAME:    skip_lines
 * PURPOSE: Skips a specified number of lines in the buffer passed in.
 * PARAMETERS:
 *      p - Address of the pointer to the head of the buffer
 *      n - The number of lines to skip
 * RETURNS:
 *      Total number of lines skipped
 */
int
skip_lines(char **p, int n)
{
    int lines = 0;

    while(n > 0)
    {
        *p = strchr(*p, '\n');
        if(!p)
            return(lines);

        (*p)++;
        n--;
        lines++;
    }

    return(lines);
}

/* NAME:    print_op
 * PURPOSE: Prints a string indicating what monitored file operation triggered
 *          the event occurrence.
 * PARAMETERS:
 *      evp_rc - The return code from RC_FROM_EVPROD
 * RETURNS:
 *      Nothing.
 */
void
print_op(int evp_rc)
{
    if(evp_rc == AHAFS_MODFILE_WRITE)
        printf("File written.");
    else if(evp_rc == AHAFS_MODFILE_UNMOUNT)
        printf("Filesystem unmounted.");
    else if(evp_rc == AHAFS_MODFILE_MAP)
        printf("File mapped.");
    else if(evp_rc == AHAFS_MODFILE_REMOVE)
        printf("File removed.");
    else if(evp_rc == AHAFS_MODFILE_RENAME)
        printf("File renamed.");
    else if(evp_rc == AHAFS_MODFILE_FCLEAR)
        printf("File cleared.");
    else if(evp_rc == AHAFS_MODFILE_FTRUNC)
        printf("File truncated.");
    else if(evp_rc == AHAFS_MODFILE_OVERMOUNT)
        printf("File overmounted.");
    else
        printf("Unknown file op: %d", evp_rc);
}

/* NAME:    mk_subdirs
 * PURPOSE: Creates necessary subdirectories in the AIX Event Infrastructure
 *          file system for monitoring the object specified.
 * RETURNS:
 *      Return code from mkdir call
 */
int
mk_subdirs()
{
    char cmd[2048];
    char *p; 

    /* Strip off the monitor file name */
    p = strrchr(objName, '/');

    if(p == NULL)
        return(0); 

    sprintf(cmd, "/usr/bin/mkdir -p /aha/fs/modFile.monFactory/");
    strncat(cmd, objName, (p - objName));
    return(system(cmd));
} 

/* NAME:    parse_data
 * PURPOSE: This function will parse the event occurrence data and take
 *          corrective action if necessary.
 * PARAMETERS:
 *      buf - A pointer to the buffer containing the event occurrence data
 *      err - Indicates if the previous select() call returned an error
 *            (a different parsing format is required).
 * RETURNS:
 *        0 - No corrective action needed.
 *        1 - Corrective action taken, monitoring must be restarted.
 *        2 - Unrecoverable error in parsing
 */
int
parse_data(char *buf, int err)
{
    int    rc = 0, evp_rc, recreate = 0;
    char   *p;
    time_t sec, nsec;
    int    seq_num;
    pid_t  pid;
    uid_t  uid, gid;
    gid_t  luid;
    char   curTm[64], cmd[64];
    char   uname[64], lname[64], gname[64]; 

    p = buf; 

    /* Check for BUF_WRAP */
   if(strncmp(buf, "BUF_WRAP", strlen("BUF_WRAP")) == 0)
   {
       printf("Buffer wrap detected, Some event occurrences lost!\n");
       return(0);
   }

   /* Since we are using the default buffer size (4K), and have specified
    * INFO_LVL=1, we won't see any EVENT_OVERFLOW conditions.  Applications
    * should check for this keyword if they are using an INFO_LVL of 2 or
    * higher, and have a buffer size of <= 4K
    */

   /* Skip "BEGIN_EVENT_INFO" header */
   if(skip_lines(&p, 1) != 1)
       return(2);

   /* Get timestamp and sequence number. */
   if(sscanf(p,"TIME_tvsec=%ld\nTIME_tvnsec=%ld\nSEQUENCE_NUM=%d\n",
       &sec, &nsec, &seq_num) == 3)
   {
       ctime_r(&sec, curTm);
       if(skip_lines(&p, 3) != 3)
           return(2);

       printf("Time            : %s", curTm);
       printf("Seq num         : %d", seq_num);
       if(seq_num != sequence_num)
           printf(" (%d duplicates)\n", (seq_num - sequence_num));
       else
           printf("\n");

       sequence_num = seq_num + 1;
   }
   else
       return(2);

   if(err)
   {
       /* We just expect to see the RC_FROM_EVPROD and the "END_EVENT_DATA"
        * footer after the timestamp and sequence number in the error case */
       if(sscanf(p, "RC_FROM_EVPROD=%d\nEND_EVENT_DATA", &evp_rc) == 1)
       {
           printf("Error in event monitoring: %d.", evp_rc);
           if(evp_rc == ENODEV)
           {
               /* Object being monitored doesn't exist, recreate it */
               printf(" Recreating object.\n");
               recreate = 1;
           }
           else
           {
               /* Some other error occurred which we can't correct */
               printf(" Unable to recover.\n");
               rc = 2;
           }
       }
       else
           return(2);
   }
   else
   {
       /* Collect user and process info */
       if(sscanf(p,
               "PID=%ld\nUID=%ld\nUID_LOGIN=%ld\nGID=%ld\nPROG_NAME=%s\n",
               &pid, &uid, &luid, &gid, cmd) == 5)
       {
           strcpy(uname, IDtouser(uid));
           strcpy(lname, IDtouser(luid));
           strcpy(gname, IDtogroup(gid));

           printf("Process ID  : %d\n", pid);
           printf("User Info   : userName=%s, loginName=%s, groupName=%s\n",
                      uname, lname, gname);
           printf("Program Name        : %s\n", cmd);

           /* Get the RC_FROM_EVPROD */
           if(skip_lines(&p, 5) != 5)
               return(2);

           if(sscanf(p, "RC_FROM_EVPROD=%d\nEND_EVENT_DATA", &evp_rc) == 1)
           {
               print_op(evp_rc);

               switch(evp_rc)
               {
                   case AHAFS_MODFILE_REMOVE:
                   case AHAFS_MODFILE_RENAME:
                       /* Recreate the object */
                       printf(" Recreating object.\n\n");
                       recreate = 1;
                       break;
                   case AHAFS_MODFILE_OVERMOUNT:
                   case AHAFS_MODFILE_UNMOUNT:
                       /* Unrecoverable "unavailable" events. */
                       printf(" Unable to recover.\n\n");
                       rc = 2;
                       break;
                   default:
                       printf(" No action required.\n\n");
               }
           }
           else
               return(2);
       }
       else
           return(2);

   }

   if(recreate)
   {
       int fd;
       fd = creat(objName, objMode);
       if(fd != -1)
       {
           rc = chown(objName, objUid, objGid);
       }

       if((fd == -1) || rc)
       {
           perror("Error recreating object");
           rc = 2;
       }
       else
           rc = 1;
   }

   return(rc);
}

int
main(int argc, char *argv[])
{
   int    c, i = 0;
   int    fd, rc, bytes, err=0, restart;
   fd_set readfds;
   char   monFile[PATH_MAX];
   char   resultData[RDWR_BUF_SIZE];
   char   monFileWrStr[RDWR_BUF_SIZE];
   struct stat statbuf;

   if(argc != 2)
   {
       printf("Usage: %s <full path to file to monitor>\n", argv[0]);
       printf("  Example: %s /etc/passwd\n", argv[0]);
       return(-1);
   }


   /* Create monitor file name for object */
   objName = argv[1];
   sprintf(monFile, "/aha/fs/modFile.monFactory");
   if((strlen(monFile) + strlen(objName) + 5) > PATH_MAX)
   {
       fprintf(stderr, "Error: Cannot monitor object, path name too long\n");
       return(ENAMETOOLONG);
   }

   /* Make the necessary subdirectories for the monitor file */
   if(rc = mk_subdirs())
       return(rc);

   strcat(monFile, objName);
   strcat(monFile, ".mon");

   /* Save off the mode and ownership of monitored object */
   if(stat(objName, &statbuf) < 0)
   {
       perror("Error stating file");
       return(errno);
   }

   objGid = statbuf.st_gid;
   objUid = statbuf.st_uid;
   objMode = statbuf.st_mode;

open:
   restart = 0;
   sequence_num = 0;

   /* Open the monitor file, creating it if necessary */
   fd = open(monFile, O_CREAT|O_RDWR);
   if(fd < 0)
   {
       perror("Error opening monitor file");
       return(errno);
   }

   /* Write out the monitoring specifications.
    * In this case, we are monitoring for a state change event type
    * (modFile):
    *    CHANGED=YES
    * We will be waiting in select call, rather than a read:
    *    WAIT_TYPE=WAIT_IN_SELECT
    * we only want minimal information:
    *    INFO_LVL=1
    */
   sprintf(monFileWrStr, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=1");

   rc = write(fd, monFileWrStr, strlen(monFileWrStr)+1);
   if (rc < 0)
   {
       perror("Error writing to monitor file");
       return(errno);
   }

   /* Keep monitoring for event occurrences until stopped */
   while(1)
   {
       /* Initialize the set */
       FD_ZERO(&readfds);
       FD_SET(fd, &readfds);
       err = 0;

       rc = select(fd+1, &readfds, NULL, NULL, NULL);
       if (rc <= 0)
       {
           /* All errors in event monitoring will cause select to return
            * EBADF.  Read to see if any additional data is available.
            */
           perror("Error issuing select");
           err = 1;
       }


       bytes = pread(fd, resultData, RDWR_BUF_SIZE, 0);
       if(bytes < 0)
           perror("Error reading monitor file");
       else if(bytes == 0)
           fprintf(stderr,
               "Error reading monitor file.  No data to be read\n");
       else
           restart = parse_data(resultData, err);

       if(restart == 2)
           break;

       if(restart)
       {
           close(fd);
           goto open;
       }
   }
   close(fd);
   return(err);

}