Merge branch 'master' into next
authorAndre Noll <maan@systemlinux.org>
Sun, 26 Apr 2009 19:05:18 +0000 (21:05 +0200)
committerAndre Noll <maan@systemlinux.org>
Sun, 26 Apr 2009 19:05:18 +0000 (21:05 +0200)
1  2 
afs.c
command.c
server.c

diff --combined afs.c
--- 1/afs.c
--- 2/afs.c
+++ b/afs.c
@@@ -776,7 -776,7 +776,7 @@@ static void signal_pre_select(struct sc
        para_fd_set(st->fd, &s->rfds, &s->max_fileno);
  }
  
- static void signal_post_select(struct sched *s, struct task *t)
+ static void afs_signal_post_select(struct sched *s, struct task *t)
  {
        struct signal_task *st = container_of(t, struct signal_task, task);
        if (getppid() == 1) {
@@@ -805,7 -805,10 +805,7 @@@ static void register_signal_task(void
  {
        struct signal_task *st = &signal_task_struct;
  
 -      if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
 -              PARA_EMERG_LOG("failed to ignore SIGPIPE\n");
 -              exit(EXIT_FAILURE);
 -      }
 +      para_sigaction(SIGPIPE, SIG_IGN);
        st->fd = para_signal_init();
        PARA_INFO_LOG("signal pipe: fd %d\n", st->fd);
        para_install_sighandler(SIGINT);
        para_install_sighandler(SIGHUP);
  
        st->task.pre_select = signal_pre_select;
-       st->task.post_select = signal_post_select;
+       st->task.post_select = afs_signal_post_select;
        sprintf(st->task.status, "signal task");
        register_task(&st->task);
  }
diff --combined command.c
+++ b/command.c
@@@ -30,8 -30,6 +30,8 @@@
  #include "user_list.h"
  #include "server_command_list.h"
  #include "afs_command_list.h"
 +#include "sched.h"
 +#include "signal.h"
  
  /** Commands including options must be shorter than this. */
  #define MAX_COMMAND_LEN 32768
@@@ -44,8 -42,15 +44,8 @@@ extern int mmd_mutex
  extern struct misc_meta_data *mmd;
  extern struct sender senders[];
  
 -static void dummy(int s)
 +static void dummy(__a_unused int s)
  {
 -      /*
 -       * At least on Solaris, SIGUSR1 is one-shot, i.e. the signal action is
 -       * restored to the default state once the signal handler has been
 -       * called.
 -       */
 -      if (s == SIGUSR1)
 -              signal(SIGUSR1, dummy);
  }
  
  static void mmd_dup(struct misc_meta_data *new_mmd)
@@@ -108,7 -113,7 +108,7 @@@ static char *get_status(struct misc_met
        char *status, *flags; /* vss status info */
        char *ut = uptime_str();
        long offset = (nmmd->offset + 500) / 1000;
 -      struct timeval now;
 +      struct timeval current_time;
        struct tm mtime_tm;
  
        /* report real status */
                localtime_r(&nmmd->mtime, &mtime_tm);
                strftime(mtime, 29, "%b %d %Y", &mtime_tm);
        }
 -      gettimeofday(&now, NULL);
 +      gettimeofday(&current_time, NULL);
        ret = make_message(
                "%s: %zu\n" /* file size */
                "%s: %s\n" /* mtime */
                        (long unsigned)nmmd->stream_start.tv_sec,
                        (long unsigned)nmmd->stream_start.tv_usec,
                status_item_list[SI_CURRENT_TIME],
 -                      (long unsigned)now.tv_sec,
 -                      (long unsigned)now.tv_usec,
 +                      (long unsigned)current_time.tv_sec,
 +                      (long unsigned)current_time.tv_usec,
  
                nmmd->afd.verbose_ls_output
  
@@@ -313,7 -318,7 +313,7 @@@ int com_stat(int fd, int argc, char * c
        struct misc_meta_data tmp, *nmmd = &tmp;
        char *s;
  
 -      signal(SIGUSR1, dummy);
 +      para_sigaction(SIGUSR1, dummy);
  
        if (argc > 1)
                num = atoi(argv[1]);
@@@ -650,14 -655,6 +650,14 @@@ out
  
  }
  
 +static void reset_signals(void)
 +{
 +      para_sigaction(SIGCHLD, SIG_IGN);
 +      para_sigaction(SIGINT, SIG_DFL);
 +      para_sigaction(SIGTERM, SIG_DFL);
 +      para_sigaction(SIGHUP, SIG_DFL);
 +}
 +
  /**
   * Perform user authentication and execute a command.
   *
@@@ -697,7 -694,11 +697,7 @@@ __noreturn void handle_connect(int fd, 
        char *p, *command = NULL;
        size_t numbytes;
  
 -      signal(SIGCHLD, SIG_IGN);
 -      signal(SIGINT, SIG_DFL);
 -      signal(SIGTERM, SIG_DFL);
 -      signal(SIGHUP, SIG_DFL);
 -
 +      reset_signals();
        /* we need a blocking fd here as recv() might return EAGAIN otherwise. */
        ret = mark_fd_blocking(fd);
        if (ret < 0)
        ret = cmd->handler(fd, argc, argv);
        mutex_lock(mmd_mutex);
        mmd->num_commands++;
-       free(mmd->afd.afhi.info_string);
-       free(mmd->afd.afhi.chunk_table);
        mutex_unlock(mmd_mutex);
        if (ret >= 0)
                goto out;
diff --combined server.c
+++ b/server.c
@@@ -291,15 -291,32 +291,32 @@@ static void signal_post_select(struct s
                        if (pid != mmd->afs_pid)
                                continue;
                        PARA_EMERG_LOG("fatal: afs died\n");
-                       goto genocide;
+                       kill(0, SIGTERM);
+                       goto cleanup;
                }
                break;
        /* die on sigint/sigterm. Kill all children too. */
        case SIGINT:
        case SIGTERM:
                PARA_EMERG_LOG("terminating on signal %d\n", st->signum);
- genocide:
                kill(0, SIGTERM);
+               /*
+                * We must wait for afs because afs catches SIGINT/SIGTERM.
+                * Before reacting to the signal, afs might want to use the
+                * shared memory area and the mmd mutex.  If we destroy this
+                * mutex too early and afs tries to lock the shared memory
+                * area, the call to mutex_lock() will fail and terminate the
+                * afs process. This leads to dirty osl tables.
+                *
+                * There's no such problem with the other children of the
+                * server process (the command handlers) as these reset their
+                * SIGINT/SIGTERM handlers to the default action, i.e.  these
+                * processes get killed immediately by the above kill().
+                */
+               PARA_INFO_LOG("waiting for afs (pid %d) to die\n",
+                       (int)mmd->afs_pid);
+               waitpid(mmd->afs_pid, NULL, 0);
+ cleanup:
                free(mmd->afd.afhi.chunk_table);
                free(mmd->afd.afhi.info_string);
                close_listed_fds();
@@@ -318,15 -335,25 +335,15 @@@ static void init_signal_task(void
        st->task.post_select = signal_post_select;
        sprintf(st->task.status, "signal task");
  
 +      PARA_NOTICE_LOG("setting up signal handling\n");
        st->fd = para_signal_init(); /* always successful */
 -
 -      PARA_NOTICE_LOG("setting up signal handlers\n");
 -      if (para_install_sighandler(SIGINT) < 0)
 -              goto err;
 -      if (para_install_sighandler(SIGTERM) < 0)
 -              goto err;
 -      if (para_install_sighandler(SIGHUP) < 0)
 -              goto err;
 -      if (para_install_sighandler(SIGCHLD) < 0)
 -              goto err;
 -      if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
 -              goto err;
 +      para_install_sighandler(SIGINT);
 +      para_install_sighandler(SIGTERM);
 +      para_install_sighandler(SIGHUP);
 +      para_install_sighandler(SIGCHLD);
 +      para_sigaction(SIGPIPE, SIG_IGN);
        add_close_on_fork_list(st->fd);
        register_task(&st->task);
 -      return;
 -err:
 -      PARA_EMERG_LOG("could not install signal handlers\n");
 -      exit(EXIT_FAILURE);
  }
  
  static void command_pre_select(struct sched *s, struct task *t)
@@@ -342,6 -369,8 +359,8 @@@ static void command_post_select(struct 
        int new_fd, ret, i;
        char *peer_name;
        pid_t child_pid;
+       uint32_t *chunk_table;
+       char *info_string;
  
        if (!FD_ISSET(sct->listen_fd, &s->rfds))
                return;
        mmd->num_connects++;
        mmd->active_connections++;
        random();
+       /* The chunk table and the info_string are pointers located in the
+        * mmd struct that point to dynamically allocated memory that must be
+        * freed by the parent and the child. However, as the mmd struct is in
+        * a shared memory area, there's no guarantee that after the fork these
+        * pointers are still valid in child context. As these two pointers are
+        * not used in the child anyway, we save them to local variables and
+        * free the memory via that copy in the child.
+        */
+       info_string = mmd->afd.afhi.info_string;
+       chunk_table = mmd->afd.afhi.chunk_table;
        child_pid = fork();
        if (child_pid < 0) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
                /* parent keeps accepting connections */
                return;
        }
+       /* mmd might already have changed at this point */
+       free(info_string);
+       free(chunk_table);
        alarm(ALARM_TIMEOUT);
        close_listed_fds();
        para_signal_shutdown();
@@@ -504,13 -546,19 +536,13 @@@ static void server_init(int argc, char 
         * before the child gets a chance to ignore this signal -- only the
         * good die young.
         */
 -      if (signal(SIGUSR1, SIG_IGN) == SIG_ERR) {
 -              PARA_EMERG_LOG("failed to ignore SIGUSR1\n");
 -              exit(EXIT_FAILURE);
 -      }
 +      para_sigaction(SIGUSR1, SIG_IGN);
        /*
         * We have to install a SIGCHLD handler before the afs process is being
         * forked off. Otherwise, para_server does not notice if afs dies before
         * the SIGCHLD handler has been installed by init_signal_task() below.
         */
 -      if (signal(SIGCHLD, tmp_sigchld_handler) == SIG_ERR) {
 -              PARA_EMERG_LOG("failed to install temporary SIGCHLD handler\n");
 -              exit(EXIT_FAILURE);
 -      }
 +      para_sigaction(SIGCHLD, tmp_sigchld_handler);
        PARA_NOTICE_LOG("initializing the audio file selector\n");
        afs_socket = init_afs();
        init_signal_task();