RPM Package Manager, CVS Repository
  http://rpm5.org/cvs/
  ____________________________________________________________________________

  Server: rpm5.org                         Name:   Jeff Johnson
  Root:   /v/rpm/cvs                       Email:  j...@rpm5.org
  Module: rpm                              Date:   23-May-2017 06:55:11
  Branch: rpm-5_4                          Handle: 2017052304551100

  Modified files:           (Branch: rpm-5_4)
    rpm/rpmio               fanotify.c

  Log:
    - Take 2 ...

  Summary:
    Revision    Changes     Path
    1.1.2.3     +316 -230   rpm/rpmio/fanotify.c
  ____________________________________________________________________________

  patch -p0 <<'@@ .'
  Index: rpm/rpmio/fanotify.c
  ============================================================================
  $ cvs diff -u -r1.1.2.2 -r1.1.2.3 fanotify.c
  --- rpm/rpmio/fanotify.c      23 May 2017 04:53:56 -0000      1.1.2.2
  +++ rpm/rpmio/fanotify.c      23 May 2017 04:55:11 -0000      1.1.2.3
  @@ -1,265 +1,351 @@
  -/* Copyright 2015 Mark Haines
  +/*
  + *   File:   fanotify-example.c
  + *   Date:   Fri Nov 15 14:55:49 2013
  + *   Author: Aleksander Morgado <aleksan...@lanedo.com>
    *
  - * Licensed under the Apache License, Version 2.0 (the "License");
  - * you may not use this file except in compliance with the License.
  - * You may obtain a copy of the License at
  + *   A simple tester of fanotify in the Linux kernel.
    *
  - *     http://www.apache.org/licenses/LICENSE-2.0
  + *   This program is released in the Public Domain.
    *
  - * Unless required by applicable law or agreed to in writing, software
  - * distributed under the License is distributed on an "AS IS" BASIS,
  - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  - * See the License for the specific language governing permissions and
  - * limitations under the License.
  + *   Compile with:
  + *     $> gcc -o fanotify-example fanotify-example.c
  + *
  + *   Run as:
  + *     $> ./fanotify-example /path/to/monitor /another/path/to/monitor ...
    */
   
  -#include "system.h"
  +/* Define _GNU_SOURCE, Otherwise we don't get O_LARGEFILE */
  +#define _GNU_SOURCE
   
  -#if defined(HAVE_SYS_FANOTIFY_H)
  -#include <sys/fanotify.h>
  -#endif
  -
  -#include "debug.h"
  -
  -const char USAGE[] =
  -    "Usage: fanotify [FLAGS PATH]... [FLAGS]\n"
  -    "Watch a directory or filesytem using fanotify\n\n"
  -    "If no path is supplied then watch the current directory\n\n"
  -    "Examples:\n"
  -    "   fanotify CLOSE_WRITE\n"
  -    "       Watch the current directory\n"
  -    "   fanotify CLOSE_WRITE MOUNT\n"
  -    "       Watch the current filesytem\n"
  -    "   fanotify CLOSE_WRITE \"\" /home\n"
  -    "       Watch the /home directory\n";
  -
  -struct flag {
  -    uint64_t mask;
  -    const char *name;
  +#include <stdio.h>
  +#include <signal.h>
  +#include <string.h>
  +#include <unistd.h>
  +#include <stdlib.h>
  +#include <poll.h>
  +#include <errno.h>
  +#include <limits.h>
  +#include <sys/stat.h>
  +#include <sys/signalfd.h>
  +#include <fcntl.h>
  +
  +#include <linux/fanotify.h>
  +
  +/* Structure to keep track of monitored directories */
  +typedef struct {
  +  /* Path of the directory */
  +  char *path;
  +} monitored_t;
  +
  +/* Size of buffer to use when reading fanotify events */
  +#define FANOTIFY_BUFFER_SIZE 8192
  +
  +/* Enumerate list of FDs to poll */
  +enum {
  +  FD_POLL_SIGNAL = 0,
  +  FD_POLL_FANOTIFY,
  +  FD_POLL_MAX
   };
   
  -static const struct flag MASKS[8] = {
  -#define      _ENTRY(_v)      { FAN_##_v, #_v, }
  -    _ENTRY(OPEN),
  -    _ENTRY(ACCESS),
  -    _ENTRY(MODIFY),
  -    _ENTRY(CLOSE),
  -    _ENTRY(CLOSE_WRITE),
  -    _ENTRY(CLOSE_NOWRITE),
  -    _ENTRY(ONDIR),
  -    _ENTRY(EVENT_ON_CHILD),
  -#undef       _ENTRY
  -};
  -static const unsigned int MASK_COUNT = sizeof(MASKS)/sizeof(MASKS[0]);
  +/* Setup fanotify notifications (FAN) mask. All these defined in fanotify.h. 
*/
  +static uint64_t event_mask =
  +  (FAN_ACCESS |         /* File accessed */
  +   FAN_MODIFY |         /* File modified */
  +   FAN_CLOSE_WRITE |    /* Writtable file closed */
  +   FAN_CLOSE_NOWRITE |  /* Unwrittable file closed */
  +   FAN_OPEN |           /* File was opened */
  +   FAN_ONDIR |          /* We want to be reported of events in the directory 
*/
  +   FAN_EVENT_ON_CHILD); /* We want to be reported of events in files of the 
directory */
  +
  +/* Array of directories being monitored */
  +static monitored_t *monitors;
  +static int n_monitors;
  +
  +static char *
  +get_program_name_from_pid (int     pid,
  +                        char   *buffer,
  +                        size_t  buffer_size)
  +{
  +  int fd;
  +  ssize_t len;
  +  char *aux;
  +
  +  /* Try to get program name by PID */
  +  sprintf (buffer, "/proc/%d/cmdline", pid);
  +  if ((fd = open (buffer, O_RDONLY)) < 0)
  +    return NULL;
  +
  +  /* Read file contents into buffer */
  +  if ((len = read (fd, buffer, buffer_size - 1)) <= 0)
  +    {
  +      close (fd);
  +      return NULL;
  +    }
  +  close (fd);
   
  -static const struct flag FLAGS[] = {
  -#define      _ENTRY(_v)      { FAN_MARK_##_v, #_v, }
  -    _ENTRY(DONT_FOLLOW),
  -    _ENTRY(ONLYDIR),
  -    _ENTRY(MOUNT),
  -    _ENTRY(IGNORED_MASK),
  -#undef       _ENTRY
  -};
  -static const unsigned int FLAG_COUNT = sizeof(FLAGS)/sizeof(FLAGS[0]);
  +  buffer[len] = '\0';
  +  aux = strstr (buffer, "^@");
  +  if (aux)
  +    *aux = '\0';
  +
  +  return buffer;
  +}
  +
  +static char *
  +get_file_path_from_fd (int     fd,
  +                    char   *buffer,
  +                    size_t  buffer_size)
  +{
  +  ssize_t len;
  +
  +  if (fd <= 0)
  +    return NULL;
  +
  +  sprintf (buffer, "/proc/self/fd/%d", fd);
  +  if ((len = readlink (buffer, buffer, buffer_size - 1)) < 0)
  +    return NULL;
  +
  +  buffer[len] = '\0';
  +  return buffer;
  +}
  +
  +static void
  +event_process (struct fanotify_event_metadata *event)
  +{
  +  char path[PATH_MAX];
   
  -uint64_t parse_flags(const struct flag *flags, unsigned flag_count,
  -                  const char *pos)
  +  printf ("Received event in path '%s'",
  +          get_file_path_from_fd (event->fd,
  +                                   path,
  +                                   PATH_MAX) ?
  +          path : "unknown");
  +  printf (" pid=%d (%s): \n",
  +          event->pid,
  +          (get_program_name_from_pid (event->pid,
  +                                        path,
  +                                        PATH_MAX) ?
  +           path : "unknown"));
  +
  +  if (event->mask & FAN_OPEN)
  +    printf ("\tFAN_OPEN\n");
  +  if (event->mask & FAN_ACCESS)
  +    printf ("\tFAN_ACCESS\n");
  +  if (event->mask & FAN_MODIFY)
  +    printf ("\tFAN_MODIFY\n");
  +  if (event->mask & FAN_CLOSE_WRITE)
  +    printf ("\tFAN_CLOSE_WRITE\n");
  +  if (event->mask & FAN_CLOSE_NOWRITE)
  +    printf ("\tFAN_CLOSE_NOWRITE\n");
  +  fflush (stdout);
  +
  +  close (event->fd);
  +}
  +
  +static void
  +shutdown_fanotify (int fanotify_fd)
   {
  -    uint64_t mask = 0;
  -    unsigned j;
  -    char const *next = pos;
  -
  -    while (*next) {
  -     /* Scan through the ',' separated list of flags */
  -     next = strchr(pos, ',');
  -     if (!next)
  -         next = pos + strlen(pos);
  -
  -     for (j = 0; j < flag_count; ++j) {
  -         const struct flag *fp = flags + j;
  -         if (((int) strlen(fp->name) == (next - pos))
  -             && (strncmp(fp->name, pos, (next - pos)) == 0)) {
  -             /* If the flag has a name we know then add it to the mask */
  -             mask |= fp->mask;
  -             break;
  -         }
  -     }
  -     if (j == flag_count) {
  -         /* If we don't know what the flag is then exit */
  -         fprintf(stderr, "Unknown value: \"%*s\"\nPossible values are:\n",
  -                             (int) (next - pos), pos);
  -         for (j = 0; j < flag_count; ++j)
  -             fprintf(stderr, "    %s\n", flags[j].name);
  -         exit(1);
  -     }
  -     pos = next + 1;
  +  int i;
  +
  +  for (i = 0; i < n_monitors; ++i)
  +    {
  +      /* Remove the mark, using same event mask as when creating it */
  +      fanotify_mark (fanotify_fd,
  +                     FAN_MARK_REMOVE,
  +                     event_mask,
  +                     AT_FDCWD,
  +                     monitors[i].path);
  +      free (monitors[i].path);
       }
  -    return mask;
  +  free (monitors);
  +  close (fanotify_fd);
   }
   
  -char *print_flags(char *te, char *tend,
  -               const struct flag *flags, unsigned flag_count,
  -               uint64_t mask)
  +static int
  +initialize_fanotify (int          argc,
  +                  const char **argv)
   {
  -    /* If the mask is empty then we don't print anything. */
  -    if (!mask)
  -     goto exit;
  -
  -    for (unsigned j = 0; j < flag_count; ++j) {
  -     const struct flag *fp = flags + j;
  -     if ((fp->mask & mask) != fp->mask)
  -         continue;
  -     /* Print the name of the bits */
  -     size_t length = strlen(fp->name);
  -     if ((tend - te) < (int) length)
  -         length = (tend - te);
  -     memcpy(te, fp->name, length);
  -     te += length;
  -     if (te < tend)
  -         *te++ = '|';
  -     /* Remove the bits from the mask. */
  -     mask &= ~fp->mask;
  +  int i;
  +  int fanotify_fd;
  +
  +  /* Create new fanotify device */
  +  if ((fanotify_fd = fanotify_init (FAN_CLOEXEC,
  +                                    O_RDONLY | O_CLOEXEC | O_LARGEFILE)) < 0)
  +    {
  +      fprintf (stderr,
  +               "Couldn't setup new fanotify device: %s\n",
  +               strerror (errno));
  +      return -1;
       }
   
  -    if (mask) {
  -     /* The mask contained some bits we don't know about. Print it as hex */
  -     te += snprintf(te, (tend - te), "0x%llx", (long long) mask);
  -    } else {
  -     /* We have written a trailing '|' character since the mask is set and
  -      * we known what all the bits mean. So we can safely move te one
  -      * character back to remove the trailing '|' */
  -     --te;
  +  /* Allocate array of monitor setups */
  +  n_monitors = argc - 1;
  +  monitors = malloc (n_monitors * sizeof (monitored_t));
  +
  +  /* Loop all input directories, setting up marks */
  +  for (i = 0; i < n_monitors; ++i)
  +    {
  +      monitors[i].path = strdup (argv[i + 1]);
  +      /* Add new fanotify mark */
  +      if (fanotify_mark (fanotify_fd,
  +                         FAN_MARK_ADD,
  +                         event_mask,
  +                         AT_FDCWD,
  +                         monitors[i].path) < 0)
  +        {
  +          fprintf (stderr,
  +                   "Couldn't add monitor in directory '%s': '%s'\n",
  +                   monitors[i].path,
  +                   strerror (errno));
  +          return -1;
  +        }
  +
  +      printf ("Started monitoring directory '%s'...\n",
  +              monitors[i].path);
       }
   
  -exit:
  -    return te;
  +  return fanotify_fd;
   }
   
  -int main(int argc, char const *argv[])
  +static void
  +shutdown_signals (int signal_fd)
   {
  -    int ec = 1;              /* assume failure */
  -    int rc;
  +  close (signal_fd);
  +}
   
  -    if (argc == 1) {
  -     fprintf(stdout, "%s", USAGE);
  -     goto exit;
  +static int
  +initialize_signals (void)
  +{
  +  int signal_fd;
  +  sigset_t sigmask;
  +
  +  /* We want to handle SIGINT and SIGTERM in the signal_fd, so we block 
them. */
  +  sigemptyset (&sigmask);
  +  sigaddset (&sigmask, SIGINT);
  +  sigaddset (&sigmask, SIGTERM);
  +
  +  if (sigprocmask (SIG_BLOCK, &sigmask, NULL) < 0)
  +    {
  +      fprintf (stderr,
  +               "Couldn't block signals: '%s'\n",
  +               strerror (errno));
  +      return -1;
       }
   
  -    /*
  -     * Create a fanotify_fd. We only need to be notified about events, and
  -     * we only want to read the files.
  -     */
  -    int fanfd = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY);
  -    if (fanfd < 0) {
  -     /*
  -      * The most likely reason to fail here is that we don't have
  -      * the CAP_SYS_ADMIN cabability needed by fanotify_init
  -      */
  -     if (errno == EPERM)
  -         fprintf(stderr, "fanotify needs to be run as root\n");
  -     else
  -         perror("fanotify_init");
  -     goto exit;
  +  /* Get new FD to read signals from it */
  +  if ((signal_fd = signalfd (-1, &sigmask, 0)) < 0)
  +    {
  +      fprintf (stderr,
  +               "Couldn't setup signal FD: '%s'\n",
  +               strerror (errno));
  +      return -1;
       }
   
  -    /*
  -     * In theory fanotify_mark should be able to take AT_FDCWD for the dirfd.
  -     * However it seems to complain if we pass AT_FDCWD to it. So instead we
  -     * open the current working directory and pass the resulting fd.
  -     */
  -    int cwdfd = openat(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY);
  -    if (cwdfd < 0) {
  -     perror("openat");
  -     goto exit;
  +  return signal_fd;
  +}
  +
  +int
  +main (int          argc,
  +      const char **argv)
  +{
  +  int signal_fd;
  +  int fanotify_fd;
  +  struct pollfd fds[FD_POLL_MAX];
  +
  +  /* Input arguments... */
  +  if (argc < 2)
  +    {
  +      fprintf (stderr, "Usage: %s directory1 [directory2 ...]\n", argv[0]);
  +      exit (EXIT_FAILURE);
       }
   
  -    for (int i = 1; i < argc; ++i) {
  -     /* Parse the mask bits from the first argument */
  -     uint64_t mask = parse_flags(MASKS, MASK_COUNT, argv[i]);
  -     unsigned int flags = FAN_MARK_ADD;
  -     char const *path = ".";
  -     /* Then parse the flags bits from the second argument */
  -     if (++i < argc)
  -         flags |= parse_flags(FLAGS, FLAG_COUNT, argv[i]);
  -     /* Then optionally set path using the third argument */
  -     if (++i < argc)
  -         path = argv[i];
  -     rc = fanotify_mark(fanfd, flags, mask, cwdfd, path);
  -     if (rc < 0) {
  -         perror("fanotify_mark");
  -         goto exit;
  -     }
  +  /* Initialize signals FD */
  +  if ((signal_fd = initialize_signals ()) < 0)
  +    {
  +      fprintf (stderr, "Couldn't initialize signals\n");
  +      exit (EXIT_FAILURE);
       }
   
  -    close(cwdfd);
  +  /* Initialize fanotify FD and the marks */
  +  if ((fanotify_fd = initialize_fanotify (argc, argv)) < 0)
  +    {
  +      fprintf (stderr, "Couldn't initialize fanotify\n");
  +      exit (EXIT_FAILURE);
  +    }
   
  -    for (;;) {
  -     char s[BUFSIZ];
  -     size_t ns = sizeof(s);
  -     char *se = s;
  -
  -     ssize_t nr = read(fanfd, s, ns);
  -     if (nr < 0) {
  -         perror("read");
  -         exit(1);
  -     }
  -     char *send = s + nr;
  -
  -     while (se < send) {
  -         struct fanotify_event_metadata * event
  -                     = (struct fanotify_event_metadata *) se;
  -         char t[BUFSIZ];
  -         size_t nt = sizeof(t);
  -         char *te = t;
  -         /* Leave space at the end of the output buffer for a '\n' */
  -         char *tend = t + nt - 1;
  -
  -         /* Check that we have enough input read an event structure. */
  -         if ((send - se) < (int) sizeof(*event)) {
  -             perror("Invalid fanotify_event_meta");
  -             goto exit;
  -         }
  -
  -         /* Check that we have all of the event structure and that it's
  -          * a version that we understand */
  -         if ((send - s) < event->event_len
  -          || event->vers != FANOTIFY_METADATA_VERSION)
  -         {
  -             perror("Invalid fanotify_event_meta");
  -             goto exit;
  -         }
  -
  -         /* Print the event mask. Each bit will be separated by '|'
  -          * characters. */
  -         te = print_flags(te, tend, MASKS, MASK_COUNT, event->mask);
  -
  -         /* Print the pid of the process that this is event is from */
  -         te += snprintf(te, (tend - te), " %d ", event->pid);
  -
  -         /* We aren't told the path of the event directly. But we can read
  -          * the /proc/self/fd/%d symlink to see what path the file
  -          * descriptor was opened with */
  -         char fn[32];
  -         snprintf(fn, sizeof(fn), "/proc/self/fd/%d", event->fd);
  -         nr = readlink(fn, te, (tend - te));
  -         if (nr < 0) {
  -             perror("readlink");
  -             goto exit;
  -         }
  -         te += nr;
  -         *te = '\0';
  -
  -         fprintf(stdout, "%s\n", t);
  -
  -         /* Close the event's file descriptor. */
  -         close(event->fd);
  -
  -         /* Advance to the next event in the input buffer */
  -         se += event->event_len;
  -     }
  +  /* Setup polling */
  +  fds[FD_POLL_SIGNAL].fd = signal_fd;
  +  fds[FD_POLL_SIGNAL].events = POLLIN;
  +  fds[FD_POLL_FANOTIFY].fd = fanotify_fd;
  +  fds[FD_POLL_FANOTIFY].events = POLLIN;
  +
  +  /* Now loop */
  +  for (;;)
  +    {
  +      /* Block until there is something to be read */
  +      if (poll (fds, FD_POLL_MAX, -1) < 0)
  +        {
  +          fprintf (stderr,
  +                   "Couldn't poll(): '%s'\n",
  +                   strerror (errno));
  +          exit (EXIT_FAILURE);
  +        }
  +
  +      /* Signal received? */
  +      if (fds[FD_POLL_SIGNAL].revents & POLLIN)
  +        {
  +          struct signalfd_siginfo fdsi;
  +
  +          if (read (fds[FD_POLL_SIGNAL].fd,
  +                    &fdsi,
  +                    sizeof (fdsi)) != sizeof (fdsi))
  +            {
  +              fprintf (stderr,
  +                       "Couldn't read signal, wrong size read\n");
  +              exit (EXIT_FAILURE);
  +            }
  +
  +          /* Break loop if we got the expected signal */
  +          if (fdsi.ssi_signo == SIGINT ||
  +              fdsi.ssi_signo == SIGTERM)
  +            {
  +              break;
  +            }
  +
  +          fprintf (stderr,
  +                   "Received unexpected signal\n");
  +        }
  +
  +      /* fanotify event received? */
  +      if (fds[FD_POLL_FANOTIFY].revents & POLLIN)
  +        {
  +          char buffer[FANOTIFY_BUFFER_SIZE];
  +          ssize_t length;
  +
  +          /* Read from the FD. It will read all events available up to
  +           * the given buffer size. */
  +          if ((length = read (fds[FD_POLL_FANOTIFY].fd,
  +                              buffer,
  +                              FANOTIFY_BUFFER_SIZE)) > 0)
  +            {
  +              struct fanotify_event_metadata *metadata;
  +
  +              metadata = (struct fanotify_event_metadata *)buffer;
  +              while (FAN_EVENT_OK (metadata, length))
  +                {
  +                  event_process (metadata);
  +                  if (metadata->fd > 0)
  +                    close (metadata->fd);
  +                  metadata = FAN_EVENT_NEXT (metadata, length);
  +                }
  +            }
  +        }
       }
  -    ec = 0;
   
  -exit:
  -    return ec;
  +  /* Clean exit */
  +  shutdown_fanotify (fanotify_fd);
  +  shutdown_signals (signal_fd);
  +
  +  printf ("Exiting fanotify example...\n");
  +
  +  return EXIT_SUCCESS;
   }
  @@ .
______________________________________________________________________
RPM Package Manager                                    http://rpm5.org
CVS Sources Repository                                rpm-cvs@rpm5.org

Reply via email to