Files
s390-tools/mon_tools/mon_fsstatd.c
Dan Horák 847f16f632 Switch to using /run directory instead of the legacy /var/run
Closes: https://github.com/ibm-s390-tools/s390-tools/pull/53
Signed-off-by: Dan Horák <dan@danny.cz>
Acked-by: Sa Liu <saliu@de.ibm.com>
Acked-by: Harald Freudenberger <freude@linux.ibm.com>
Acked-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2019-02-11 12:33:56 +01:00

454 lines
12 KiB
C

/*
* mon_fsstatd - Write file system utilization data to the z/VM monitor stream
*
* Copyright IBM Corp. 2006, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <linux/types.h>
#include <mntent.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/vfs.h>
#include <syslog.h>
#include <time.h>
#include <unistd.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 = "/run/mon_fsstatd.pid";
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 int store_pid(void)
{
FILE *f = fopen(pid_file, "w");
if (!f) {
syslog(LOG_ERR, "cannot open pid file %s: %s", pid_file,
strerror(errno));
return -1;
}
fprintf(f, "%d\n", getpid());
fclose(f);
return 0;
}
/*
* 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->applid = FSSTATD_APPLID;
mw_hdrp->hdrlen = sizeof(struct monwrite_hdr);
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)
{
int pipe_fds[2], startup_rc = 1;
pid_t pid;
if (pipe(pipe_fds) == -1) {
syslog(LOG_ERR, "pipe error: %s\n", strerror(errno));
exit(1);
}
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
syslog(LOG_ERR, "fork error: %s\n", strerror(errno));
exit(1);
}
if (pid > 0) {
/* Wait for startup return code from daemon */
if (read(pipe_fds[0], &startup_rc, sizeof(startup_rc)) == -1)
syslog(LOG_ERR, "pipe read error: %s\n", strerror(errno));
/* With startup_rc == 0, pid file was written at this point */
exit(startup_rc);
}
/* Change the file mode mask */
umask(0);
/* 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));
goto notify_parent;
}
/* Change the current working directory */
if (chdir("/") < 0) {
syslog(LOG_ERR, "chdir error: %s\n", strerror(errno));
goto notify_parent;
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Store daemon pid */
if (store_pid() < 0)
goto notify_parent;
startup_rc = 0;
notify_parent:
/* Inform waiting parent about startup return code */
if (write(pipe_fds[1], &startup_rc, sizeof(startup_rc)) == -1) {
syslog(LOG_ERR, "pipe write error: %s\n", strerror(errno));
exit(1);
}
if (startup_rc != 0)
exit(startup_rc);
}
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, "nfsd", 4) == 0 ||
strncmp(ent->mnt_type, "tmpfs", 5) == 0 ||
strncmp(ent->mnt_type, "sysfs", 5) == 0 ||
strncmp(ent->mnt_type, "pstore", 6) == 0 ||
strncmp(ent->mnt_type, "cgroup", 6) == 0 ||
strncmp(ent->mnt_type, "mqueue", 6) == 0 ||
strncmp(ent->mnt_type, "devpts", 6) == 0 ||
strncmp(ent->mnt_type, "debugfs", 7) == 0 ||
strncmp(ent->mnt_type, "devtmpfs", 8) == 0 ||
strncmp(ent->mnt_type, "configfs", 8) == 0 ||
strncmp(ent->mnt_type, "selinuxfs", 9) == 0 ||
strncmp(ent->mnt_type, "hugetlbfs", 9) == 0 ||
strncmp(ent->mnt_type, "securityfs", 10) == 0 ||
strncmp(ent->mnt_type, "rpc_pipefs", 10) == 0 ||
strncmp(ent->mnt_type, "binfmt_misc", 11) == 0 ||
strncmp(ent->mnt_type, "ignore", 6) == 0)) {
ent = getmntent(mnttab);
continue;
}
result = statvfs(ent->mnt_dir, &buf);
if (result != 0) {
syslog(LOG_ERR, "statvfs error on %s: %s\n",
ent->mnt_dir, strerror(errno));
ent = getmntent(mnttab);
continue;
}
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, 2017\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;
}