Exemple #1
0
static int start_child_process(apr_pool_t *p, server_rec *server, struct global_config_data *d) {
    apr_proc_t* proc;
    apr_status_t status;

/*     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "Spawning child pid=%lu", (unsigned long) getpid()); */

    proc = apr_palloc(p, sizeof(apr_proc_t));
    ap_assert(proc);

    switch (status = apr_proc_fork(proc, p)) {

        case APR_INCHILD:
            child_process(p, server, d);
            exit(1);
            /* never reached */
            break;

        case APR_INPARENT:
            apr_pool_note_subprocess(p, proc, APR_KILL_ONLY_ONCE);
/*             ap_log_error(APLOG_MARK, APLOG_NOTICE, status, server, "Child process %lu", (unsigned long) proc->pid); */

            break;

        default:
            ap_log_error(APLOG_MARK, APLOG_ERR, status, server, "apr_proc_fork() failed");
            return HTTP_INTERNAL_SERVER_ERROR;
    }

    return OK;
}
Exemple #2
0
int lua_apr_proc_fork(lua_State *L)
{
  lua_apr_proc *process = proc_alloc(L, NULL);
  apr_status_t status = apr_proc_fork(&process->handle, process->memory_pool);
  if (status != APR_INCHILD && status != APR_INPARENT)
    return push_error_status(L, status);
  lua_pushstring(L, status == APR_INPARENT ? "parent" : "child");
  return 2;
}
Exemple #3
0
static void test_anon(abts_case *tc, void *data)
{
    apr_proc_t proc;
    apr_status_t rv;
    apr_shm_t *shm;
    apr_size_t retsize;
    int cnt, i;
    int recvd;

    rv = apr_shm_create(&shm, SHARED_SIZE, NULL, p);
    APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
    ABTS_PTR_NOTNULL(tc, shm);

    retsize = apr_shm_size_get(shm);
    ABTS_INT_EQUAL(tc, SHARED_SIZE, retsize);

    boxes = apr_shm_baseaddr_get(shm);
    ABTS_PTR_NOTNULL(tc, boxes);

    rv = apr_proc_fork(&proc, p);
    if (rv == APR_INCHILD) { /* child */
        int num = msgwait(5, 0, N_BOXES);
        /* exit with the number of messages received so that the parent
         * can check that all messages were received.
         */
        exit(num);
    }
    else if (rv == APR_INPARENT) { /* parent */
        i = N_BOXES;
        cnt = 0;
        while (cnt++ < N_MESSAGES) {
            if ((i-=3) < 0) {
                i += N_BOXES; /* start over at the top */
            }
            msgput(i, MSG);
            apr_sleep(apr_time_make(0, 10000));
        }
    }
    else {
        ABTS_FAIL(tc, "apr_proc_fork failed");
    }
    /* wait for the child */
    rv = apr_proc_wait(&proc, &recvd, NULL, APR_WAIT);
    ABTS_INT_EQUAL(tc, N_MESSAGES, recvd);

    rv = apr_shm_destroy(shm);
    APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
}
Exemple #4
0
/** Handles fork() calls and related housekeeping. 
 *
 * This function calls fork twice, as per Unix FAQ 1.7.  It returns 0
 * to processes that should not do further processing after this
 * function is called.
 */
static int kd_spawn(apr_pool_t *pool) {
    apr_proc_t proc;
    apr_status_t err;

    /* Going into daemon, or not? */
    err = apr_proc_fork(&proc, pool);
    if (err != APR_INPARENT && err != APR_INCHILD) {
        CRITICAL(_log_server_, "Cannot fork to daemon.");			
        return -1;
    }

    /* Parent process is bailing out. */
    if (err == APR_INPARENT) {
        kd_spawn_save_pid_file(proc.pid);
        return 0; 
    }

    /* setsid() makes the current process the leader of a new terminal
       session.  The Unix FAQ (1.7) recommends that daemons fork again
       after setsid() so that they can never be attached to a terminal
       again. */
    if (setsid() < 0) 
        WARN(_log_server_, "setsid() failed.");

    /* The child is running at this point.  Close standard inherited
       streams and open new ones to /dev/null.  That means the forked
       server will be totally silent. */    

    /* This is usually mandated by UNIX good practice, but I just
       don't feel like uncommenting that. */
    //chdir("/");
    
    close(0);
    close(1);
    close(2);
    
    open("/dev/null", O_WRONLY);
    dup(0);
    dup(0);

    return 1;
}
static void make_child(CuTest *tc, apr_proc_t **proc, apr_pool_t *p)
{
    apr_status_t rv;

    *proc = apr_pcalloc(p, sizeof(**proc));

    /* slight delay to allow things to settle */
    apr_sleep (1);

    rv = apr_proc_fork(*proc, p);
    if (rv == APR_INCHILD) {
        int i = 0;
        /* The parent process has setup all processes to call apr_terminate
         * at exit.  But, that means that all processes must also call
         * apr_initialize at startup.  You cannot have an unequal number
         * of apr_terminate and apr_initialize calls.  If you do, bad things
         * will happen.  In this case, the bad thing is that if the mutex
         * is a semaphore, it will be destroyed before all of the processes
         * die.  That means that the test will most likely fail.
         */
        apr_initialize();

        if (apr_proc_mutex_child_init(&proc_lock, NULL, p))
            exit(1);

        do {
            if (apr_proc_mutex_lock(proc_lock))
                exit(1);
            i++;
            *x = increment(*x);
            if (apr_proc_mutex_unlock(proc_lock))
                exit(1);
        } while (i < MAX_ITER);
        exit(0);
    } 

    CuAssert(tc, "fork failed", rv == APR_INPARENT);
}
Exemple #6
0
int main(int argc, const char * const argv[])
{
    apr_file_t *infd, *skwrapper;
    apr_sockaddr_t *skaddr;
    apr_getopt_t *gopt;
    apr_socket_t *skt;
    apr_pool_t *pool;
    apr_status_t rv;
    apr_proc_t proc;


    /* Command line arguments */
    int num_to_start = 1, port = 0;
    const char *interface = NULL;
    const char *command = NULL;

    apr_app_initialize(&argc, &argv, NULL);

    atexit(apr_terminate);

    apr_pool_create(&pool, NULL);

    rv = apr_getopt_init(&gopt, pool, argc, argv);
    if (rv) {
        return EXIT_FAILURE;
    }

    for (;;) {
        const char *arg;
        char opt;

        rv = apr_getopt(gopt, "c:p:i:N:", &opt, &arg);
        if (APR_STATUS_IS_EOF(rv)) {
            break;
        } else if (rv) {
            usage();
        } else {
            switch (opt) {
            case 'c':
                command = arg;
                break;

            case 'p':
                port = atoi(arg);
                if (! port) {
                    usage();
                }
                break;

            case 'i':
                interface = arg;
                break;

            case 'N':
                num_to_start = atoi(arg);
                if (! num_to_start) {
                    usage();
                }
                break;

            default:
                break;
            }
        }
    }

    if (! command || ! port) {
        usage();
    }

    rv = apr_sockaddr_info_get(&skaddr, interface, APR_UNSPEC, port, 0, pool);
    if (rv) {
        exit_error(rv, "apr_sockaddr_info_get");
    }

    rv = apr_socket_create(&skt, skaddr->family, SOCK_STREAM, APR_PROTO_TCP, pool);
    if (rv) {
        exit_error(rv, "apr_socket_create");
    }

    rv = apr_socket_bind(skt, skaddr);
    if (rv) {
        exit_error(rv, "apr_socket_bind");
    }

    rv = apr_socket_listen(skt, 1024);
    if (rv) {
        exit_error(rv, "apr_socket_listen");
    }

    rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
    if (rv) {
        exit_error(rv, "apr_proc_detach");
    }

#if defined(WIN32) || defined(OS2) || defined(NETWARE)

#error "Please implement me."

#else

    while (--num_to_start >= 0) {
        rv = apr_proc_fork(&proc, pool);
        if (rv == APR_INCHILD) {
            apr_os_file_t oft = 0;
            apr_os_sock_t oskt;

            /* Ok, so we need a file that has file descriptor 0 (which
             * FastCGI wants), but points to our socket.  This isn't really
             * possible in APR, so we cheat a bit.  I have no idea how to
             * do this on a non-unix platform, so for now this is platform
             * specific.  Ick.
             *
             * Note that this has to happen post-detach, otherwise fd 0
             * gets closed during apr_proc_detach and it's all for nothing.
             *
             * Unfortunately, doing this post detach means we have no way
             * to let anyone know if there's a problem at this point :( */

            rv = apr_os_file_put(&infd, &oft, APR_READ | APR_WRITE, pool);
            if (rv) {
                exit(EXIT_FAILURE);
            }

            rv = apr_os_sock_get(&oskt, skt);
            if (rv) {
                exit(EXIT_FAILURE);
            }

            rv = apr_os_file_put(&skwrapper, &oskt, APR_READ | APR_WRITE,
                                 pool);
            if (rv) {
                exit(EXIT_FAILURE);
            }

            rv = apr_file_dup2(infd, skwrapper, pool);
            if (rv) {
                exit(EXIT_FAILURE);
            }

            /* XXX Can't use apr_proc_create because there's no way to get
             *     infd into the procattr without going through another dup2,
             *     which means by the time it gets to the fastcgi process it
             *     is no longer fd 0, so it doesn't work.  Sigh. */

            execl(command, command, NULL);

        } else if (rv == APR_INPARENT) {
            if (num_to_start == 0) {
                apr_socket_close(skt);
            }
        } else {
            exit_error(rv, "apr_proc_fork");
        }
    }

#endif

    return EXIT_SUCCESS;
}
Exemple #7
0
/*
 * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
 * either return an error to be displayed, or set *EXIT_CODE to non-zero and
 * return SVN_NO_ERROR.
 */
static svn_error_t *
sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
{
  enum run_mode run_mode = run_mode_unspecified;
  svn_boolean_t foreground = FALSE;
  apr_socket_t *sock;
  apr_sockaddr_t *sa;
  svn_error_t *err;
  apr_getopt_t *os;
  int opt;
  serve_params_t params;
  const char *arg;
  apr_status_t status;
#ifndef WIN32
  apr_proc_t proc;
#endif
  svn_boolean_t is_multi_threaded;
  enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
  svn_boolean_t cache_fulltexts = TRUE;
  svn_boolean_t cache_txdeltas = TRUE;
  svn_boolean_t cache_revprops = FALSE;
  svn_boolean_t use_block_read = FALSE;
  apr_uint16_t port = SVN_RA_SVN_PORT;
  const char *host = NULL;
  int family = APR_INET;
  apr_int32_t sockaddr_info_flags = 0;
#if APR_HAVE_IPV6
  svn_boolean_t prefer_v6 = FALSE;
#endif
  svn_boolean_t quiet = FALSE;
  svn_boolean_t is_version = FALSE;
  int mode_opt_count = 0;
  int handling_opt_count = 0;
  const char *config_filename = NULL;
  const char *pid_filename = NULL;
  const char *log_filename = NULL;
  svn_node_kind_t kind;
  apr_size_t min_thread_count = THREADPOOL_MIN_SIZE;
  apr_size_t max_thread_count = THREADPOOL_MAX_SIZE;
#ifdef SVN_HAVE_SASL
  SVN_ERR(cyrus_init(pool));
#endif

  /* Check library versions */
  SVN_ERR(check_lib_versions());

  /* Initialize the FS library. */
  SVN_ERR(svn_fs_initialize(pool));

  SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));

  params.root = "/";
  params.tunnel = FALSE;
  params.tunnel_user = NULL;
  params.read_only = FALSE;
  params.base = NULL;
  params.cfg = NULL;
  params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
  params.logger = NULL;
  params.config_pool = NULL;
  params.authz_pool = NULL;
  params.fs_config = NULL;
  params.vhost = FALSE;
  params.username_case = CASE_ASIS;
  params.memory_cache_size = (apr_uint64_t)-1;
  params.zero_copy_limit = 0;
  params.error_check_interval = 4096;

  while (1)
    {
      status = apr_getopt_long(os, svnserve__options, &opt, &arg);
      if (APR_STATUS_IS_EOF(status))
        break;
      if (status != APR_SUCCESS)
        {
          usage(argv[0], pool);
          *exit_code = EXIT_FAILURE;
          return SVN_NO_ERROR;
        }
      switch (opt)
        {
        case '6':
#if APR_HAVE_IPV6
          prefer_v6 = TRUE;
#endif
          /* ### Maybe error here if we don't have IPV6 support? */
          break;

        case 'h':
          help(pool);
          return SVN_NO_ERROR;

        case 'q':
          quiet = TRUE;
          break;

        case SVNSERVE_OPT_VERSION:
          is_version = TRUE;
          break;

        case 'd':
          if (run_mode != run_mode_daemon)
            {
              run_mode = run_mode_daemon;
              mode_opt_count++;
            }
          break;

        case SVNSERVE_OPT_FOREGROUND:
          foreground = TRUE;
          break;

        case SVNSERVE_OPT_SINGLE_CONN:
          handling_mode = connection_mode_single;
          handling_opt_count++;
          break;

        case 'i':
          if (run_mode != run_mode_inetd)
            {
              run_mode = run_mode_inetd;
              mode_opt_count++;
            }
          break;

        case SVNSERVE_OPT_LISTEN_PORT:
          {
            apr_uint64_t val;

            err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
            if (err)
              return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
                                       _("Invalid port '%s'"), arg);
            port = (apr_uint16_t)val;
          }
          break;

        case SVNSERVE_OPT_LISTEN_HOST:
          host = arg;
          break;

        case 't':
          if (run_mode != run_mode_tunnel)
            {
              run_mode = run_mode_tunnel;
              mode_opt_count++;
            }
          break;

        case SVNSERVE_OPT_TUNNEL_USER:
          params.tunnel_user = arg;
          break;

        case 'X':
          if (run_mode != run_mode_listen_once)
            {
              run_mode = run_mode_listen_once;
              mode_opt_count++;
            }
          break;

        case 'r':
          SVN_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));

          SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool));
          if (kind != svn_node_dir)
            {
              return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                       _("Root path '%s' does not exist "
                         "or is not a directory"), params.root);
            }

          params.root = svn_dirent_internal_style(params.root, pool);
          SVN_ERR(svn_dirent_get_absolute(&params.root, params.root, pool));
          break;

        case 'R':
          params.read_only = TRUE;
          break;

        case 'T':
          handling_mode = connection_mode_thread;
          handling_opt_count++;
          break;

        case 'c':
          params.compression_level = atoi(arg);
          if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE)
            params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
          if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX)
            params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX;
          break;

        case 'M':
          params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
          break;

        case SVNSERVE_OPT_CACHE_TXDELTAS:
          cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true;
          break;

        case SVNSERVE_OPT_CACHE_FULLTEXTS:
          cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true;
          break;

        case SVNSERVE_OPT_CACHE_REVPROPS:
          cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true;
          break;

        case SVNSERVE_OPT_BLOCK_READ:
          use_block_read = svn_tristate__from_word(arg) == svn_tristate_true;
          break;

        case SVNSERVE_OPT_CLIENT_SPEED:
          {
            apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0);

            /* for slower clients, don't try anything fancy */
            if (bandwidth >= 1000)
              {
                /* block other clients for at most 1 ms (at full bandwidth).
                   Note that the send buffer is 16kB anyways. */
                params.zero_copy_limit = bandwidth * 120;

                /* check for aborted connections at the same rate */
                params.error_check_interval = bandwidth * 120;
              }
          }
          break;

        case SVNSERVE_OPT_MIN_THREADS:
          min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
          break;

        case SVNSERVE_OPT_MAX_THREADS:
          max_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
          break;

#ifdef WIN32
        case SVNSERVE_OPT_SERVICE:
          if (run_mode != run_mode_service)
            {
              run_mode = run_mode_service;
              mode_opt_count++;
            }
          break;
#endif

        case SVNSERVE_OPT_CONFIG_FILE:
          SVN_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
          config_filename = svn_dirent_internal_style(config_filename, pool);
          SVN_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
                                          pool));
          break;

        case SVNSERVE_OPT_PID_FILE:
          SVN_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
          pid_filename = svn_dirent_internal_style(pid_filename, pool);
          SVN_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, pool));
          break;

         case SVNSERVE_OPT_VIRTUAL_HOST:
           params.vhost = TRUE;
           break;

         case SVNSERVE_OPT_LOG_FILE:
          SVN_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
          log_filename = svn_dirent_internal_style(log_filename, pool);
          SVN_ERR(svn_dirent_get_absolute(&log_filename, log_filename, pool));
          break;

        }
    }

  if (is_version)
    {
      SVN_ERR(version(quiet, pool));
      return SVN_NO_ERROR;
    }

  if (os->ind != argc)
    {
      usage(argv[0], pool);
      *exit_code = EXIT_FAILURE;
      return SVN_NO_ERROR;
    }

  if (mode_opt_count != 1)
    {
      svn_error_clear(svn_cmdline_fputs(
#ifdef WIN32
                      _("You must specify exactly one of -d, -i, -t, "
                        "--service or -X.\n"),
#else
                      _("You must specify exactly one of -d, -i, -t or -X.\n"),
#endif
                       stderr, pool));
      usage(argv[0], pool);
      *exit_code = EXIT_FAILURE;
      return SVN_NO_ERROR;
    }

  if (handling_opt_count > 1)
    {
      svn_error_clear(svn_cmdline_fputs(
                      _("You may only specify one of -T or --single-thread\n"),
                      stderr, pool));
      usage(argv[0], pool);
      *exit_code = EXIT_FAILURE;
      return SVN_NO_ERROR;
    }

  /* construct object pools */
  is_multi_threaded = handling_mode == connection_mode_thread;
  params.fs_config = apr_hash_make(pool);
  svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
                cache_txdeltas ? "1" :"0");
  svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
                cache_fulltexts ? "1" :"0");
  svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
                cache_revprops ? "2" :"0");
  svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ,
                use_block_read ? "1" :"0");

  SVN_ERR(svn_repos__config_pool_create(&params.config_pool,
                                        is_multi_threaded,
                                        pool));
  SVN_ERR(svn_repos__authz_pool_create(&params.authz_pool,
                                       params.config_pool,
                                       is_multi_threaded,
                                       pool));

  /* If a configuration file is specified, load it and any referenced
   * password and authorization files. */
  if (config_filename)
    {
      params.base = svn_dirent_dirname(config_filename, pool);

      SVN_ERR(svn_repos__config_pool_get(&params.cfg, NULL,
                                         params.config_pool,
                                         config_filename,
                                         TRUE, /* must_exist */
                                         FALSE, /* names_case_sensitive */
                                         NULL,
                                         pool));
    }

  if (log_filename)
    SVN_ERR(logger__create(&params.logger, log_filename, pool));
  else if (run_mode == run_mode_listen_once)
    SVN_ERR(logger__create_for_stderr(&params.logger, pool));

  if (params.tunnel_user && run_mode != run_mode_tunnel)
    {
      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
               _("Option --tunnel-user is only valid in tunnel mode"));
    }

  if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
    {
      apr_pool_t *connection_pool;
      svn_ra_svn_conn_t *conn;
      svn_stream_t *stdin_stream;
      svn_stream_t *stdout_stream;

      params.tunnel = (run_mode == run_mode_tunnel);
      apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
                                redirect_stdout);

      SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool));
      SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));

      /* Use a subpool for the connection to ensure that if SASL is used
       * the pool cleanup handlers that call sasl_dispose() (connection_pool)
       * and sasl_done() (pool) are run in the right order. See issue #3664. */
      connection_pool = svn_pool_create(pool);
      conn = svn_ra_svn_create_conn4(NULL, stdin_stream, stdout_stream,
                                     params.compression_level,
                                     params.zero_copy_limit,
                                     params.error_check_interval,
                                     connection_pool);
      err = serve(conn, &params, connection_pool);
      svn_pool_destroy(connection_pool);

      return err;
    }

#ifdef WIN32
  /* If svnserve needs to run as a Win32 service, then we need to
     coordinate with the Service Control Manager (SCM) before
     continuing.  This function call registers the svnserve.exe
     process with the SCM, waits for the "start" command from the SCM
     (which will come very quickly), and confirms that those steps
     succeeded.

     After this call succeeds, the service is free to run.  At some
     point in the future, the SCM will send a message to the service,
     requesting that it stop.  This is translated into a call to
     winservice_notify_stop().  The service is then responsible for
     cleanly terminating.

     We need to do this before actually starting the service logic
     (opening files, sockets, etc.) because the SCM wants you to
     connect *first*, then do your service-specific logic.  If the
     service process takes too long to connect to the SCM, then the
     SCM will decide that the service is busted, and will give up on
     it.
     */
  if (run_mode == run_mode_service)
    {
      err = winservice_start();
      if (err)
        {
          svn_handle_error2(err, stderr, FALSE, "svnserve: ");

          /* This is the most common error.  It means the user started
             svnserve from a shell, and specified the --service
             argument.  svnserve cannot be started, as a service, in
             this way.  The --service argument is valid only valid if
             svnserve is started by the SCM. */
          if (err->apr_err ==
              APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
            {
              svn_error_clear(svn_cmdline_fprintf(stderr, pool,
                  _("svnserve: The --service flag is only valid if the"
                    " process is started by the Service Control Manager.\n")));
            }

          svn_error_clear(err);
          *exit_code = EXIT_FAILURE;
          return SVN_NO_ERROR;
        }

      /* The service is now in the "starting" state.  Before the SCM will
         consider the service "started", this thread must call the
         winservice_running() function. */
    }
#endif /* WIN32 */

  /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
     APR_UNSPEC, because it may give us back an IPV6 address even if we can't
     create IPV6 sockets. */

#if APR_HAVE_IPV6
#ifdef MAX_SECS_TO_LINGER
  /* ### old APR interface */
  status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
#else
  status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
                             pool);
#endif
  if (status == 0)
    {
      apr_socket_close(sock);
      family = APR_UNSPEC;

      if (prefer_v6)
        {
          if (host == NULL)
            host = "::";
          sockaddr_info_flags = APR_IPV6_ADDR_OK;
        }
      else
        {
          if (host == NULL)
            host = "0.0.0.0";
          sockaddr_info_flags = APR_IPV4_ADDR_OK;
        }
    }
#endif

  status = apr_sockaddr_info_get(&sa, host, family, port,
                                 sockaddr_info_flags, pool);
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't get address info"));
    }


#ifdef MAX_SECS_TO_LINGER
  /* ### old APR interface */
  status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
#else
  status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
                             pool);
#endif
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't create server socket"));
    }

  /* Prevents "socket in use" errors when server is killed and quickly
   * restarted. */
  status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't set options on server socket"));
    }

  status = apr_socket_bind(sock, sa);
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't bind server socket"));
    }

  status = apr_socket_listen(sock, ACCEPT_BACKLOG);
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't listen on server socket"));
    }

#if APR_HAS_FORK
  if (run_mode != run_mode_listen_once && !foreground)
    /* ### ignoring errors... */
    apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);

  apr_signal(SIGCHLD, sigchld_handler);
#endif

#ifdef SIGPIPE
  /* Disable SIGPIPE generation for the platforms that have it. */
  apr_signal(SIGPIPE, SIG_IGN);
#endif

#ifdef SIGXFSZ
  /* Disable SIGXFSZ generation for the platforms that have it, otherwise
   * working with large files when compiled against an APR that doesn't have
   * large file support will crash the program, which is uncool. */
  apr_signal(SIGXFSZ, SIG_IGN);
#endif

  if (pid_filename)
    SVN_ERR(write_pid_file(pid_filename, pool));

#ifdef WIN32
  status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
  if (status)
    winservice_svnserve_accept_socket = INVALID_SOCKET;

  /* At this point, the service is "running".  Notify the SCM. */
  if (run_mode == run_mode_service)
    winservice_running();
#endif

  /* Configure FS caches for maximum efficiency with svnserve.
   * For pre-forked (i.e. multi-processed) mode of operation,
   * keep the per-process caches smaller than the default.
   * Also, apply the respective command line parameters, if given. */
  {
    svn_cache_config_t settings = *svn_cache_config_get();

    if (params.memory_cache_size != -1)
      settings.cache_size = params.memory_cache_size;

    settings.single_threaded = TRUE;
    if (handling_mode == connection_mode_thread)
      {
#if APR_HAS_THREADS
        settings.single_threaded = FALSE;
#else
        /* No requests will be processed at all
         * (see "switch (handling_mode)" code further down).
         * But if they were, some other synchronization code
         * would need to take care of securing integrity of
         * APR-based structures. That would include our caches.
         */
#endif
      }

    svn_cache_config_set(&settings);
  }

#if APR_HAS_THREADS
  SVN_ERR(svn_root_pools__create(&connection_pools));

  if (handling_mode == connection_mode_thread)
    {
      /* create the thread pool with a valid range of threads */
      if (max_thread_count < 1)
        max_thread_count = 1;
      if (min_thread_count > max_thread_count)
        min_thread_count = max_thread_count;

      status = apr_thread_pool_create(&threads,
                                      min_thread_count,
                                      max_thread_count,
                                      pool);
      if (status)
        {
          return svn_error_wrap_apr(status, _("Can't create thread pool"));
        }

      /* let idle threads linger for a while in case more requests are
         coming in */
      apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT);

      /* don't queue requests unless we reached the worker thread limit */
      apr_thread_pool_threshold_set(threads, 0);
    }
  else
    {
      threads = NULL;
    }
#endif

  while (1)
    {
      connection_t *connection = NULL;
      SVN_ERR(accept_connection(&connection, sock, &params, handling_mode,
                                pool));
      if (run_mode == run_mode_listen_once)
        {
          err = serve_socket(connection, connection->pool);
          close_connection(connection);
          return err;
        }

      switch (handling_mode)
        {
        case connection_mode_fork:
#if APR_HAS_FORK
          status = apr_proc_fork(&proc, connection->pool);
          if (status == APR_INCHILD)
            {
              /* the child would't listen to the main server's socket */
              apr_socket_close(sock);

              /* serve_socket() logs any error it returns, so ignore it. */
              svn_error_clear(serve_socket(connection, connection->pool));
              close_connection(connection);
              return SVN_NO_ERROR;
            }
          else if (status != APR_INPARENT)
            {
              err = svn_error_wrap_apr(status, "apr_proc_fork");
              logger__log_error(params.logger, err, NULL, NULL);
              svn_error_clear(err);
            }
#endif
          break;

        case connection_mode_thread:
          /* Create a detached thread for each connection.  That's not a
             particularly sophisticated strategy for a threaded server, it's
             little different from forking one process per connection. */
#if APR_HAS_THREADS
          attach_connection(connection);

          status = apr_thread_pool_push(threads, serve_thread, connection,
                                        0, NULL);
          if (status)
            {
              return svn_error_wrap_apr(status, _("Can't push task"));
            }
#endif
          break;

        case connection_mode_single:
          /* Serve one connection at a time. */
          /* serve_socket() logs any error it returns, so ignore it. */
          svn_error_clear(serve_socket(connection, connection->pool));
        }

      close_connection(connection);
    }

  /* NOTREACHED */
}
Exemple #8
0
static void rand_fork(abts_case *tc, void *data)
{
    apr_proc_t proc;
    apr_status_t rv;
    apr_size_t nbytes = RANDOM_BUF_SZ;
    apr_size_t cmd_size = 1;
    char cmd = 'X';
    unsigned char expected[RANDOM_BUF_SZ] = {
        0xac, 0x93, 0xd2, 0x5c, 0xc7, 0xf5, 0x8d, 0xc2,
        0xd8, 0x8d, 0xb6, 0x7a, 0x94, 0xe1, 0x83, 0x4c,
        0x26, 0xe2, 0x38, 0x6d, 0xf5, 0xbd, 0x9d, 0x6e,
        0x91, 0x77, 0x3a, 0x4b, 0x9b, 0xef, 0x9b, 0xa3,
        0x9f, 0xf6, 0x6d, 0x0c, 0xdc, 0x4b, 0x02, 0xe9,
        0x5d, 0x3d, 0xfc, 0x92, 0x6b, 0xdf, 0xc9, 0xef,
        0xb9, 0xa8, 0x74, 0x09, 0xa3, 0xff, 0x64, 0x8d,
        0x19, 0xc1, 0x31, 0x31, 0x17, 0xe1, 0xb7, 0x7a,
        0xe7, 0x55, 0x14, 0x92, 0x05, 0xe3, 0x1e, 0xb8,
        0x9b, 0x1b, 0xdc, 0xac, 0x0e, 0x15, 0x08, 0xa2,
        0x93, 0x13, 0xf6, 0x04, 0xc6, 0x9d, 0xf8, 0x7f,
        0x26, 0x32, 0x68, 0x43, 0x2e, 0x5a, 0x4f, 0x47,
        0xe8, 0xf8, 0x59, 0xb7, 0xfb, 0xbe, 0x30, 0x04,
        0xb6, 0x63, 0x6f, 0x19, 0xf3, 0x2c, 0xd4, 0xeb,
        0x32, 0x8a, 0x54, 0x01, 0xd0, 0xaf, 0x3f, 0x13,
        0xc1, 0x7f, 0x10, 0x2e, 0x08, 0x1c, 0x28, 0x4b,
    };

    apr_file_t *readdatap = NULL;
    apr_file_t *writedatap = NULL;
    apr_file_t *readcmdp = NULL;
    apr_file_t *writecmdp = NULL;
    apr_pool_t *p;
    int i;

    apr_pool_create(&p, NULL);
    /* Set up data pipe for children */
    rv = apr_file_pipe_create(&readdatap, &writedatap, p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    ABTS_PTR_NOTNULL(tc, readdatap);
    ABTS_PTR_NOTNULL(tc, writedatap);
    /* Set up cmd pipe for children */
    rv = apr_file_pipe_create(&readcmdp, &writecmdp, p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    ABTS_PTR_NOTNULL(tc, readcmdp);
    ABTS_PTR_NOTNULL(tc, writecmdp);

    rand_run_kat(tc, apr_random_secure_bytes, r, expected);

    for (i = 0; i< 10; i++)
    {
        rv = apr_proc_fork(&proc, p);
        if (rv == APR_INCHILD) {
            int n = rand_check_kat(apr_random_secure_bytes, r, expected, readcmdp, writedatap);
            exit(n);
        }
        else if (rv == APR_INPARENT) {
            int exitcode;
            apr_exit_why_e why;

            /* Read the random data generated by child */
            rv = apr_file_read(readdatap, expected, &nbytes);
            ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
            /* Tell child to finish */
            rv = apr_file_write(writecmdp, &cmd, &cmd_size);
            ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
            apr_proc_wait(&proc, &exitcode, &why, APR_WAIT);
            if (why != APR_PROC_EXIT) {
                ABTS_FAIL(tc, "Child terminated abnormally");
            }
            else if (exitcode == 0) {
                if (i == 0)
                {
                    ABTS_FAIL(tc, "Child produced our randomness");
                } else
                {
                    ABTS_FAIL(tc, "Child produced randomness of previous child");
                }
            }
            else if (exitcode == 2) {
                ABTS_FAIL(tc, "Child randomness failed");
            }
            else if (exitcode != 1) {
                ABTS_FAIL(tc, "Unknown child error");
            }
        } else {
            ABTS_FAIL(tc, "Fork failed");
        }
    }

}
static int privileges_req(request_rec *r)
{
    /* secure mode: fork a process to handle the request */
    apr_proc_t proc;
    apr_status_t rv;
    int exitcode;
    apr_exit_why_e exitwhy;
    int fork_req;
    priv_cfg *cfg = ap_get_module_config(r->server->module_config,
                                         &privileges_module);

    void *breadcrumb = ap_get_module_config(r->request_config,
                                            &privileges_module);

    if (!breadcrumb) {
        /* first call: this is the vhost */
        fork_req = (cfg->mode == PRIV_SECURE);

        /* set breadcrumb */
        ap_set_module_config(r->request_config, &privileges_module, &cfg->mode);

        /* If we have per-dir config, defer doing anything */
        if ((cfg->mode == PRIV_SELECTIVE)) {
            /* Defer dropping privileges 'til we have a directory
             * context that'll tell us whether to fork.
             */
            return DECLINED;
        }
    }
    else {
        /* second call is for per-directory. */
        priv_dir_cfg *dcfg;
        if ((cfg->mode != PRIV_SELECTIVE)) {
            /* Our fate was already determined for the vhost -
             * nothing to do per-directory
             */
            return DECLINED;
        }
        dcfg = ap_get_module_config(r->per_dir_config, &privileges_module);
        fork_req = (dcfg->mode == PRIV_SECURE);
    }

    if (fork_req) {
       rv = apr_proc_fork(&proc, r->pool);
        switch (rv) {
        case APR_INPARENT:
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02140)
                          "parent waiting for child");
            /* FIXME - does the child need to run synchronously?
             * esp. if we enable mod_privileges with threaded MPMs?
             * We do need at least to ensure r outlives the child.
             */
            rv = apr_proc_wait(&proc, &exitcode, &exitwhy, APR_WAIT);
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02141) "parent: child %s",
                          (rv == APR_CHILD_DONE) ? "done" : "notdone");

            /* The child has taken responsibility for reading all input
             * and sending all output.  So we need to bow right out,
             * and even abandon "normal" housekeeping.
             */
            r->eos_sent = 1;
            apr_table_unset(r->headers_in, "Content-Type");
            apr_table_unset(r->headers_in, "Content-Length");
            /* Testing with ab and 100k requests reveals no nasties
             * so I infer we're not leaking anything like memory
             * or file descriptors.  That's nice!
             */
            return DONE;
        case APR_INCHILD:
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02142) "In child!");
            break;  /* now we'll drop privileges in the child */
        default:
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02143)
                          "Failed to fork secure child process!");
            return HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    /* OK, now drop privileges. */

    /* cleanup should happen even if something fails part-way through here */
    apr_pool_cleanup_register(r->pool, r, privileges_end_req,
                              apr_pool_cleanup_null);
    /* set user and group if configured */
    if (cfg->uid || cfg->gid) {
        if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02144)
                          "No privilege to set user/group");
        }
        /* if we should be able to set these but can't, it could be
         * a serious security issue.  Bail out rather than risk it!
         */
        if (cfg->uid && (setuid(cfg->uid) == -1)) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02145)
                          "Error setting userid");
            return HTTP_INTERNAL_SERVER_ERROR;
        }
        if (cfg->gid && (setgid(cfg->gid) == -1)) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02146)
                          "Error setting group");
            return HTTP_INTERNAL_SERVER_ERROR;
        }
    }
    /* set vhost's privileges */
    if (setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv) == -1) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02147)
                      "Error setting effective privileges");
        return HTTP_INTERNAL_SERVER_ERROR;
    }

    /* ... including those of any subprocesses */
    if (setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv) == -1) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02148)
                      "Error setting inheritable privileges");
        return HTTP_INTERNAL_SERVER_ERROR;
    }
    if (setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv) == -1) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02149)
                      "Error setting limit privileges");
        return HTTP_INTERNAL_SERVER_ERROR;
    }

    /* If we're in a child process, drop down PPERM too */
    if (fork_req) {
        if (setppriv(PRIV_SET, PRIV_PERMITTED, cfg->priv) == -1) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02150)
                          "Error setting permitted privileges");
            return HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    return OK;
}
Exemple #10
0
static apr_status_t
create_process_manager(server_rec * main_server, apr_pool_t * configpool)
{
    apr_status_t rv;

    g_process_manager =
        (apr_proc_t *) apr_pcalloc(configpool, sizeof(*g_process_manager));
    rv = apr_proc_fork(g_process_manager, configpool);
    if (rv == APR_INCHILD) {
        /* I am the child */
        g_pm_pid = getpid();
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server,
                     "mod_fcgid: Process manager %" APR_PID_T_FMT  " started", getpid());

        if ((rv = init_signal(main_server)) != APR_SUCCESS) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server,
                         "mod_fcgid: can't install signal handler, exiting now");
            exit(DAEMON_STARTUP_ERROR);
        }

        /* If running as root, switch to configured user.
         *
         * When running children via suexec, only the effective uid is
         * switched, so that the PM can return to euid 0 to kill child
         * processes.
         *
         * When running children as the configured user, the real uid
         * is switched.
         */
        if (ap_unixd_config.suexec_enabled) {
            if (getuid() != 0) {
                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, main_server,
                             "mod_fcgid: current user is not root while suexec is enabled, exiting now");
                exit(DAEMON_STARTUP_ERROR);
            }
            suexec_setup_child();
        } else
            ap_unixd_setup_child();
        apr_file_pipe_timeout_set(g_pm_read_pipe,
                                  apr_time_from_sec(g_wakeup_timeout));
        apr_file_close(g_ap_write_pipe);
        apr_file_close(g_ap_read_pipe);

        /* Initialize spawn controler */
        spawn_control_init(main_server, configpool);

        pm_main(main_server, configpool);

        ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server,
                     "mod_fcgid: Process manager %" APR_PID_T_FMT " stopped", getpid());
        exit(0);
    } else if (rv != APR_INPARENT) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server,
                     "mod_fcgid: Create process manager error");
        exit(1);
    }

    /* I am the parent
       I will send the stop signal in procmgr_stop_procmgr() */
    apr_pool_note_subprocess(configpool, g_process_manager,
                             APR_KILL_ONLY_ONCE);
    apr_proc_other_child_register(g_process_manager, fcgid_maint,
                                  g_process_manager, NULL, configpool);

    return APR_SUCCESS;
}
Exemple #11
0
static int mod_mapcache_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
   apr_status_t rc;
   mapcache_server_cfg* cfg = ap_get_module_config(s->module_config, &mapcache_module);
   apr_lockmech_e lock_type = APR_LOCK_DEFAULT;
   server_rec *sconf;

   if(!cfg) {
      ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "configuration not found in server context");
      return 1;
   }

#if APR_HAS_PROC_PTHREAD_SERIALIZE
   lock_type = APR_LOCK_PROC_PTHREAD;
#endif
   rc = apr_global_mutex_create(&cfg->mutex,cfg->mutex_name,lock_type,p);
   if(rc != APR_SUCCESS) {
      ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s, "Could not create global parent mutex %s", cfg->mutex_name);
      return rc;
   }
#ifdef AP_NEED_SET_MUTEX_PERMS
   rc = unixd_set_global_mutex_perms(cfg->mutex);
   if(rc != APR_SUCCESS) {
      ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s, "Could not set permissions on global parent mutex %s", cfg->mutex_name);
      return rc;
   }
#endif
   apr_pool_cleanup_register(p,cfg->mutex,
         (void*)apr_global_mutex_destroy, apr_pool_cleanup_null);
#ifndef DISABLE_VERSION_STRING
   ap_add_version_component(p, MAPCACHE_USERAGENT);
#endif
   for (sconf = s->next; sconf; sconf = sconf->next) {
      mapcache_server_cfg* config = ap_get_module_config(sconf->module_config, &mapcache_module);
      config->mutex = cfg->mutex;
   }

#if APR_HAS_FORK
   /* fork a child process to let it accomplish post-configuration tasks with the uid of the runtime user */
   apr_proc_t proc;
   apr_status_t rv;
   rv = apr_proc_fork(&proc, ptemp);
   if (rv == APR_INCHILD) {
#define ap_unixd_setup_child unixd_setup_child
      ap_unixd_setup_child();
      mapcache_context *ctx = (mapcache_context*)apache_server_context_create(s,p);
      for (sconf = s; sconf; sconf = sconf->next) {
         mapcache_server_cfg* config = ap_get_module_config(sconf->module_config, &mapcache_module);
         if(config->aliases) {
            apr_hash_index_t *entry = apr_hash_first(ptemp,config->aliases);

            /* loop through the configured configurations */
            while (entry) {
               const char *alias;
               apr_ssize_t aliaslen;
               mapcache_cfg *c;
               apr_hash_this(entry,(const void**)&alias,&aliaslen,(void**)&c);
               mapcache_configuration_post_config(ctx, c);
               if(GC_HAS_ERROR(ctx)) {
                  ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EGENERAL, s, "post config for %s failed: %s", alias,
                        ctx->get_error_message(ctx));
                  exit(APR_EGENERAL);
               }
               entry = apr_hash_next(entry);
            }
         }
      }
      exit(0);
   } else if (rv == APR_INPARENT) {
      apr_exit_why_e exitwhy;
      int exitcode;
      apr_proc_wait(&proc,&exitcode,&exitwhy,APR_WAIT);
      if(exitwhy != APR_PROC_EXIT) {
         ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EGENERAL, s, "mapcache post-config child terminated abnormally");
         return APR_EGENERAL;
      } else {
         if(exitcode != 0) {
            return APR_EGENERAL;
         }
      }
      return OK;
   } else {
      ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EGENERAL, s, "failed to fork mapcache post-config child");
      return APR_EGENERAL;
   }
#else /* APR_HAS_FORK */
   mapcache_context *ctx = (mapcache_context*)apache_server_context_create(s,p);
   for (sconf = s; sconf; sconf = sconf->next) {
      mapcache_server_cfg* config = ap_get_module_config(sconf->module_config, &mapcache_module);
      if(config->aliases) {
         apr_hash_index_t *entry = apr_hash_first(ptemp,config->aliases);

         /* loop through the configured configurations */
         while (entry) {
            const char *alias;
            apr_ssize_t aliaslen;
            mapcache_cfg *c;
            apr_hash_this(entry,(const void**)&alias,&aliaslen,(void**)&c);
            mapcache_configuration_post_config(ctx, c);
            if(GC_HAS_ERROR(ctx)) {
               ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EGENERAL, s, "post config for %s failed: %s", alias,
                     ctx->get_error_message(ctx));
               return APR_EGENERAL;
            }
            entry = apr_hash_next(entry);
         }
      }
   }
   return OK;
#endif
}