ifeq ($(FCRONDYN), 1)
LIBOBJS := socket.o $(LIBOBJS)
endif
-OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o $(LIBOBJS)
-OBJSTAB := fcrontab.o cl.o subs.o mem.o save.o temp_file.o log.o fileconf.o allow.o read_string.o u_list.o env_list.o fcronconf.o
-OBJSDYN := fcrondyn.o subs.o mem.o log.o allow.o read_string.o fcronconf.o
-OBJCONV := convert-fcrontab.o cl.o subs.o mem.o save.o log.o u_list.o env_list.o fcronconf.o
-OBJSIG := fcronsighup.o subs.o mem.o log.o allow.o fcronconf.o
+OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o filesubs.o $(LIBOBJS)
+OBJSTAB := fcrontab.o cl.o subs.o mem.o save.o temp_file.o log.o fileconf.o allow.o read_string.o u_list.o env_list.o fcronconf.o filesubs.o
+OBJSDYN := fcrondyn.o subs.o mem.o log.o allow.o read_string.o fcronconf.o filesubs.o
+OBJCONV := convert-fcrontab.o cl.o subs.o mem.o save.o log.o u_list.o env_list.o fcronconf.o filesubs.o
+OBJSIG := fcronsighup.o subs.o mem.o log.o allow.o fcronconf.o filesubs.o
HEADERSALL := config.h $(SRCDIR)/global.h $(SRCDIR)/cl.h $(SRCDIR)/log.h $(SRCDIR)/subs.h $(SRCDIR)/mem.h $(SRCDIR)/save.h $(SRCDIR)/option.h $(SRCDIR)/dyncom.h
# this is a regular expression :
remove_blanks(start);
if (strcmp(str, start) == 0) {
- fclose(f);
+ xfclose_check(&f, file);
return 1;
}
if (strcmp(start, "all") == 0) {
- fclose(f);
+ xfclose_check(&f, file);
return 2;
}
}
- fclose(f);
+ xfclose_check(&f, file);
/* if execution gets here, string is not in file */
return 0;
int audit_fd = audit_open();
audit_log_user_message(audit_fd, AUDIT_USER_START, "fcron deny",
NULL, NULL, NULL, 0);
- close(audit_fd);
+ xclose_check(&audit_fd, "audit");
}
#endif
error("file %s is truncated : you should reinstall it with fcrontab",
file_name);
- fclose(ff);
+ xfclose_check(&ff, file_name);
return OK;
err:
if (ff != NULL)
- fclose(ff);
+ xfclose_check(&ff, file_name);
if (cl != NULL && cl->cl_next == NULL) {
/* line is not yet in the line list of the file : free it */
Free_safe(line);
- fclose(f);
+ xfclose(&f);
/* open a temp file in write mode and truncate it */
strcpy(buf, "tmp_");
if ((reboot = creat(REBOOT_LOCK, S_IRUSR & S_IWUSR)) < 0)
error_e("Can't create lock for reboot jobs.");
else
- close(reboot);
+ xclose_check(&reboot, REBOOT_LOCK);
/* run @reboot jobs */
return 1;
die_e("Cannot open dir %s", dir);
if (fstat(dir_fd, &st) != 0) {
- close(dir_fd);
+ xclose_check(&dir_fd, "spooldir");
die_e("Cannot fstat %s", dir);
}
if (!S_ISDIR(st.st_mode)) {
- close(dir_fd);
+ xclose_check(&dir_fd, "spooldir");
die("%s exists and is not a directory", dir);
}
if (fchown(dir_fd, useruid, usergid) != 0) {
- close(dir_fd);
+ xclose_check(&dir_fd, "spooldir");
die_e("Cannot fchown dir %s to %s:%s", dir, USERNAME, GROUPNAME);
}
if (fchmod
(dir_fd,
S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP) != 0) {
- close(dir_fd);
+ xclose_check(&dir_fd, "spooldir");
die_e("Cannot change dir %s's mode to 770", dir);
}
- close(dir_fd);
+ xclose_check(&dir_fd, "spooldir");
exit(EXIT_OK);
#ifndef _HPUX_SOURCE
ioctl(fd, TIOCNOTTY, 0);
#endif
- close(fd);
+ xclose_check(&fd, "/dev/tty");
}
if (freopen("/dev/null", "w", stdout) == NULL)
/* close most other open fds */
xcloselog();
for (fd = 3; fd < 250; fd++)
+ /* don't use xclose_check() as we do expect most of them to fail */
(void)close(fd);
/* finally, create a new session */
|| st.st_mode & S_IWGRP || st.st_mode & S_IWOTH) {
error("Conf file (%s) must be owned by root:" GROUPNAME
" and (no more than) 644 : ignored", fcronconf, GROUPNAME);
- fclose(f);
+ xfclose_check(&f, fcronconf);
return;
}
/* debug(" sendmail=%s", sendmail); */
}
- fclose(f);
+ xfclose_check(&f, fcronconf);
}
error_e("error in recv()");
if (!existing_connection)
- close(fd);
+ xclose_check(&fd, "unix socket");
return OK;
}
#endif /* HAVE_LIBREADLINE */
if (!existing_connection)
- close(fd);
+ xclose_check(&fd, "unix socket");
return OK;
}
if ((fp = fopen(pidfile, "r")) != NULL) {
if (fscanf(fp, "%" ATTR_SIZE_PIDT "d", CAST_PIDT_PTR & pid) < 1)
error("Unable to read fcron daemon's pid (fscanf(fp,...))");
- fclose(fp);
+ xfclose_check(&fp, pidfile);
}
return pid;
char sigfile[PATH_LEN];
char buf[PATH_LEN];
+ sigfile[0] = '\0';
t = time(NULL);
tm = localtime(&t);
sleep(sl);
- fclose(fp);
- close(fd);
+ xfclose_check(&fp, sigfile);
+ xclose_check(&fd, sigfile);
if (remove(sigfile) < 0)
error_e("Could not remove %s");
}
int
-copy_src(int from, char *dest)
+copy_src(int from, const char *dest)
/* copy src file orig (already opened) to dest */
/* we first copy the file to a temp file name, and then we rename it,
* so as to avoid data loss if the filesystem is full. */
goto exiterr;
}
- close(to_fd);
+ xclose_check(&to_fd, dest);
to_fd = -1;
if (rename_as_user(tmp_filename_str, dest, useruid, fcrontab_gid) < 0) {
exiterr:
if (to_fd != -1)
- close(to_fd);
+ xclose_check(&to_fd, dest);
return ERR;
}
}
else if (asuid == rootuid && fchown(fd, rootuid, fcrontab_gid) != 0)
error_e("Could not fchown %s to root", buf);
- close(fd);
+ xclose_check(&fd, buf);
need_sig = 1;
void
-list_file(char *file)
+list_file(const char *file)
{
FILE *f = NULL;
int c;
f = fdopen(fd, "r");
if (f == NULL) {
- close(fd);
+ xclose_check(&fd, file);
die_e("User %s could not read file \"%s\"", user, file);
}
while ((c = getc(f)) != EOF)
putchar(c);
- fclose(f);
+ xfclose_check(&f, file);
}
void
-edit_file(char *fcron_orig)
+edit_file(const char *fcron_orig)
/* copy file to a temp file, edit that file, and install it
* if necessary */
{
int status;
struct stat st;
time_t mtime = 0;
- char *tmp_str;
+ char *tmp_str = NULL;
FILE *f = NULL, *fi = NULL;
int file = -1, origfd = -1;
int c;
|| strcmp(cureditor, "\0") == 0)
cureditor = editor;
- file = temp_file(&tmp_str);
+ /* temp_file() dies on error, so tmp_str is always set */
+ file = temp_file(&tmp_str);
if ((fi = fdopen(file, "w")) == NULL) {
error_e("could not fdopen");
goto exiterr;
goto exiterr;
}
}
- fclose(f);
- f = NULL;
+ xfclose_check(&f, fcron_orig);
if (ferror(fi))
error_e("Error while writing new fcrontab to %s");
}
#endif
/* close the file before the user edits it */
- close(file);
+ xclose_check(&file, tmp_str);
switch (pid = fork()) {
case 0:
delete_file(user);
end:
- if (file != -1 && close(file) != 0)
- error_e("could not close %s", tmp_str);
+ xclose_check(&file, tmp_str);
if (remove_as_user(tmp_str, useruid, fcrontab_gid) != 0)
error_e("could not remove %s", tmp_str);
- free(tmp_str);
+ Free_safe(tmp_str);
xexit(return_val);
exiterr:
+ xfclose_check(&fi, tmp_str);
+ xclose_check(&file, tmp_str);
if (remove_as_user(tmp_str, useruid, fcrontab_gid) != 0)
error_e("could not remove %s", tmp_str);
- free(tmp_str);
- if (f != NULL)
- fclose(f);
- if (fi != NULL)
- fclose(fi);
- if (file != -1)
- close(file);
+ xfclose_check(&f, fcron_orig);
+ Free_safe(tmp_str);
xexit(EXIT_ERR);
-
}
close(0);
dup2(i, 0);
- close(i);
+ xclose(&i);
xexit(install_stdin());
--- /dev/null
+/*
+ * FCRON - periodic command scheduler
+ *
+ * Copyright 2000-2012 Thibault Godouet <fcron@free.fr>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * The GNU General Public License can also be found in the file
+ * `LICENSE' that comes with the fcron source distribution.
+ */
+
+
+#include "global.h"
+#include "filesubs.h"
+
+/* close() a file, and set the FD to -1.
+ * Returns close()'s return value and leaves errno as is. */
+int
+xclose(int *fd)
+{
+ int retval = -1;
+
+ if (*fd != -1) {
+ retval = close(*fd);
+ *fd = -1;
+ }
+
+ return retval;
+}
+
+/* close() a file, and set the FD to -1. Check for errors and log them.
+ * Returns close()'s return value.
+ * WARNING: do NOT call from log.c to avoid potential infinite loops! */
+int
+xclose_check(int *fd, const char *filedesc)
+{
+ int retval = -1;
+
+ if (*fd != -1) {
+ retval = close(*fd);
+ if (retval != 0) {
+ error_e("Error while closing %s", filedesc);
+ }
+ *fd = -1;
+ }
+
+ return retval;
+}
+
+/* fclose() a file, and set the FILE* to NULL.
+ * Returns fclose()'s return value and leaves errno as is. */
+int
+xfclose(FILE **f)
+{
+ int retval = EOF;
+
+ if (f == NULL) {
+ return retval;
+ }
+
+ if (*f != NULL) {
+ retval = fclose (*f);
+ *f = NULL;
+ }
+
+ return retval;
+}
+
+/* fclose() a file, and set the FILE* to NULL. Check for errors and log them.
+ * Returns fclose()'s return value.
+ * WARNING: do NOT call from log.c to avoid potential infinite loops! */
+int
+xfclose_check(FILE **f, const char *filedesc)
+{
+ int retval = EOF;
+
+ if (f == NULL) {
+ return retval;
+ }
+
+ if (*f != NULL) {
+ retval = fclose (*f);
+ if (retval != 0) {
+ error_e("Error while fclosing %s", filedesc);
+ }
+ *f = NULL;
+ }
+
+ return retval;
+}
+
--- /dev/null
+/*
+ * FCRON - periodic command scheduler
+ *
+ * Copyright 2000-2012 Thibault Godouet <fcron@free.fr>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * The GNU General Public License can also be found in the file
+ * `LICENSE' that comes with the fcron source distribution.
+ */
+
+/* filesubs: file related macros and functions.
+ * They are used to make the code safer. */
+
+#ifndef __FILESUBS_H__
+#define __FILESUBS_H__
+
+/* macros */
+
+/* function definitions */
+
+int xclose(int *fd);
+int xclose_check(int *fd, const char *filedesc);
+int xfclose(FILE **f);
+int xfclose_check(FILE **f, const char *filedesc);
+
+#endif /* __FILESUBS_H__ */
{
FILE *fp;
int i;
+ char loadavg_path = PROC "/loadavg";
if (n > 3)
n = 3;
- if ((fp = fopen(PROC "/loadavg", "r")) == NULL) {
- error_e("could not open '" PROC "/loadavg'"
- " (make sure procfs is mounted)");
+ if ((fp = fopen(loadavg_path, "r")) == NULL) {
+ error_e("could not open '%s' (make sure procfs is mounted)",
+ loadavg_path);
i = -1;
}
else {
}
}
end:
- fclose(fp);
+ xfclose_check(&fp, loadavg_path);
return (i < 0) ? i : i;
}
#include "log.h"
/* functions used by fcrontab, fcrondyn, and fcron */
#include "subs.h"
-
+/* file related helper functions */
+#include "filesubs.h"
#endif /* __GLOBAL_H__ */
die_e("dup2() error"); /* dup2 also clears close-on-exec flag */
/* we close the pipe_fd[]s : the resources remain, and the pipe will
* be effectively close when the job stops */
- if (close(pipe_fd[0]) < 0)
- error_e("setup_stderr_stdout: could not close(pipe_fd[0])");
- if (close(pipe_fd[1]) < 0)
- error_e("setup_stderr_stdout: could not close(pipe_fd[1])");
+ xclose_check(&(pipe_fd[0]), "pipe_fd[0] in setup_stderr_stdout");
+ xclose_check(&(pipe_fd[1]), "pipe_fd[1] in setup_stderr_stdout");
/* Standard buffering results in unwanted behavior (some messages,
* at least error from fcron process itself, are lost) */
#ifdef HAVE_SETLINEBUF
&curhome, &content_type, &encoding);
/* close unneeded READ fd */
- if (close(pipe_pid_fd[0]) < 0)
- error_e("child: could not close(pipe_pid_fd[0])");
+ xclose_check(&(pipe_pid_fd[0]), "child's pipe_pid_fd[0]");
pipe_fd[0] = pipe_fd[1] = -1;
if (!to_stdout && is_mail(line->cl_option)) {
error_e("Fork error : could not exec \"%s\"", line->cl_shell);
if (write(pipe_pid_fd[1], &pid, sizeof(pid)) < 0)
error_e("could not write child pid to pipe_pid_fd[1]");
- if (pipe_fd[0] != -1 && close(pipe_fd[0]) < 0)
- error_e("child: could not close(pipe_fd[0])");
- if (pipe_fd[1] != -1 && close(pipe_fd[1]) < 0)
- error_e("child: could not close(pipe_fd[1])");
- if (close(pipe_pid_fd[1]) < 0)
- error_e("child: could not close(pipe_pid_fd[1])");
+ xclose_check(&(pipe_fd[0]), "child's pipe_fd[0]");
+ xclose_check(&(pipe_fd[1]), "child's pipe_fd[1]");
+ xclose_check(&(pipe_pid_fd[1]), "child's pipe_pid_fd[1]");
exit(EXIT_ERR);
break;
/* grand child (child of the 2nd fork) */
/* the grand child does not use this pipe: close remaining fd */
- if (close(pipe_pid_fd[1]) < 0)
- error_e("grand child: could not close(pipe_pid_fd[1])");
+ xclose_check(&(pipe_pid_fd[1]), "grand child's pipe_pid_fd[1]");
if (!to_stdout)
/* note : the following closes the pipe */
/* child (parent of the 2nd fork) */
/* close unneeded WRITE pipe and READ pipe */
- if (pipe_fd[1] != -1 && close(pipe_fd[1]) < 0)
- error_e("child: could not close(pipe_fd[1])");
+ xclose_check(&(pipe_fd[1]), "child's pipe_fd[1]");
#ifdef CHECKRUNJOB
debug("run_job(): child: pipe_fd[1] and pipe_pid_fd[0] closed"
if (fputs(mailbuf, mailf) < 0)
warn("fputs() failed to write to mail file for job %s (pid %d)", line->cl_shell, pid);
/* (closes also pipe_fd[0]): */
- if (fclose(pipef) != 0)
- error_e("child: Could not fclose(pipef)");
+ xfclose_check(&pipef, "child's pipef");
}
/* FIXME : FOLLOWING HACK USELESS ? */
#ifdef CHECKRUNJOB
debug("run_job(): child: closing pipe with parent");
#endif /* CHECKRUNJOB */
- if (close(pipe_pid_fd[1]) < 0)
- error_e("child: could not close(pipe_pid_fd[1])");
+ xclose_check(&(pipe_pid_fd[1]), "child's pipe_pid_fd[1]");
/* we use a while because of a possible interruption by a signal */
while ((pid = wait3(&status, 0, NULL)) > 0) {
/* parent */
/* close unneeded WRITE fd */
- if (close(pipe_pid_fd[1]) < 0)
- error_e("parent: could not close(pipe_pid_fd[1])");
+ xclose_check(&(pipe_pid_fd[1]), "parent's pipe_pid_fd[1]");
exeent->e_ctrl_pid = pid;
exeent->e_job_pid = -1;
break;
}
- if (close(pipe_pid_fd[0]) < 0)
- error_e("parent: could not close(pipe_pid_fd[0])");
+ xclose_check(&(pipe_pid_fd[0]), "parent's pipe_pid_fd[0]");
#ifdef CHECKRUNJOB
debug
}
/* if mail is sent, execution doesn't get here : close /dev/null */
- if (mailf != NULL && fclose(mailf) != 0)
- die_e("Can't close file mailf");
+ xfclose_check(&mailf, "Can't close file mailf");
exit(0);
char *make_msg(const char *append, char *fmt, va_list args);
void log_syslog_str(int priority, char *msg);
-void log_file_str(FILE * logfile, int priority, char *msg);
+void log_file_str(int priority, char *msg);
void log_console_str(int priority, char *msg);
void log_fd_str(int fd, char *msg);
static void print_line_prefix(FILE * logfile, int priority);
* or take no action if logging is suppressed.
* This function will be called automatically if you attempt to log something,
* however you may have to call it explicitely as it needs to run before the
- * program becomes a daemon so as it can print any errors on the console.
- */
+ * program becomes a daemon so as it can print an error on the console
+ * if it can't open the logs correctly. */
void
xopenlog(void)
{
// check whether we need to close syslog, or a file.
if (logfile != NULL) {
- if (fclose(logfile) != 0) {
+ /* we must NOT use xfclose_check() in log.c to avoid infinite loops */
+ if (xfclose(&logfile) != 0) {
int saved_errno = errno;
syslog(COMPLAIN_LEVEL, "Error while closing log file '%s': %s",
/* log a simple string to a log file if needed */
void
-log_file_str(FILE * logfile, int priority, char *msg)
+log_file_str(int priority, char *msg)
{
xopenlog();
return;
log_syslog_str(priority, msg);
- log_file_str(logfile, priority, msg);
+ log_file_str(priority, msg);
log_console_str(priority, msg);
log_fd_str(fd, msg);
return;
log_syslog_str(priority, msg);
- log_file_str(logfile, priority, msg);
+ log_file_str(priority, msg);
log_console_str(priority, msg);
log_fd_str(fd, msg);
if (fchown(fd, own_uid, own_gid) != 0) {
error_e("Could not fchown %s to uid:%d gid:%d", filename, own_uid,
own_gid);
- if (close(fd) < 0)
- error_e("save_one_file(%s): could not close(fd)", filename);
+ if (xclose(&fd) < 0)
+ error_e("save_one_file(%s): could not xclose(fd)", filename);
remove_as_user(filename, own_uid, own_gid);
return ERR;
}
/* save file : */
if (write_file_to_disk(fd, file, save_date) == ERR) {
- if (close(fd) < 0)
- error_e("save_one_file(%s): could not close(fd)", filename);
+ if (xclose(&fd) < 0)
+ error_e("save_one_file(%s): could not xclose(fd)", filename);
remove_as_user(filename, own_uid, own_gid);
return ERR;
}
- if (close(fd) < 0)
- error_e("save_one_file(%s): could not close(fd)", filename);
+ if (xclose(&fd) < 0)
+ error_e("save_one_file(%s): could not xclose(fd)", filename);
return OK;
}
and make client points to the next entry */
{
shutdown((*client)->fcl_sock_fd, SHUT_RDWR);
- close((*client)->fcl_sock_fd);
+ xclose_check(&((*client)->fcl_sock_fd), "client fd");
remove_from_select_set((*client)->fcl_sock_fd, &master_set, &set_max_fd);
debug("connection closed : fd : %d", (*client)->fcl_sock_fd);
if (prev_client == NULL) {
error_e
("Could not set fd attribute O_NONBLOCK : connection rejected.");
shutdown(fd, SHUT_RDWR);
- close(fd);
+ xclose_check(&fd, "client fd");
}
else {
Alloc(client, fcrondyn_cl);
if (listen_fd) {
shutdown(listen_fd, SHUT_RDWR);
- close(listen_fd);
+ xclose_check(&listen_fd, "listening fd");
unlink(fifofile);
client = fcrondyn_cl_base;
while (client != NULL) {
shutdown(client->fcl_sock_fd, SHUT_RDWR);
- close(client->fcl_sock_fd);
+ xclose_check(&(client->fcl_sock_fd), "client fd");
client_buf = client->fcl_next;
Free_safe(client);
if (fstat(fd, &s) < 0) {
saved_errno = errno;
error_e("open_as_user(): could not fstat %s", pathname);
- if (close(fd) < 0)
- error_e("open_as_user: could not close() %s", pathname);
+ if (xclose(&fd) < 0)
+ error_e("open_as_user: could not xclose() %s", pathname);
fd = -1;
}
if (!S_ISREG(s.st_mode) || s.st_nlink != 1) {
error_e("open_as_user(): file %s is not a regular file", pathname);
- if (close(fd) < 0)
- error_e("open_as_user: could not close() %s", pathname);
+ if (xclose(&fd) < 0)
+ error_e("open_as_user: could not xclose() %s", pathname);
saved_errno = 0;
fd = -1;
}
return fd;
err:
- if (fd >= 0 && close(fd) < 0)
- error_e("open_as_user: could not close() %s", pathname);
+ if (fd >= 0 && xclose(&fd) < 0)
+ error_e("open_as_user: could not xclose() %s", pathname);
errno = saved_errno;
return -1;
}