Logo Search packages:      
Sourcecode: s390-tools version File versions  Download package

mon_fsstatd.c

/*
 * mon_fsstatd
 * Author: Melissa Howland <melissa.howland@us.ibm.com>
 *
 * Copyright IBM Corp. 2006.
 *
 * Daemon that writes filesystem utilization data to the z/VM monitor stream.
 */
#include <unistd.h>
#include <mntent.h>
#include <sys/vfs.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <time.h>
#include <stdarg.h>
#include <linux/types.h>
#include <sys/statvfs.h>
#include <sys/stat.h>
#include <getopt.h>
#include <signal.h>
#include "mon_fsstatd.h"

static int attach;
static int mw_dev;
static char small_mon_record[SMALL_MON_RECORD_LEN];
static char large_mon_record[LARGE_MON_RECORD_LEN];
static long sample_interval = 60;

static const char *pid_file = "/var/run/mon_fsstatd.pid";

00035 struct mw_name_lens {
      __u16  mw_name_len;
      __u16  mw_dir_len;
      __u16  mw_type_len;
      __u16  mw_fsdata_len;
      __u16  mw_total;
};

/*
 * Clean up when SIGTERM or SIGINT received
 */
void stop_fsstatd(int UNUSED(a))
{
      remove(pid_file);
      exit(0);
}

/*
 * Set up for handling SIGTERM or SIGINT
 */
static void fsstatd_handle_signals(void)
{
      struct sigaction act;

      act.sa_flags = 0;
      sigemptyset(&act.sa_mask);

      act.sa_handler = stop_fsstatd;
      if (sigaction(SIGTERM, &act, NULL) < 0) {
            fprintf(stderr, "sigaction( SIGTERM, ... ) failed - "
                  "reason %s\n", strerror(errno));
            exit(1);
      }
      act.sa_handler = stop_fsstatd;
      if (sigaction(SIGINT, &act, NULL) < 0) {
            fprintf(stderr, "sigaction( SIGINT, ... ) failed - "
                  "reason %s\n", strerror(errno));
            exit(1);
      }
}

/*
 * Open /dev/monwriter
 */
static void fsstatd_open_monwriter(void)
{
      mw_dev = open("/dev/monwriter", O_EXCL | O_RDWR);
      if (mw_dev == -1) {
            printf("cannot open /dev/monwriter: %s\n", strerror(errno));
            exit(1);
      }
}

/*
 * Store daemon's pid so it can be stopped
 */
static void store_pid(void)
{
      FILE *f = fopen(pid_file, "w");

      if (!f) {
            syslog(LOG_ERR, "cannot open pid file %s: %s", pid_file,
                   strerror(errno));
            exit(1);
      }
      fprintf(f, "%d\n", getpid());
      fclose(f);
}

/*
 * Stop sampling of any buffers that are not longer needed
 */
static void stop_unused(int curr_max, int prev_max)
{
      struct monwrite_hdr *mw_hdrp;
      int i;

      mw_hdrp = (struct monwrite_hdr *)small_mon_record;
      mw_hdrp->mon_function = MONWRITE_STOP_INTERVAL;
      mw_hdrp->datalen = 0;
      for (i = 0; i < prev_max - curr_max; i += 2) {
            mw_hdrp->mod_level = curr_max + i;
            if (write(mw_dev, mw_hdrp, sizeof(struct monwrite_hdr)) == -1)
                  syslog(LOG_ERR, "write error on STOP: %s\n",
                         strerror(errno));
      }
}

/*
 * Calculate lengths of data to be written to monitor stream
 */
static struct mw_name_lens fsstatd_get_lens(struct mntent *ent)
{
      struct mw_name_lens name_lens;

      name_lens.mw_name_len = strlen(ent->mnt_fsname);
      name_lens.mw_dir_len = strlen(ent->mnt_dir);
      name_lens.mw_type_len = strlen(ent->mnt_type);
      /* if name & dir too long to fit both, truncate them */
      if (name_lens.mw_name_len +
          name_lens.mw_dir_len +
          name_lens.mw_type_len > MAX_NAMES_LEN) {
            if (name_lens.mw_name_len > MAX_NAME_LEN)
                  name_lens.mw_name_len = MAX_NAME_LEN;
            if (name_lens.mw_dir_len > MAX_DIR_LEN)
                  name_lens.mw_dir_len = MAX_DIR_LEN;
            if (name_lens.mw_type_len > MAX_TYPE_LEN)
                  name_lens.mw_type_len = MAX_TYPE_LEN;
      }
      /* total fs data to be written */
      name_lens.mw_fsdata_len = sizeof(__u16) + name_lens.mw_name_len +
                          sizeof(__u16) + name_lens.mw_dir_len +
                          sizeof(__u16) + name_lens.mw_type_len +
                          sizeof(struct fsstatd_data);
      /* total monitor data to be written in monitor record */
      name_lens.mw_total = sizeof(struct fsstatd_hdr) +
                       name_lens.mw_fsdata_len;
      return name_lens;
}

/*
 * Write fs data for ent to monitor stream
 */
static void fsstatd_write_ent(struct mntent *ent, time_t curr_time,
                  int *small_maxp, int *big_maxp, struct statvfs buf)
{
      struct monwrite_hdr *mw_hdrp;
      struct fsstatd_hdr *mw_fshdrp;
      struct fsstatd_data *mw_fsdatap;

      char *mw_tmpp;
      char *mw_bufp;
      struct mw_name_lens mw_lens;
      int write_len;

      mw_lens = fsstatd_get_lens(ent);

      if ((mw_lens.mw_total + sizeof(struct monwrite_hdr))
          <= sizeof(small_mon_record)) {
            mw_bufp = small_mon_record;
            memset(&small_mon_record, 0, sizeof(small_mon_record));
            mw_hdrp = (struct monwrite_hdr *)mw_bufp;

            mw_hdrp->datalen = sizeof(small_mon_record) -
                           sizeof(struct monwrite_hdr);
            write_len = sizeof(small_mon_record);
            mw_hdrp->mod_level = *small_maxp;
            *small_maxp += 2;
      } else {
            mw_bufp = large_mon_record;
            memset(&large_mon_record, 0, sizeof(large_mon_record));
            mw_hdrp = (struct monwrite_hdr *)mw_bufp;
            mw_hdrp->datalen = sizeof(large_mon_record) -
                           sizeof(struct monwrite_hdr);
            write_len = sizeof(large_mon_record);
            mw_hdrp->mod_level = *big_maxp;
            *big_maxp += 2;
      }

      /* fill in rest of monwrite_hdr */
      mw_tmpp = mw_bufp;
      mw_hdrp->applid = FSSTATD_APPLID;
      mw_hdrp->hdrlen = sizeof(struct monwrite_hdr);
      mw_hdrp->mon_function = MONWRITE_START_INTERVAL;

      /* fill in fsstatd_hdr */
      mw_tmpp += sizeof(struct monwrite_hdr);
      mw_fshdrp = (struct fsstatd_hdr *)mw_tmpp;
      mw_fshdrp->time_stamp = (__u64) curr_time;
      mw_fshdrp->fsstat_data_len = (__u16) mw_lens.mw_fsdata_len;
      mw_fshdrp->fsstat_data_offset = (__u16) sizeof(struct fsstatd_hdr);

      /* fill in fs name, dir name and fs type and lengths */
      mw_tmpp += sizeof(struct fsstatd_hdr);
      memcpy(mw_tmpp, &mw_lens.mw_name_len, sizeof(__u16));
      mw_tmpp += sizeof(__u16);
      strncpy(mw_tmpp, ent->mnt_fsname, mw_lens.mw_name_len);
      mw_tmpp += mw_lens.mw_name_len;
      memcpy(mw_tmpp, &mw_lens.mw_dir_len, sizeof(__u16));
      mw_tmpp += sizeof(__u16);
      strncpy(mw_tmpp, ent->mnt_dir, mw_lens.mw_dir_len);
      mw_tmpp += mw_lens.mw_dir_len;
      memcpy(mw_tmpp, &mw_lens.mw_type_len, sizeof(__u16));
      mw_tmpp += sizeof(__u16);
      strncpy(mw_tmpp, ent->mnt_type, mw_lens.mw_type_len);

      /* fill in fsstatd_data */
      mw_tmpp += mw_lens.mw_type_len;
      mw_fsdatap = (struct fsstatd_data *)mw_tmpp;
      mw_fsdatap->fs_bsize = (__u64)buf.f_bsize;
      mw_fsdatap->fs_frsize = (__u64) buf.f_frsize;
      mw_fsdatap->fs_blocks = (__u64) buf.f_blocks;
      mw_fsdatap->fs_bfree = (__u64) buf.f_bfree;
      mw_fsdatap->fs_bavail = (__u64) buf.f_bavail;
      mw_fsdatap->fs_files = (__u64) buf.f_files;
      mw_fsdatap->fs_ffree = (__u64) buf.f_ffree;
      mw_fsdatap->fs_favail = (__u64) buf.f_favail;
      mw_fsdatap->fs_flag = (__u64) buf.f_flag;

      if (write(mw_dev, mw_bufp, write_len) == -1)
            syslog(LOG_ERR, "write error: %s\n", strerror(errno));
}

/*
 * Run as background process
 */
static void fsstatd_daemonize(void)
{
      pid_t pid;

      /* Fork off the parent process */
      pid = fork();
      if (pid < 0) {
            syslog(LOG_ERR, "fork error: %s\n", strerror(errno));
            exit(1);
      }
      if (pid > 0)
            exit(0);

      /* Change the file mode mask */
      umask(0);

      /* Store daemon pid */
      store_pid();
      /* Catch SIGINT and SIGTERM to clean up pid file on exit */
      fsstatd_handle_signals();

      /* Create a new SID for the child process */
      if (setsid() < 0) {
            syslog(LOG_ERR, "setsid error: %s\n",  strerror(errno));
            exit(1);
      }

      /* Change the current working directory */
      if (chdir("/") < 0) {
            syslog(LOG_ERR, "chdir error: %s\n",  strerror(errno));
            exit(1);
      }

      /* Close out the standard file descriptors */
      close(STDIN_FILENO);
      close(STDOUT_FILENO);
      close(STDERR_FILENO);
}

static int fsstatd_do_work(void)
{
      FILE *mnttab;

      time_t curr_time;
      struct statvfs buf;
      struct mntent *ent;

      int result;
      int curr_small_max, prev_small_max;
      int curr_big_max, prev_big_max;

      /*
       * small buffers use even mod_levels,
       * big buffers use odd mod_levels
       */
      prev_small_max = 0;
      prev_big_max = 1;
      syslog(LOG_INFO, "sample interval: %lu\n", sample_interval);
      while (1) {
            time(&curr_time);
            mnttab = fopen("/etc/mtab", "r");
            if (mnttab == NULL) {
                  syslog(LOG_ERR, "cannot open /etc/mtab: %s\n",
                        strerror(errno));
                  break;
            }
            curr_small_max = 0;
            curr_big_max = 1;

            ent = getmntent(mnttab);
            if (ent == NULL) {
                  syslog(LOG_ERR, "getmntent error: %s\n",
                        strerror(errno));
                  fclose(mnttab);
                  break;
            }

            while (ent) {
                  /* Only sample physical filesystem size data */
                  if ((strncmp(ent->mnt_type, "autofs", 6) == 0 ||
                       strncmp(ent->mnt_type, "none", 4) == 0 ||
                       strncmp(ent->mnt_type, "proc", 4) == 0 ||
                       strncmp(ent->mnt_type, "subfs", 5) == 0 ||
                       strncmp(ent->mnt_type, "ignore", 6) == 0)) {
                        ent = getmntent(mnttab);
                        continue;
                  }
                  result = statvfs(ent->mnt_dir, &buf);
                  if (result != 0) {
                        fclose(mnttab);
                        syslog(LOG_ERR, "statvfs error on %s: %s\n",
                              ent->mnt_dir, strerror(errno));
                        break;
                  }

                  if (buf.f_blocks > 0)
                        fsstatd_write_ent(ent, curr_time,
                              &curr_small_max, &curr_big_max, buf);
                  ent = getmntent(mnttab);
            }

            if (curr_small_max < prev_small_max)
                  stop_unused(curr_small_max, prev_small_max);
            if (curr_big_max < prev_big_max)
                  stop_unused(curr_big_max, prev_big_max);

            prev_small_max = curr_small_max;
            prev_big_max = curr_big_max;
            fclose(mnttab);
            sleep(sample_interval);
      }
      return 1;
}


/*
 Parse options
*/
static int parse_options(int argc, char **argv)
{
      int opt;

      do {
            opt = getopt_long(argc, argv, opt_string, options, NULL);
            switch (opt) {
            case -1:
                  /* Reached end of parameter list. */
                  break;
            case 'h':
                  printf("%s", help_text);
                  exit(0);
            case 'v':
                  printf("mon_fsstatd: version %s\n", RELEASE_STRING);
                  printf("Copyright IBM Corp. 2006\n");
                  exit(0);
            case 'a':
                  attach = 1;
                  break;
            case 'i':
                  sample_interval = strtol(optarg, NULL, 10);
                  if (sample_interval <= 0) {
                        fprintf(stderr, "Error: Invalid interval "
                              "(needs to be greater than 0)\n");
                        return(1);
                  }
                  break;
            default:
                  fprintf(stderr, "Try ' --help' for more"
                        " information.\n");
                  return(1) ;
            }
      } while (opt != -1);
      return(0);
}

int main(int argc, char **argv)
{
      int rc;

      rc = parse_options(argc, argv);
      if (rc > 0)
            return rc;
      fsstatd_open_monwriter();
      openlog("mon_fsstatd", 0, LOG_DAEMON);
      if (!attach)
            fsstatd_daemonize();
      rc = fsstatd_do_work();
      close(mw_dev);
      return rc;
}

Generated by  Doxygen 1.6.0   Back to index