/*
* Copyright (C), 2000-2007 by the monit project group.
* All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#ifdef HAVE_STDIO_H
#include
#endif
#ifdef HAVE_STDLIB_H
#include
#endif
#ifdef HAVE_ERRNO_H
#include
#endif
#ifdef HAVE_STRING_H
#include
#endif
#ifdef HAVE_STRINGS_H
#include
#endif
#ifdef HAVE_SYS_TYPES_H
#include
#endif
#ifdef HAVE_SYS_STAT_H
#include
#endif
#ifdef HAVE_UNISTD_H
#include
#endif
#ifdef HAVE_FCNTL_H
#include
#endif
#ifdef HAVE_DIRENT_H
#include
#endif
#include "monitor.h"
/**
* Utilities used for managing files used by monit.
*
* @author Jan-Henrik Haukeland,
* @author Christian Hopp,
* @author Martin Pala,
*
* @version \$Id: file.c,v 1.14 2007/08/10 21:34:33 martinp Exp $
*
* @file
*/
/* ------------------------------------------------------------------ Public */
/**
* Initialize the programs file variables
*/
void File_init() {
char pidfile[STRLEN];
char statefile[STRLEN];
/* Check if the pidfile was already set during configfile parsing */
if(Run.pidfile == NULL) {
/* Set the location of this programs pidfile */
if(! getuid()) {
snprintf(pidfile, STRLEN, "%s/%s", MYPIDDIR, MYPIDFILE);
} else {
snprintf(pidfile, STRLEN, "%s/.%s", Run.Env.home, MYPIDFILE);
}
Run.pidfile= xstrdup(pidfile);
}
/* Set the location of the programs state file */
if(Run.statefile == NULL) {
snprintf(statefile, STRLEN, "%s/.%s", Run.Env.home, MYSTATEFILE);
Run.statefile= xstrdup(statefile);
}
}
/**
* Finalize and remove temporary files
*/
void File_finalize() {
unlink(Run.pidfile);
unlink(Run.statefile);
}
/**
* Get a object's last modified timestamp.
* @param object A object to stat
* @param type Requested object's type
* @return Max of either st_mtime or st_ctime or
* FALSE if not found or different type of object
*/
time_t File_getTimestamp(char *object, mode_t type) {
struct stat buf;
ASSERT(object);
if(! stat(object, &buf)) {
if(((type == S_IFREG) && S_ISREG(buf.st_mode)) ||
((type == S_IFDIR) && S_ISDIR(buf.st_mode)) ||
((type == (S_IFREG|S_IFDIR)) && (S_ISREG(buf.st_mode) ||
S_ISDIR(buf.st_mode)))
) {
return MAX(buf.st_mtime, buf.st_ctime);
} else {
LogError("%s: Invalid object type - %s\n", prog, object);
}
}
return FALSE;
}
/**
* Search the system for the monit control file. Try first ~/.monitrc,
* if that fails try /etc/monitrc, then SYSCONFDIR/monitrc (default:
* /usr/local/etc/monitrc) and finally ./monitrc.
* Exit the application if the control file was not found.
* @return The location of monits control file (monitrc)
*/
char *File_findControlFile() {
char *rcfile= xcalloc(sizeof(char), STRLEN + 1);
snprintf(rcfile, STRLEN, "%s/.%s", Run.Env.home, MONITRC);
if(File_exist(rcfile)) {
return (rcfile);
}
memset(rcfile, 0, STRLEN);
snprintf(rcfile, STRLEN, "/etc/%s", MONITRC);
if(File_exist(rcfile)) {
return (rcfile);
}
memset(rcfile, 0, STRLEN);
snprintf(rcfile, STRLEN, "%s/%s", SYSCONFDIR, MONITRC);
if(File_exist(rcfile)) {
return (rcfile);
}
memset(rcfile, 0, STRLEN);
snprintf(rcfile, STRLEN, "/usr/local/etc/%s", MONITRC);
if(File_exist(rcfile)) {
return (rcfile);
}
if(File_exist(MONITRC)) {
memset(rcfile, 0, STRLEN);
snprintf(rcfile, STRLEN, "%s/%s", Run.Env.cwd, MONITRC);
return (rcfile);
}
LogError("%s: Cannot find the control file at "
"~/.%s, /etc/%s, %s/%s, /usr/local/etc/%s or at ./%s \n",
prog, MONITRC, MONITRC, SYSCONFDIR, MONITRC, MONITRC, MONITRC);
exit(1);
}
/**
* Create a program's pidfile - Such a file is created when in daemon
* mode. The file is created with mask = MYPIDMASK (usually 644).
* @param pidfile The name of the pidfile to create
* @return TRUE if the file was created, otherwise FALSE.
*/
int File_createPidFile(char *pidfile) {
FILE *F= NULL;
ASSERT(pidfile);
umask(MYPIDMASK);
unlink(pidfile);
if ((F= fopen(pidfile,"w")) == (FILE *)NULL) {
LogError("%s: Error opening pidfile '%s' for writing -- %s\n",
prog, pidfile, STRERROR);
return(FALSE);
}
fprintf(F, "%d\n", (int)getpid());
fclose(F);
return TRUE;
}
/**
* Check if the file is a regular file
* @param file A path to the file to check
* @return TRUE if file exist and is a regular file, otherwise FALSE
*/
int File_isFile(char *file) {
struct stat buf;
ASSERT(file);
return (stat(file, &buf) == 0 && S_ISREG(buf.st_mode));
}
/**
* Check if this is a directory.
* @param dir An absolute directory path
* @return TRUE if dir exist and is a regular directory, otherwise
* FALSE
*/
int File_isDirectory(char *dir) {
struct stat buf;
ASSERT(dir);
return (stat(dir, &buf) == 0 && S_ISDIR(buf.st_mode));
}
/**
* Check if this is a fifo
* @param fifo A path to the fifo to check
* @return TRUE if fifo exist, otherwise FALSE
*/
int File_isFifo(char *fifo) {
struct stat buf;
ASSERT(fifo);
return (stat(fifo, &buf) == 0 && S_ISFIFO(buf.st_mode));
}
/**
* Check if the file exist on the system
* @file A path to the file to check
* @return TRUE if file exist otherwise FALSE
*/
int File_exist(char *file) {
struct stat buf;
ASSERT(file);
return (stat(file, &buf) == 0);
}
/**
* Security check for files. The files must have the same uid as the
* REAL uid of this process, it must have permissions no greater than
* "maxpermission" and it must not be a symbolic link. We check these
* conditions here.
* @param filename The filename of the checked file
* @param description The description of the checked file
* @param permmask The permission mask for the file
* @return TRUE if the test passed otherwise FALSE
*/
int File_checkStat(char *filename, char *description, int permmask) {
struct stat buf;
errno= 0;
ASSERT(filename);
ASSERT(description);
if(lstat(filename, &buf) < 0) {
LogError("%s: Cannot stat the %s '%s' -- %s\n",
prog, description, filename, STRERROR);
return FALSE;
}
if(S_ISLNK(buf.st_mode)) {
LogError("%s: The %s '%s' must not be a symbolic link.\n",
prog, description, filename);
return(FALSE);
}
if(!S_ISREG(buf.st_mode)) {
LogError("%s: The %s '%s' is not a regular file.\n",
prog, description, filename);
return FALSE;
}
if(buf.st_uid != geteuid()) {
LogError("%s: The %s '%s' must be owned by you.\n",
prog, description, filename);
return FALSE;
}
if((buf.st_mode & 0777 ) & ~permmask) {
/*
Explanation:
buf.st_mode & 0777 -> We just want to check the
permissions not the file type...
we did it already!
() & ~permmask -> We check if there are any other
permissions set than in permmask
*/
LogError("%s: The %s '%s' must have permissions no more "
"than -%c%c%c%c%c%c%c%c%c (0%o); "
"right now permissions are -%c%c%c%c%c%c%c%c%c (0%o).\n",
prog, description, filename,
permmask&S_IRUSR?'r':'-',
permmask&S_IWUSR?'w':'-',
permmask&S_IXUSR?'x':'-',
permmask&S_IRGRP?'r':'-',
permmask&S_IWGRP?'w':'-',
permmask&S_IXGRP?'x':'-',
permmask&S_IROTH?'r':'-',
permmask&S_IWOTH?'w':'-',
permmask&S_IXOTH?'x':'-',
permmask&0777,
buf.st_mode&S_IRUSR?'r':'-',
buf.st_mode&S_IWUSR?'w':'-',
buf.st_mode&S_IXUSR?'x':'-',
buf.st_mode&S_IRGRP?'r':'-',
buf.st_mode&S_IWGRP?'w':'-',
buf.st_mode&S_IXGRP?'x':'-',
buf.st_mode&S_IROTH?'r':'-',
buf.st_mode&S_IWOTH?'w':'-',
buf.st_mode&S_IXOTH?'x':'-',
buf.st_mode& 0777);
return FALSE;
}
return TRUE;
}
/**
* Check whether the specified directory exist or create it using
* specified mode.
* @param path The fully qualified path to the directory
* @param mode The permission for the directory
* @return TRUE if the passed otherwise FALSE
*/
int File_checkQueueDirectory(char *path, mode_t mode) {
struct stat st;
if(stat(path, &st)) {
if(errno == ENOENT) {
int rv;
mode_t mask = umask(QUEUEMASK);
rv = mkdir(path, mode);
umask(mask);
if(rv) {
LogError("%s: cannot create the directory %s -- %s\n",
prog, path, STRERROR);
return FALSE;
}
} else {
LogError("%s: cannot read the directory %s -- %s\n",
prog, path, STRERROR);
return FALSE;
}
} else if(! S_ISDIR(st.st_mode)) {
LogError("%s: the %s is not the directory\n", prog, path);
return FALSE;
}
return TRUE;
}
/**
* Check the queue size limit.
* @param path The fully qualified path to the directory
* @param limit The queue limit
* @return TRUE if the passed otherwise FALSE
*/
int File_checkQueueLimit(char *path, int limit) {
int used = 0;
DIR *dir = NULL;
struct dirent *de = NULL;
if(limit <= 0) {
LogError("%s: event queue full\n", prog);
return FALSE;
}
if(! (dir = opendir(path)) ) {
LogError("%s: cannot open the directory %s -- %s\n", prog, path, STRERROR);
return FALSE;
}
while( (de = readdir(dir)) ) {
struct stat st;
if(!stat(de->d_name, &st) && S_ISREG(st.st_mode) && ++used > limit) {
LogError("%s: event queue full\n", prog);
closedir(dir);
return FALSE;
}
}
closedir(dir);
return TRUE;
}
/**
* Write data to the queue file
* @param file Filedescriptor to write to
* @param data Data to be written
* @param size Size of the data to be written
* @return TRUE if the passed otherwise FALSE
*/
int File_writeQueue(FILE *file, void *data, int size) {
ASSERT(file);
/* write size */
if(fwrite(&size, 1, sizeof(int), file) != sizeof(int) || ferror(file))
goto error;
/* write data if any */
if(size > 0)
if(fwrite(data, 1, size, file) != size || ferror(file))
goto error;
return TRUE;
error:
LogError("%s: unable to write data to the file\n", prog);
return FALSE;
}
/**
* Read the data from the queue file's actual position
* @param file Filedescriptor to read from
* @param size Size of the data read
* @return The data read if any or NULL. The size parameter is set
* appropriately.
*/
void *File_readQueue(FILE *file, int *size) {
void *data = NULL;
ASSERT(file);
/* read size */
if(fread(size, 1, sizeof(int), file) != sizeof(int) || ferror(file))
goto error;
/* read data if any */
if(*size > 0) {
data = xcalloc(1, *size);
if(fread(data, 1, *size, file) != *size || ferror(file)) {
goto error;
}
}
return data;
error:
LogError("%s: unable to read data from the file\n", prog);
return FALSE;
}