This is to prepare for getting notifed of time changes via timerfd_create() and select ()
VERSION := @VERSION@
CFLAGS += $(OPTIM) $(OPTION) $(DEFS) $(CPPFLAGS)
ifeq ($(FCRONDYN), 1)
-LIBOBJS := socket.o $(LIBOBJS)
+LIBOBJS := $(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 filesubs.o $(LIBOBJS)
+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 select.o fcrondyn_svr.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
/* Define if you have the <sys/resource.h> header file. */
#undef HAVE_SYS_RESOURCE_H
+/* Define if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
/* Define if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
AC_CHECK_HEADERS(stdarg.h)
AC_CHECK_HEADERS(termios.h)
AC_CHECK_HEADERS(strings.h)
-AC_CHECK_HEADERS(sys/types.h sys/socket.h sys/un.h)
+AC_CHECK_HEADERS(sys/select.h sys/types.h sys/socket.h sys/un.h)
AC_CHECK_HEADERS(security/pam_appl.h pam/pam_appl.h crypt.h shadow.h libaudit.h)
AC_CHECK_HEADERS(sys/resource.h)
AC_CHECK_HEADERS(grp.h)
#include "job.h"
#include "temp_file.h"
#include "fcronconf.h"
+#include "select.h"
#ifdef FCRONDYN
-#include "socket.h"
+#include "fcrondyn_svr.h"
#endif
RETSIGTYPE sigusr1_handler(int x);
RETSIGTYPE sigusr2_handler(int x);
RETSIGTYPE sigcont_handler(int x);
-long int get_suspend_duration(time_t slept_from);
+long int read_suspend_duration(time_t slept_from);
void check_suspend(time_t slept_from, time_t nwt);
int parseopt(int argc, char *argv[]);
void get_lock(void);
exe_list_t *exe_list; /* jobs which are executed */
-time_t begin_sleep; /* the time at which sleep began */
time_t now; /* the current time */
#ifdef HAVE_LIBPAM
save_file(NULL);
#ifdef FCRONDYN
- close_socket();
+ fcrondyn_socket_close(NULL);
#endif
f = file_base;
}
long int
-get_suspend_duration(time_t slept_from)
- /* Return the amount of time the system was suspended (to mem or disk).
+read_suspend_duration(time_t slept_from)
+ /* Return the amount of time the system was suspended (to mem or disk),
+ * as read from suspendfile.
* Return 0 on error.
*
* The idea is that:
long int suspend_duration; /* amount of time the system was suspended */
long int time_diff; /* estimate of suspend_duration (as fallback) */
- suspend_duration = get_suspend_duration(slept_from);
+ suspend_duration = read_suspend_duration(slept_from);
/* Also check if there was an unaccounted sleep duration, in case
* the OS is not configured to let fcron properly know about suspends
lavg_list->max_entries = lavg_queue_max;
lavg_serial_running = 0;
-#ifdef FCRONDYN
- /* initialize socket */
- init_socket();
-#endif
-
/* initialize random number generator :
* WARNING : easy to guess !!! */
/* we use the hostname and tv_usec in order to get different seeds
time_t slept_from; /* time it was when we went into sleep */
time_t nwt; /* next wake time */
#ifdef HAVE_GETTIMEOFDAY
+ struct select_instance main_select;
struct timeval now_tv; /* we use usec field to get more precision */
-#endif
-#if defined(FCRONDYN) && defined(HAVE_GETTIMEOFDAY)
- int retcode = 0;
struct timeval sleep_tv; /* we use usec field to get more precision */
#endif
debug("Entering main loop");
+#ifdef HAVE_GETTIMEOFDAY
+ select_init(&main_select);
+#ifdef FCRONDYN
+ fcrondyn_socket_init(&main_select);
+#endif
+#endif
+
now = my_time();
synchronize_dir(".", is_system_startup());
slept_from = time(NULL);
#ifdef HAVE_GETTIMEOFDAY
-#ifdef FCRONDYN
gettimeofday(&now_tv, NULL);
debug("now gettimeofday tv_sec=%ld, tv_usec=%ld %s", now_tv.tv_sec,
now_tv.tv_usec, ctime(&nwt));
/* note: read_set is set in socket.c */
debug("nwt=%s, sleep sec=%ld, usec=%ld", ctime(&nwt), sleep_tv.tv_sec,
sleep_tv.tv_usec);
- if ((retcode =
- select(set_max_fd + 1, &read_set, NULL, NULL, &sleep_tv)) < 0
- && errno != EINTR)
- die_e("select returned %d", errno);
-#else /* FCRONDYN */
- if (nwt - now > 0) {
- sleep(nwt - now - 1);
- }
- gettimeofday(&now_tv, NULL);
- /* we set tv_usec to slightly more than necessary to avoid
- * infinite loop */
- usleep(1000000 + min_sleep_usec - now_tv.tv_usec);
-#endif /* FCRONDYN */
+ select_call(&main_select, &sleep_tv);
#else /* HAVE_GETTIMEOFDAY */
if (nwt - now > 0) {
sleep(nwt - now);
#if defined(FCRONDYN) && defined(HAVE_GETTIMEOFDAY)
/* check if there's a new connection, a new command to answer, etc ... */
/* we do that *after* other checks, to avoid Denial Of Service attacks */
- check_socket(retcode);
+ fcrondyn_socket_check(&main_select);
#endif
nwt = check_lavg(save);
/* This file contains all fcron's code (server) to handle communication with fcrondyn */
-
#include "fcron.h"
-#include "socket.h"
+#include "fcrondyn_svr.h"
+#include "select.h"
#include "getloadavg.h"
#include "database.h"
#include "fcronconf.h"
void remove_connection(struct fcrondyn_cl **client,
- struct fcrondyn_cl *prev_client);
+ struct fcrondyn_cl *prev_client, select_instance *si);
void exe_cmd(struct fcrondyn_cl *client);
#ifdef SO_PEERCRED /* linux */
void auth_client_so_peercred(struct fcrondyn_cl *client);
void cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd,
exe_t * e);
void cmd_run(struct fcrondyn_cl *client, long int *cmd, int fd, int is_root);
-void add_to_select_set(int fd, fd_set * set, int *max_fd);
-void remove_from_select_set(int fd, fd_set * set, int *max_fd);
fcrondyn_cl *fcrondyn_cl_base; /* list of connected fcrondyn clients */
int fcrondyn_cl_num = 0; /* number of fcrondyn clients currently connected */
-fd_set read_set; /* client fds list : cmd waiting ? */
-fd_set master_set; /* master set : needed since select() modify read_set */
-int set_max_fd = 0; /* needed by select() */
int listen_fd = -1; /* fd which catches incoming connection */
int auth_fail = 0; /* number of auth failure */
time_t auth_nofail_since = 0; /* we refuse auth since x due to too many failures */
void
-add_to_select_set(int fd, fd_set * set, int *max_fd)
- /* add fd to set, and update max_fd if necessary (for select()) */
-{
- FD_SET(fd, set);
- if (fd > *max_fd)
- *max_fd = fd;
-}
-
-
-void
-remove_from_select_set(int fd, fd_set * set, int *max_fd)
- /* remove fd to set, and update max_fd if necessary (for select()) */
-{
- FD_CLR(fd, set);
- if (fd == *max_fd) {
- /* find the biggest fd in order to update max_fd */
- struct fcrondyn_cl *client;
- int tmp_max_fd = listen_fd;
-
- for (client = fcrondyn_cl_base; client != NULL;
- client = client->fcl_next) {
- if (client->fcl_sock_fd > tmp_max_fd)
- tmp_max_fd = client->fcl_sock_fd;
- }
-
- /* update max_fd */
- *max_fd = tmp_max_fd;
- }
-}
-
-
-void
-init_socket(void)
+fcrondyn_socket_init(select_instance *si)
/* do everything needed to get a working listening socket */
{
struct sockaddr_un addr;
int len = 0;
int sun_len = 0;
- /* used in fcron.c:main_loop():select() */
- FD_ZERO(&read_set);
- FD_ZERO(&master_set);
-
if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
error_e("Could not create socket : fcrondyn won't work");
return;
}
/* no error */
- add_to_select_set(listen_fd, &master_set, &set_max_fd);
+ select_add_read(si, listen_fd);
- /* copy master in read_fs, because read_fs will be modified by select() */
- read_set = master_set;
- debug("Socket initialized : listen_fd : %d set_max_fd : %d ", listen_fd,
- set_max_fd);
+ debug("Socket initialized : listen_fd : %d", listen_fd);
return;
err:
- close_socket();
+ fcrondyn_socket_close(si);
}
}
void
-remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client)
+remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client, select_instance *si)
/* close the connection, remove it from the list
and make client points to the next entry */
{
debug("closing connection : fd : %d", (*client)->fcl_sock_fd);
shutdown((*client)->fcl_sock_fd, SHUT_RDWR);
- remove_from_select_set((*client)->fcl_sock_fd, &master_set, &set_max_fd);
+ select_rm_read(si, (*client)->fcl_sock_fd);
xclose_check(&((*client)->fcl_sock_fd), "client fd");
if (prev_client == NULL) {
fcrondyn_cl_base = (*client)->fcl_next;
}
void
-check_socket(int num)
+fcrondyn_socket_check(select_instance *si)
/* check for new connection, command, connection closed */
{
int fd = -1, avoid_fd = -1;
int read_len = 0;
struct fcrondyn_cl *client = NULL, *prev_client = NULL;
- if (num <= 0)
+ if (si->retcode <= 0)
/* no socket to check : go directly to the end of that function */
- goto final_settings;
+ return;
debug("Checking socket ...");
- if (FD_ISSET(listen_fd, &read_set)) {
+ if (FD_ISSET(listen_fd, &si->readfds)) {
debug("got new connection ...");
fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len);
if (fd == -1) {
error_e
("could not accept new connection : isset(listen_fd = %d) = %d",
- listen_fd, FD_ISSET(listen_fd, &read_set));
+ listen_fd, FD_ISSET(listen_fd, &si->readfds));
}
else {
fcntl(fd, F_SETFD, 1);
/* to avoid trying to read from it in this call */
avoid_fd = fd;
- add_to_select_set(fd, &master_set, &set_max_fd);
+ select_add_read(si, fd);
fcrondyn_cl_num += 1;
debug("Added connection fd : %d - %d connections", fd,
client = fcrondyn_cl_base;
while (client != NULL) {
- if (!FD_ISSET(client->fcl_sock_fd, &read_set)
+ if (!FD_ISSET(client->fcl_sock_fd, &si->readfds)
|| client->fcl_sock_fd == avoid_fd) {
/* check if the connection has not been idle for too long ... */
if (client->fcl_user == NULL
&& now - client->fcl_idle_since > MAX_AUTH_TIME) {
warn("Connection with no auth for more than %ds : closing it.",
MAX_AUTH_TIME);
- remove_connection(&client, prev_client);
+ remove_connection(&client, prev_client, si);
}
else if (now - client->fcl_idle_since > MAX_IDLE_TIME) {
warn("Connection of %s is idle for more than %ds : closing it.",
client->fcl_user, MAX_IDLE_TIME);
- remove_connection(&client, prev_client);
+ remove_connection(&client, prev_client, si);
}
else {
/* nothing to do on this one ... check the next one */
recv(client->fcl_sock_fd, buf_int, sizeof(buf_int), 0)) <= 0) {
if (read_len == 0) {
/* connection closed by client */
- remove_connection(&client, prev_client);
+ remove_connection(&client, prev_client, si);
}
else {
error_e("error recv() from sock fd %d", client->fcl_sock_fd);
}
}
- final_settings:
- /* copy master_set in read_set, because read_set is modified by select() */
- read_set = master_set;
}
void
-close_socket(void)
- /* close connections, close socket, remove socket file */
+fcrondyn_socket_close(select_instance *si)
+ /* close connections, close socket, remove socket file.
+ * If si is not NULL, then remove the fds from si's readfds */
{
struct fcrondyn_cl *client, *client_buf = NULL;
while (client != NULL) {
shutdown(client->fcl_sock_fd, SHUT_RDWR);
xclose_check(&(client->fcl_sock_fd), "client fd");
+ if (si != NULL) {
+ select_rm_read(si, client->fcl_sock_fd);
+ }
client_buf = client->fcl_next;
Free_safe(client);
/* This file describe the communication protocol between fcron and fcrondyn */
-#ifndef __SOCKET_H__
-#define __SOCKET_H__
+#ifndef __FCRONDYN_SVR_H__
+#define __FCRONDYN_SVR_H__
#include "dyncom.h"
+#include "select.h"
#ifdef HAVE_SYS_RESOURCE_H
/* needed by setpriority() */
#include <sys/resource.h>
#endif
-/* public var defined by socket.c */
-extern fd_set read_set;
-extern int set_max_fd;
-
/* functions prototypes */
-extern void init_socket(void);
-extern void check_socket(int num);
-extern void close_socket(void);
-
-
+extern void fcrondyn_socket_init(select_instance *si);
+extern void fcrondyn_socket_check(select_instance *si);
+extern void fcrondyn_socket_close(select_instance *si);
/* struct used by fcron : */
typedef struct fcrondyn_cl {
} fcrondyn_cl;
-#endif /* __SOCKET_H__ */
+#endif /* __FCRONDYN_SVR_H__ */
* which is a (not long) int (32bits).
* As a time_t of INT_MAX=2^31 is 'only' in year 2038, we try to use a larger value
* if we can. */
-// FIXME: test on 32bit system
+/* // FIXME: test on 32bit system */
/* 2^33 = 8589934592, so LONG is 64bits at least */
#if (LONG_MAX > INT_MAX) && (LONG_MAX > 8589934592)
/* defined as time_t of 1st Jan of year (SHRT_MAX-1900) at 00:00:00 */
#ifndef __LOG_H__
#define __LOG_H__
+#ifdef HAVE_LIBPAM
+# include "pam.h"
+#endif
+
extern char debug_opt;
extern char *logfile_path; /* path to a file to log to. Set to NULL to disable logging to a file */
extern int dosyslog; /* set to 1 when we log messages to syslog, else 0 */
--- /dev/null
+/*
+ * FCRON - periodic command scheduler
+ *
+ * Copyright 2000-2014 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.
+ */
+
+/* This file contains helper functions around an instance of select() */
+
+#include "select.h"
+#include "unistd.h"
+#include "errno.h"
+#include "global.h"
+
+void
+select_init(struct select_instance *si)
+ /* Initialize the structure (zero it) */
+{
+ FD_ZERO(&si->readfds);
+ FD_ZERO(&si->__readfds_master);
+ si->retcode = 0;
+ si->__fd_max = -1;
+}
+
+void
+select_add_read(select_instance *si, int fd)
+ /* Add a fd to the read set */
+{
+ FD_SET(fd, &si->__readfds_master);
+ if (fd > si->__fd_max) {
+ si->__fd_max = fd;
+ }
+ debug("select: added fd %d (fd_max=%d)", fd, si->__fd_max);
+
+}
+
+void
+select_rm_read(select_instance *si, int fd)
+ /* remove a fd to the read set */
+{
+ FD_CLR(fd, &si->__readfds_master);
+ if (fd >= si->__fd_max) {
+ /* fds in the fd_set may not be continuous, so we need
+ * to check them all to find the new max */
+ int i;
+ for (i=fd; i >= 0; i--) {
+ if (FD_ISSET(i, &si->__readfds_master))
+ si->__fd_max = i;
+ }
+ }
+ debug("select: removed fd %d (fd_max=%d)", fd, si->__fd_max);
+}
+
+void
+select_call(struct select_instance *si, struct timeval *timeout)
+ /* call select for instance 'si' */
+{
+ /* re-initialize readfds -- select() will change it */
+ si->readfds = si->__readfds_master;
+
+ si->retcode = select(si->__fd_max + 1, &si->readfds, NULL, NULL, timeout);
+ if (si->retcode < 0 && errno != EINTR) {
+ die_e("select returned %d", errno);
+ }
+}
+
--- /dev/null
+/*
+ * FCRON - periodic command scheduler
+ *
+ * Copyright 2000-2014 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.
+ */
+
+/* This file contains helper functions around an instance of select() */
+
+#ifndef __SELECT_H__
+#define __SELECT_H__
+
+#include "config.h"
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+
+typedef struct select_instance {
+ /* public (read-only) */
+ int retcode; /* return value of the last call of select() */
+ fd_set readfds; /* to check the status of fds after select_call */
+ /* private */
+ fd_set __readfds_master; /* select() modifies readfds, so we maintain a list in parallel */
+ int __fd_max;
+} select_instance;
+
+/* functions prototypes */
+extern void select_init(struct select_instance *si);
+extern void select_add_read(select_instance *si, int fd);
+extern void select_rm_read(select_instance *si, int fd);
+extern void select_call(struct select_instance *si, struct timeval *timeout);
+
+#endif /* __SELECT_H__ */