From 4e3e217796789558b2f2b4f1a94c626f6c860b0a Mon Sep 17 00:00:00 2001 From: Thibault Godouet Date: Sun, 12 Jun 2016 10:32:03 +0100 Subject: [PATCH] refactored socket.[ch] into select.[ch] and fcrondyn_srv.[ch] This is to prepare for getting notifed of time changes via timerfd_create() and select () --- Makefile.in | 4 +- config.h.in | 3 ++ configure.in | 2 +- fcron.c | 48 ++++++++------------ socket.c => fcrondyn_svr.c | 93 ++++++++++---------------------------- socket.h => fcrondyn_svr.h | 19 +++----- global.h | 2 +- log.h | 4 ++ select.c | 82 +++++++++++++++++++++++++++++++++ select.h | 54 ++++++++++++++++++++++ 10 files changed, 197 insertions(+), 114 deletions(-) rename socket.c => fcrondyn_svr.c (93%) rename socket.h => fcrondyn_svr.h (82%) create mode 100644 select.c create mode 100644 select.h diff --git a/Makefile.in b/Makefile.in index f9ee3be..5349819 100644 --- a/Makefile.in +++ b/Makefile.in @@ -73,9 +73,9 @@ OPTION := 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 diff --git a/config.h.in b/config.h.in index 74dc8f8..97b56d7 100644 --- a/config.h.in +++ b/config.h.in @@ -420,6 +420,9 @@ /* Define if you have the header file. */ #undef HAVE_SYS_RESOURCE_H +/* Define if you have the header file. */ +#undef HAVE_SYS_SELECT_H + /* Define if you have the header file. */ #undef HAVE_SYS_SOCKET_H diff --git a/configure.in b/configure.in index 1deb408..56694ef 100644 --- a/configure.in +++ b/configure.in @@ -54,7 +54,7 @@ AC_CHECK_HEADERS(errno.h sys/fcntl.h getopt.h limits.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) diff --git a/fcron.c b/fcron.c index 15dc37c..7f45998 100644 --- a/fcron.c +++ b/fcron.c @@ -29,8 +29,9 @@ #include "job.h" #include "temp_file.h" #include "fcronconf.h" +#include "select.h" #ifdef FCRONDYN -#include "socket.h" +#include "fcrondyn_svr.h" #endif @@ -45,7 +46,7 @@ RETSIGTYPE sigchild_handler(int x); 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); @@ -112,7 +113,6 @@ short int lavg_serial_running; /* number of serialized lavg job being running * 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 @@ -204,7 +204,7 @@ xexit(int exit_value) save_file(NULL); #ifdef FCRONDYN - close_socket(); + fcrondyn_socket_close(NULL); #endif f = file_base; @@ -534,8 +534,9 @@ sigcont_handler(int x) } 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: @@ -660,7 +661,7 @@ check_suspend(time_t slept_from, time_t nwt) 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 @@ -852,11 +853,6 @@ main(int argc, char **argv) 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 @@ -947,15 +943,20 @@ main_loop() 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()); @@ -979,7 +980,6 @@ main_loop() 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)); @@ -1011,19 +1011,7 @@ main_loop() /* 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); @@ -1087,7 +1075,7 @@ main_loop() #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); diff --git a/socket.c b/fcrondyn_svr.c similarity index 93% rename from socket.c rename to fcrondyn_svr.c index b3016a0..497b69e 100644 --- a/socket.c +++ b/fcrondyn_svr.c @@ -24,16 +24,16 @@ /* 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); @@ -52,14 +52,9 @@ void cmd_renice(struct fcrondyn_cl *client, long int *cmd, int fd, exe_t * e, 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 */ @@ -109,49 +104,13 @@ char err_others_nallowed_str[] = 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; @@ -201,16 +160,13 @@ init_socket(void) } /* 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); } @@ -886,13 +842,13 @@ exe_cmd(struct fcrondyn_cl *client) } 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; @@ -910,7 +866,7 @@ and make client points to the next entry */ } void -check_socket(int num) +fcrondyn_socket_check(select_instance *si) /* check for new connection, command, connection closed */ { int fd = -1, avoid_fd = -1; @@ -920,19 +876,19 @@ check_socket(int num) 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); @@ -957,7 +913,7 @@ check_socket(int num) /* 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, @@ -974,19 +930,19 @@ check_socket(int num) 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 */ @@ -1000,7 +956,7 @@ check_socket(int num) 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); @@ -1024,15 +980,13 @@ check_socket(int num) } } - 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; @@ -1045,6 +999,9 @@ close_socket(void) 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); diff --git a/socket.h b/fcrondyn_svr.h similarity index 82% rename from socket.h rename to fcrondyn_svr.h index 79a64d1..a75f531 100644 --- a/socket.h +++ b/fcrondyn_svr.h @@ -24,25 +24,20 @@ /* 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 #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 { @@ -55,4 +50,4 @@ typedef struct fcrondyn_cl { } fcrondyn_cl; -#endif /* __SOCKET_H__ */ +#endif /* __FCRONDYN_SVR_H__ */ diff --git a/global.h b/global.h index 1a656e2..7888b21 100644 --- a/global.h +++ b/global.h @@ -152,7 +152,7 @@ * 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 */ diff --git a/log.h b/log.h index e5d42ea..f928a4d 100644 --- a/log.h +++ b/log.h @@ -25,6 +25,10 @@ #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 */ diff --git a/select.c b/select.c new file mode 100644 index 0000000..7cacfc8 --- /dev/null +++ b/select.c @@ -0,0 +1,82 @@ +/* + * FCRON - periodic command scheduler + * + * Copyright 2000-2014 Thibault Godouet + * + * 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); + } +} + diff --git a/select.h b/select.h new file mode 100644 index 0000000..21dedbd --- /dev/null +++ b/select.h @@ -0,0 +1,54 @@ +/* + * FCRON - periodic command scheduler + * + * Copyright 2000-2014 Thibault Godouet + * + * 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 +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#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__ */ -- 2.40.0