/* * 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; }