]> granicus.if.org Git - fcron/commitdiff
Initial revision
authorthib <thib>
Sat, 2 Mar 2002 17:24:51 +0000 (17:24 +0000)
committerthib <thib>
Sat, 2 Mar 2002 17:24:51 +0000 (17:24 +0000)
doc/en/fcrondyn.1.sgml [new file with mode: 0644]
dyncom.h [new file with mode: 0644]
read_string.c [new file with mode: 0644]
read_string.h [new file with mode: 0644]
socket.c [new file with mode: 0644]

diff --git a/doc/en/fcrondyn.1.sgml b/doc/en/fcrondyn.1.sgml
new file mode 100644 (file)
index 0000000..a91025c
--- /dev/null
@@ -0,0 +1,91 @@
+<!-- 
+Fcron documentation
+Copyright 2000-2002 Thibault Godouet <fcron@free.fr>
+Permission is granted to copy, distribute and/or modify this
+document under the terms of the GNU Free Documentation License,
+Version 1.1 or any later version published by the Free Software
+Foundation.
+A copy of the license is included in gfdl.sgml.
+-->
+
+<!-- $Id: fcrondyn.1.sgml,v 1.1 2002-03-02 17:31:24 thib Exp $ -->
+
+<refentry id="fcrondyn.1">
+    <refmeta>
+       <refentrytitle>fcrondyn</refentrytitle>
+       <manvolnum>1</manvolnum>
+       <refmiscinfo>Fcron &version; <![%devrelease; [ (<emphasis>development</emphasis> release)]]></refmiscinfo>
+       <refmiscinfo>&date;</refmiscinfo>
+    </refmeta>
+    <refnamediv>
+       <refname>fcrondyn</refname>
+       <refpurpose>dialog dyn-amically with a running fcron daemon</refpurpose>
+    </refnamediv>
+
+    <refsect1>
+       <title>Return values</title>
+       <para>&Fcrondyn; returns &exitok; on normal exit and &exiterr; on error.</para>
+    </refsect1>
+
+    <refsect1>
+       <title>Conforming to</title>
+       <para>Should be POSIX compliant.</para>
+    </refsect1>
+
+    <refsect1 id="fcrondyn.1.files">
+       <title>Files</title>
+       <variablelist>
+           <varlistentry>
+               <term><filename>&etc;/&fcron.conf.location;</filename></term>
+               <listitem>
+                   <para>Configuration file for &fcron;, &fcrontab; and &fcrondyn; : contains paths (spool dir, pid file) and default programs to use (editor, shell, etc). See <link linkend="fcron.conf.5">&fcron.conf;(5)</link> for more details.</para>
+               </listitem>
+           </varlistentry>
+           <varlistentry>
+               <term><filename>&etc;/&fcron.allow;</filename></term>
+               <listitem>
+                   <para>Users allowed to use &fcrontab; and &fcrondyn; (one name per line, special name "all" acts for everyone)</para>
+               </listitem>
+           </varlistentry>
+           <varlistentry>
+               <term><filename>&etc;/&fcron.deny;</filename></term>
+               <listitem>
+                   <para>Users who are not allowed to use &fcrontab; and &fcrondyn; (same format as allow file)</para>
+               </listitem>
+           </varlistentry>
+           <varlistentry>
+               <term><filename>&etc;/pam.d/fcron</filename> (or <filename>&etc;/pam.conf</filename>)</term>
+               <listitem>
+                   <para><productname>PAM</productname> configuration file for &fcron;. Take a look at pam(8) for more details.</para>
+               </listitem>
+           </varlistentry>
+       </variablelist>
+    </refsect1>
+
+    <refsect1>
+       <title>See also</title>
+       <simplelist>
+           <member><link linkend="fcrontab.1">&fcrontab;(1)</link></member>
+           <member><link linkend="fcrontab.5">&fcrontab;(5)</link></member>
+           <member><link linkend="fcron.8">&fcron;(8)</link></member>
+           <member><link linkend="fcron.conf.5">&fcron.conf;(5)</link></member>
+           <member>The HTML version of the documentation, if you are not reading it right now, which is far better than these man pages :)).</member>
+       </simplelist>
+    </refsect1>
+
+    <refsect1>
+       <title>Author</title>
+       <para>Thibault Godouet &email;</para>
+    </refsect1>
+
+
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-parent-document:("fcron-doc.sgml" "book" "chapter" "sect1" "")
+sgml-default-dtd-file: "fcron-doc.ced"
+End:
+-->
+
diff --git a/dyncom.h b/dyncom.h
new file mode 100644 (file)
index 0000000..cd00a10
--- /dev/null
+++ b/dyncom.h
@@ -0,0 +1,76 @@
+/*
+ * FCRON - periodic command scheduler 
+ *
+ *  Copyright 2000-2002 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.
+ */
+
+ /* $Id: dyncom.h,v 1.1 2002-03-02 17:28:24 thib Exp $ */
+
+/* This file describe the communication protocol between fcron and fcrondyn */
+
+#ifndef __DYNCOM_H__
+#define __DYNCOM_H__
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+/* string which means : "No more data to read" */
+#define END_STR "\0\0"
+
+/* arg types */
+#define USER 1
+#define JOBID 2
+#define TIME_AND_DATE 3
+#define NICE_VALUE 4
+#define SIGNAL 5
+#define BOOLEAN 6
+
+#define ALL (-1)
+#define CUR_USER (-2)
+#define ARG_REQUIRED (-3)
+
+/* commands : if you change something here, please update fcrondyn.c's cmd_list
+ *            and fcron's socket.c . */
+#define NUM_CMD 9
+
+#define CMD_LIST_JOBS 101
+#define CMD_LIST_LAVGQ 102
+#define CMD_LIST_SERIALQ 103
+#define CMD_LIST_EXEQ 104
+#define CMD_DETAILS 105
+
+#define CMD_RESCHEDULE 201
+
+#define CMD_RUNNOW 301
+#define CMD_RUN 302
+
+#define CMD_SEND_SIGNAL 401
+#define CMD_RENICE 402
+
+#endif /* __DYNCOM_H__ */
diff --git a/read_string.c b/read_string.c
new file mode 100644 (file)
index 0000000..55b9940
--- /dev/null
@@ -0,0 +1,107 @@
+
+/*
+ * FCRON - periodic command scheduler 
+ *
+ *  Copyright 2000-2002 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.
+ */
+
+ /* $Id: read_string.c,v 1.1 2002-03-02 17:24:51 thib Exp $ */
+
+
+/* read a string (password, etc ...) securely from a tty */
+
+#include "global.h"
+#include "read_string.h"
+#include "log.h"
+
+extern char debug_opt;
+
+/* Derived from Andrew Morgan <morgan@linux.kernel.org> work in Linux PAM misc lib. */
+
+/* may be used in fcrondyn without pam : */
+#ifndef HAVE_LIBPAM
+#define PAM_MAX_MSG_SIZE LINE_LEN
+#endif
+
+char *read_string(int echo, const char *prompt)
+    /* read a line of input string, giving prompt when appropriate */
+{
+    struct termios term_before, term_tmp;
+    char line[PAM_MAX_MSG_SIZE];
+    int nc, have_term=0;
+
+    debug("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt);
+
+    if (isatty(STDIN_FILENO)) {                      /* terminal state */
+
+       /* is a terminal so record settings and flush it */
+       if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) {
+           debug("error: failed to get terminal settings");
+           return NULL;
+       }
+       memcpy(&term_tmp, &term_before, sizeof(term_tmp));
+       if (!echo) 
+           term_tmp.c_lflag &= ~(ECHO);
+       have_term = 1;
+
+    } 
+    else if (!echo)
+       debug("warning: cannot turn echo off");
+
+    /* reading the line */
+    while (1) {
+
+       fprintf(stderr, "%s", prompt);
+       /* this may, or may not set echo off -- drop pending input */
+       if (have_term)
+           (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp);
+
+       nc = read(STDIN_FILENO, line, sizeof(line)-1);
+       if (have_term) {
+           (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
+           if (!echo)             /* do we need a newline? */
+               fprintf(stderr,"\n");
+       }
+       if (nc > 0) {                 /* we got some user input */
+           char *input;
+
+           if (nc > 0 && line[nc-1] == '\n') {     /* <NUL> terminate */
+               line[--nc] = '\0';
+           } else {
+               line[nc] = '\0';
+           }
+           input = ( (line) ? strdup(line):NULL );
+           Overwrite(line);
+
+           return input;                  /* return malloc()ed string */
+       } else if (nc == 0) {                                /* Ctrl-D */
+           debug("user did not want to type anything");
+           fprintf(stderr, "\n");
+           break;
+       }
+    }
+
+    if (have_term)
+       (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
+
+    memset(line, 0, PAM_MAX_MSG_SIZE);                      /* clean up */
+    return NULL;
+}
+
diff --git a/read_string.h b/read_string.h
new file mode 100644 (file)
index 0000000..75d5328
--- /dev/null
@@ -0,0 +1,37 @@
+
+/*
+ * FCRON - periodic command scheduler 
+ *
+ *  Copyright 2000-2002 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.
+ */
+
+ /* $Id: read_string.h,v 1.1 2002-03-02 17:25:21 thib Exp $ */
+
+#ifndef __READ_STRING_H__
+#define __READ_STRING_H__
+
+#include "global.h"
+
+#define CONV_ECHO_ON  1                            /* types of echo state */
+#define CONV_ECHO_OFF 0
+
+extern char *read_string(int echo, const char *prompt);
+
+#endif /* __READ_STRING_H__ */
diff --git a/socket.c b/socket.c
new file mode 100644 (file)
index 0000000..7c1ce77
--- /dev/null
+++ b/socket.c
@@ -0,0 +1,326 @@
+/*
+ * FCRON - periodic command scheduler 
+ *
+ *  Copyright 2000-2002 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.
+ */
+
+ /* $Id: socket.c,v 1.1 2002-03-02 17:29:43 thib Exp $ */
+
+/* This file contains all fcron's code (server) to handle communication with fcrondyn */
+
+
+#include "fcron.h"
+#include "socket.h"
+
+void exe_cmd(struct fcrondyn_cl *client);
+void auth_client(struct fcrondyn_cl *client);
+
+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 */
+
+
+void
+init_socket(void)
+    /* do everything needed to get a working listening socket */
+{
+    struct sockaddr_un addr;
+    int len = 0;
+
+    /* used in fcron.c:main_loop():select() */
+    FD_ZERO(&read_set);
+
+    if ( (listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1 ) {
+       error_e("Could not create socket : fcrondyn won't work");
+       return;
+    }
+
+    addr.sun_family = AF_UNIX;
+    if ( (len = strlen(fifofile)) > sizeof(addr.sun_path) ) {
+       error("Error : fifo file path too long (max is %d)", sizeof(addr.sun_path));
+       goto err;
+    }
+    strncpy(addr.sun_path, fifofile, sizeof(addr.sun_path));
+
+    unlink(fifofile);
+    if (bind(listen_fd, (struct sockaddr *) &addr,  sizeof(addr.sun_family)+len) != 0) {
+       error_e("Cannot bind socket to '%s'", fifofile);
+       goto err;
+    }
+
+    if ( listen(listen_fd, MAX_CONNECTION) != 0 ) {
+       error_e("Cannot set socket in listen mode");
+       goto err;
+    }
+
+    /* */
+    if ( chmod(fifofile, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) != 0 )
+       error_e("Cannot fchmod() socket file");
+    /* */
+        
+    fcntl(listen_fd, F_SETFD, 1);
+    /* set listen_fd to O_NONBLOCK : we do not want fcron to be stopped on error, etc */
+    if ( fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL) | O_NONBLOCK) == -1 ) {
+       error_e("Could not set listen_fd attribute O_NONBLOCK : no fcrondyn support");
+       goto err;
+    }
+
+    /* no error */
+    FD_SET(listen_fd, &master_set);
+    if ( listen_fd > set_max_fd )
+       set_max_fd = 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);
+    return;
+
+  err:
+    close_socket();
+
+}
+
+void
+auth_client(struct fcrondyn_cl *client)
+    /* check client identity */
+{
+    char *pass_cry = NULL;
+    char *pass_sys = NULL;
+    char *pass_str = NULL;
+
+#ifdef HAVE_LIBSHADOW
+    struct spwd *pass_sp = NULL;
+    if ( (pass_sp = getspnam((char *) client->fcl_cmd )) == NULL ) {
+       error_e("could not getspnam %s", (char *) client->fcl_cmd);
+       send(client->fcl_sock_fd, "0", sizeof("0"), 0);
+       return;
+    }
+    pass_sys = pass_sp->sp_pwdp;
+#else
+    struct passwd *pass = NULL;
+    if ( (pass = getpwnam((char *) client->fcl_cmd )) == NULL ) {
+       error_e("could not getpwnam %s", (char *) client->fcl_cmd);
+       send(client->fcl_sock_fd, "0", sizeof("0"), 0);
+       return;
+    }
+    pass_sys = pass->pw_passwd;
+#endif
+
+    /* password is stored after user name */
+    pass_str = &( (char *)client->fcl_cmd ) [ strlen( (char*)client->fcl_cmd ) + 1 ];
+    if ( (pass_cry = crypt(pass_str, pass_sys)) == NULL ) {
+       error_e("could not crypt()");
+       send(client->fcl_sock_fd, "0", sizeof("0"), 0);
+       return;
+    }
+
+/*      debug("pass_sp->sp_pwdp : %s", pass_sp->sp_pwdp); */
+/*      debug("pass_cry : %s", pass_cry); */
+    if (strcmp(pass_cry, pass_sys) == 0) {
+       client->fcl_user = strdup2( (char *) client->fcl_cmd );
+       send(client->fcl_sock_fd, "1", sizeof("1"), 0);
+    }
+    else {
+       error("Invalid passwd for %s from socket %d",
+             (char *) client->fcl_cmd, client->fcl_sock_fd);
+       send(client->fcl_sock_fd, "0", sizeof("0"), 0);
+    }
+}
+
+
+void
+exe_cmd(struct fcrondyn_cl *client)
+    /* read command, and call corresponding function */
+{
+    char buf[LINE_LEN];
+    int len = 0;
+
+    switch ( client->fcl_cmd[0] ) {
+    case CMD_LIST_JOBS:
+    {
+       struct job *j;
+       debug("client->fcl_cmd[1,2,3] : %d %d %d", client->fcl_cmd[0], client->fcl_cmd[1], client->fcl_cmd[2]);
+       if ( client->fcl_cmd[1] == ALL && strcmp(client->fcl_user, ROOTNAME) != 0) {
+           warn("User %s tried to list *all* jobs.", client->fcl_user);
+           send(client->fcl_sock_fd, "you are not allowed to list all jobs.\n",
+                sizeof("you are not allowed to list all jobs.\n"), 0);
+           send(client->fcl_sock_fd, END_STR, sizeof(END_STR), 0);
+           return;
+       }
+       send(client->fcl_sock_fd, "Listing jobs :\n", sizeof("Listing jobs :\n"), 0);
+       send(client->fcl_sock_fd, "ID\tCMD\t\n", sizeof("ID\tCMD\t\n"), 0);
+       for ( j = queue_base; j != NULL; j = j->j_next ) {
+           if ( client->fcl_cmd[1] == ALL ||
+                strcmp(client->fcl_user, j->j_line->cl_file->cf_user) == 0 ) {
+               len = snprintf(buf, sizeof(buf), "%ld job: %s\n", j->j_line->cl_id, j->j_line->cl_shell);
+               send(client->fcl_sock_fd, buf, len + 1, 0);
+           }
+       }
+       /* to tell fcrondyn there's no more data to wait */
+       send(client->fcl_sock_fd, END_STR, sizeof(END_STR), 0);
+    }
+    break;
+       
+    default:
+    {
+       
+       bzero(buf, sizeof(buf));
+       len = snprintf(buf, sizeof(buf), "From exe_cmd() : Hello %s !!\n",
+                      client->fcl_user);
+       debug("From exe_cmd() : Hello %s !!", client->fcl_user);
+       send(client->fcl_sock_fd, buf, len + 1, 0);
+       len = snprintf(buf, sizeof(buf), "How are you doing ?\n");
+       send(client->fcl_sock_fd, buf, len + 1, 0);
+       
+       /* to tell fcrondyn there's no more data to wait */
+       send(client->fcl_sock_fd, END_STR, sizeof(END_STR), 0);
+    }
+    }
+}
+
+void
+check_socket(int num)
+    /* check for new connection, command, connection closed */
+{
+    int fd = -1, avoid_fd = -1, addr_len = sizeof(struct sockaddr_un);
+    struct sockaddr_un client_addr;
+    long int buf_int[SOCKET_MSG_LEN];
+    int read_len = 0;
+    struct fcrondyn_cl *client = NULL, *prev_client = NULL;
+
+    if ( num <= 0 )
+       /* no socket to check : go directly to the end of that function */
+       goto final_settings;
+
+    debug("Checking socket ...");
+
+    if ( FD_ISSET(listen_fd, &read_set) ) {
+       debug("got new connection ...");
+       if ((fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len)) == -1) {
+           error_e("could not accept new connection : isset(listen_fd = %d) = %d",
+                   listen_fd, FD_ISSET(listen_fd, &read_set));
+       }
+       else {
+           fcntl(fd, F_SETFD, 1);
+           /* set fd to O_NONBLOCK : we do not want fcron to be stopped on error, etc */
+           if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1) {
+               error_e("Could not set fd attribute O_NONBLOCK : connection rejected.");
+               close(fd);
+           }
+           else {
+               Alloc(client, fcrondyn_cl);
+               client->fcl_sock_fd = fd;
+               /* means : not authenticated yet : */
+               client->fcl_user = NULL;
+               client->fcl_cmd = NULL;
+
+               /* include new entry in client list */
+               client->fcl_next = fcrondyn_cl_base;
+               fcrondyn_cl_base = client;
+               /* to avoid trying to read from it in this call */
+               avoid_fd = fd;
+               
+               FD_SET(fd, &master_set);
+               if ( fd > set_max_fd )
+                   set_max_fd = fd;
+               fcrondyn_cl_num += 1;
+               
+               debug("Added connection fd : %d - %d connections", fd, fcrondyn_cl_num);
+           }
+       }
+    }
+
+    client = fcrondyn_cl_base;
+    while ( client != NULL ) {
+       if ( ! FD_ISSET(client->fcl_sock_fd, &read_set) || client->fcl_sock_fd==avoid_fd){
+           /* nothing to do on this one ... check the next one */
+           prev_client = client;
+           client = client->fcl_next;
+           continue;
+       }
+
+       if ( (read_len = recv(client->fcl_sock_fd, buf_int, sizeof(buf_int), 0)) <= 0 ) {
+           if (read_len == 0) {
+               /* connection closed by client */
+               close(client->fcl_sock_fd);
+               FD_CLR(client->fcl_sock_fd, &master_set);
+               debug("connection closed : fd : %d", client->fcl_sock_fd);
+               if (prev_client == NULL )
+                   client = fcrondyn_cl_base = client->fcl_next;
+               else {
+                   prev_client->fcl_next = client->fcl_next;
+                   Flush(client->fcl_cmd);
+                   Flush(client);
+                   client = prev_client->fcl_next;
+               }
+               fcrondyn_cl_num -= 1;
+           }
+           else {
+               error_e("error recv() from sock fd %d", client->fcl_sock_fd);
+               prev_client = client;
+               client = client->fcl_next;
+           }
+       }
+       else {
+           client->fcl_cmd_len = read_len;
+           client->fcl_cmd = buf_int;
+           if ( client->fcl_user == NULL )
+               /* not authenticated yet */
+               auth_client(client);
+           else {
+               /* we've just read a command ... */
+               exe_cmd(client);
+           }
+           prev_client = client;
+           client = client->fcl_next;
+       }
+    }
+
+  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 */
+{
+    struct fcrondyn_cl *client, *client_buf = NULL;
+
+    if ( listen_fd ) {
+       close(listen_fd);
+       unlink(fifofile);
+
+       client = fcrondyn_cl_base;
+       while ( client != NULL ) {
+           close(client->fcl_sock_fd);
+
+           client_buf = client->fcl_next;
+           Flush(client->fcl_cmd);
+           Flush(client);
+           fcrondyn_cl_num -= 1;
+           client = client_buf;
+       }
+    }
+}