You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/11/27 12:52:46 UTC

svn commit: r1546002 [39/39] - in /subversion/branches/verify-keep-going: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/ contrib/server-side/svnc...

Modified: subversion/branches/verify-keep-going/tools/server-side/svnauthz.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/tools/server-side/svnauthz.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/tools/server-side/svnauthz.c (original)
+++ subversion/branches/verify-keep-going/tools/server-side/svnauthz.c Wed Nov 27 11:52:35 2013
@@ -59,7 +59,9 @@ static const apr_getopt_option_t options
   {"repository", svnauthz__repos, 1, ("repository authz name")},
   {"transaction", 't', 1, ("transaction id")},
   {"is", svnauthz__is, 1,
-    ("instead of outputing, tests if the access is ARG\n"
+    ("instead of outputting, test if the access is\n"
+     "                             "
+     "exactly ARG\n"
      "                             "
      "ARG can be one of the following values:\n"
      "                             "
@@ -67,10 +69,12 @@ static const apr_getopt_option_t options
      "                             "
      "    r    read-only access\n"
      "                             "
-     "   no    no access\n")
+     "   no    no access")
   },
-  {"groups-file", svnauthz__groups_file, 1, ("path to the global groups file")},
-  {"recursive", 'R', 0, ("recursive access to path")},
+  {"groups-file", svnauthz__groups_file, 1,
+   ("use the groups from file ARG")},
+  {"recursive", 'R', 0,
+   ("determine recursive access to PATH")},
   {0, 0, 0, 0}
 };
 
@@ -129,27 +133,32 @@ static const svn_opt_subcommand_desc2_t 
    {'t'} },
   {"accessof", subcommand_accessof, {0} /* no aliases */,
    ("Print or test the permissions set by an authz file.\n"
-    "usage: 1. svnauthz accessof [--username USER] [--groups-file GROUPS_FILE] TARGET\n"
-    "       2. svnauthz accessof [--username USER] [--groups-file GROUPS_FILE] \\\n"
-    "                            -t TXN REPOS_PATH FILE_PATH\n\n"
-    "  1. Prints the access of USER based on TARGET.\n"
+    "usage: 1. svnauthz accessof TARGET\n"
+    "       2. svnauthz accessof -t TXN REPOS_PATH FILE_PATH\n"
+    "\n"
+    "  1. Prints the access of USER to PATH based on authorization file at TARGET.\n"
     "     TARGET can be a path to a file or an absolute file:// URL to an authz\n"
-    "     file in a repository, but cannot be a repository relative URL (^/).\n\n"
-    "  2. Prints the access of USER based on authz file at FILE_PATH in the\n"
-    "     transaction TXN in the repository at REPOS_PATH.\n\n"
-    "  If the --username argument is omitted then access of an anonymous user\n"
-    "  will be printed.  If --path argument is omitted prints if any access\n"
-    "  to the repo is allowed.  If --groups-file is specified, the groups from\n"
-    "  GROUPS_FILE will be used.\n\n"
+    "     file in a repository, but cannot be a repository relative URL (^/).\n"
+    "\n"
+    "  2. Prints the access of USER to PATH based on authz file at FILE_PATH in the\n"
+    "     transaction TXN in the repository at REPOS_PATH.\n"
+    "\n"
+    "  USER is the argument to the --username option; if that option is not\n"
+    "  provided, then access of an anonymous user will be printed or tested.\n"
+    "\n"
+    "  PATH is the argument to the --path option; if that option is not provided,\n"
+    "  the maximal access to any path in the repository will be considered.\n"
+    "\n"
     "Outputs one of the following:\n"
     "     rw    write access (which also implies read)\n"
     "      r    read access\n"
-    "     no    no access\n\n"
+    "     no    no access\n"
+    "\n"
     "Returns:\n"
-    "    0   when syntax is OK and --is argument (if any) matches.\n"
+    "    0   when syntax is OK and '--is' argument (if any) matches.\n"
     "    1   when syntax is invalid.\n"
     "    2   operational error\n"
-    "    3   when --is argument doesn't match\n"
+    "    3   when '--is' argument doesn't match\n"
     ),
    {'t', svnauthz__username, svnauthz__path, svnauthz__repos, svnauthz__is,
     svnauthz__groups_file, 'R'} },
@@ -373,42 +382,6 @@ subcommand_accessof(apr_getopt_t *os, vo
 #undef EXIT_FAILURE
 #define EXIT_FAILURE 2
 
-/* Similar to svn_cmdline_handle_exit_error but with an exit_code argument
-   so we can comply with our contract and exit with 2 for internal failures.
-   Also is missing the pool argument since we don't need it given
-   main/sub_main. */
-static int
-handle_exit_error(svn_error_t *err, const char *prefix, int exit_code)
-{
-  /* Issue #3014:
-   * Don't print anything on broken pipes. The pipe was likely
-   * closed by the process at the other end. We expect that
-   * process to perform error reporting as necessary.
-   *
-   * ### This assumes that there is only one error in a chain for
-   * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
-  if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
-    svn_handle_error2(err, stderr, FALSE, prefix);
-  svn_error_clear(err);
-  return exit_code;
-}
-
-/* Report and clear the error ERR, and return EXIT_FAILURE. */
-#define EXIT_ERROR(err, exit_code)                               \
-  handle_exit_error(err, "svnauthz: ", exit_code)
-
-/* A redefinition of the public SVN_INT_ERR macro, that suppresses the
- * error message if it is SVN_ERR_IO_PIPE_WRITE_ERROR, amd with the
- * program name 'svnauthz' instead of 'svn'. */
-#undef SVN_INT_ERR
-#define SVN_INT_ERR(expr)                                        \
-  do {                                                           \
-    svn_error_t *svn_err__temp = (expr);                         \
-    if (svn_err__temp)                                           \
-      return EXIT_ERROR(svn_err__temp, EXIT_FAILURE);            \
-  } while (0)
-
-
 /* Return TRUE if the UI of 'svnauthz-validate' (svn 1.7 and earlier)
    should be emulated, given argv[0]. */
 static svn_boolean_t
@@ -476,8 +449,13 @@ canonicalize_access_file(const char **ca
   return SVN_NO_ERROR;
 }
 
-static int
-sub_main(int argc, const char *argv[], apr_pool_t *pool)
+/*
+ * 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)
 {
   svn_error_t *err;
 
@@ -488,7 +466,7 @@ sub_main(int argc, const char *argv[], a
   int i;
 
   /* Initialize the FS library. */
-  SVN_INT_ERR(svn_fs_initialize(pool));
+  SVN_ERR(svn_fs_initialize(pool));
 
   received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
 
@@ -497,7 +475,7 @@ sub_main(int argc, const char *argv[], a
   opt_state.txn = opt_state.repos_path = opt_state.groups_file = NULL;
 
   /* Parse options. */
-  SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
+  SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
   os->interleave = 1;
 
   if (!use_compat_mode(argv[0], pool))
@@ -512,8 +490,9 @@ sub_main(int argc, const char *argv[], a
             break;
           if (status != APR_SUCCESS)
             {
-              SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
-              return EXIT_FAILURE;
+              SVN_ERR(subcommand_help(NULL, NULL, pool));
+              *exit_code = EXIT_FAILURE;
+              return SVN_NO_ERROR;
             }
 
           /* Stash the option code in an array before parsing it. */
@@ -526,7 +505,7 @@ sub_main(int argc, const char *argv[], a
               opt_state.help = TRUE;
               break;
             case 't':
-              SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.txn, arg, pool));
+              SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.txn, arg, pool));
               break;
             case 'R':
               opt_state.recursive = TRUE;
@@ -535,28 +514,29 @@ sub_main(int argc, const char *argv[], a
               opt_state.version = TRUE;
               break;
             case svnauthz__username:
-              SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.username, arg, pool));
+              SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.username, arg, pool));
               break;
             case svnauthz__path:
-              SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.fspath, arg, pool));
+              SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.fspath, arg, pool));
               opt_state.fspath = svn_fspath__canonicalize(opt_state.fspath,
                                                           pool);
               break;
             case svnauthz__repos:
-              SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.repos_name, arg, pool));
+              SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.repos_name, arg, pool));
               break;
             case svnauthz__is:
-              SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.is, arg, pool));
+              SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.is, arg, pool));
               break;
             case svnauthz__groups_file:
-              SVN_INT_ERR(
+              SVN_ERR(
                   svn_utf_cstring_to_utf8(&opt_state.groups_file,
                                           arg, pool));
               break;
             default:
                 {
-                  SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
-                  return EXIT_FAILURE;
+                  SVN_ERR(subcommand_help(NULL, NULL, pool));
+                  *exit_code = EXIT_FAILURE;
+                  return SVN_NO_ERROR;
                 }
             }
         }
@@ -594,8 +574,9 @@ sub_main(int argc, const char *argv[], a
             {
               svn_error_clear(svn_cmdline_fprintf(stderr, pool,
                                         ("subcommand argument required\n")));
-              SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
-              return EXIT_FAILURE;
+              SVN_ERR(subcommand_help(NULL, NULL, pool));
+              *exit_code = EXIT_FAILURE;
+              return SVN_NO_ERROR;
             }
         }
       else
@@ -607,14 +588,15 @@ sub_main(int argc, const char *argv[], a
               const char *first_arg_utf8;
 
               os->ind++;
-              SVN_INT_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8,
+              SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8,
                                                   first_arg, pool));
               svn_error_clear(
                 svn_cmdline_fprintf(stderr, pool,
                                     ("Unknown subcommand: '%s'\n"),
                                     first_arg_utf8));
-              SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
-              return EXIT_FAILURE;
+              SVN_ERR(subcommand_help(NULL, NULL, pool));
+              *exit_code = EXIT_FAILURE;
+              return SVN_NO_ERROR;
             }
         }
     }
@@ -628,13 +610,12 @@ sub_main(int argc, const char *argv[], a
         {
           if (os->ind +2 != argc)
             {
-              err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                                     ("Repository and authz file arguments "
-                                      "required"));
-              return EXIT_ERROR(err, EXIT_FAILURE);
+              return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                      ("Repository and authz file arguments "
+                                       "required"));
             }
 
-          SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.repos_path, os->argv[os->ind],
+          SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.repos_path, os->argv[os->ind],
                                               pool));
           os->ind++;
 
@@ -644,24 +625,23 @@ sub_main(int argc, const char *argv[], a
       /* Exactly 1 non-option argument */
       if (os->ind + 1 != argc)
         {
-          err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                                 ("Authz file argument required"));
-          return EXIT_ERROR(err, EXIT_FAILURE);
+          return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                  ("Authz file argument required"));
         }
 
       /* Grab AUTHZ_FILE from argv. */
-      SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.authz_file, os->argv[os->ind],
+      SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.authz_file, os->argv[os->ind],
                                           pool));
 
       /* Canonicalize opt_state.authz_file appropriately. */
-      SVN_INT_ERR(canonicalize_access_file(&opt_state.authz_file,
+      SVN_ERR(canonicalize_access_file(&opt_state.authz_file,
                                            opt_state.authz_file,
                                            opt_state.txn != NULL, pool));
 
       /* Same for opt_state.groups_file if it is present. */
       if (opt_state.groups_file)
         {
-          SVN_INT_ERR(canonicalize_access_file(&opt_state.groups_file,
+          SVN_ERR(canonicalize_access_file(&opt_state.groups_file,
                                                opt_state.groups_file,
                                                opt_state.txn != NULL, pool));
         }
@@ -687,13 +667,14 @@ sub_main(int argc, const char *argv[], a
                                           pool);
           svn_opt_format_option(&optstr, badopt, FALSE, pool);
           if (subcommand->name[0] == '-')
-            SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
+            SVN_ERR(subcommand_help(NULL, NULL, pool));
           else
             svn_error_clear(svn_cmdline_fprintf(stderr, pool,
                             ("Subcommand '%s' doesn't accept option '%s'\n"
                              "Type 'svnauthz help %s' for usage.\n"),
                             subcommand->name, optstr, subcommand->name));
-          return EXIT_FAILURE;
+          *exit_code = EXIT_FAILURE;
+          return SVN_NO_ERROR;
         }
     }
 
@@ -715,7 +696,8 @@ sub_main(int argc, const char *argv[], a
         {
           /* Follow our contract that says we exit with 1 if the file does not
              validate. */
-          return EXIT_ERROR(err, 1);
+          *exit_code = 1;
+          return err;
         }
       else if (err->apr_err == SVN_ERR_AUTHZ_UNREADABLE
                || err->apr_err == SVN_ERR_AUTHZ_UNWRITABLE
@@ -723,31 +705,22 @@ sub_main(int argc, const char *argv[], a
         {
           /* Follow our contract that says we exit with 3 if --is does not
            * match. */
-          return EXIT_ERROR(err, 3);
+          *exit_code = 3;
+          return err;
         }
 
-
-      return EXIT_ERROR(err, EXIT_FAILURE);
-    }
-  else
-    {
-      /* Ensure that everything is written to stdout, so the user will
-         see any print errors. */
-      err = svn_cmdline_fflush(stdout);
-      if (err)
-        {
-          return EXIT_ERROR(err, EXIT_FAILURE);
-        }
-      return EXIT_SUCCESS;
+      return err;
     }
 
+  return SVN_NO_ERROR;
 }
 
 int
 main(int argc, const char *argv[])
 {
   apr_pool_t *pool;
-  int exit_code;
+  int exit_code = EXIT_SUCCESS;
+  svn_error_t *err;
 
   /* Initialize the app.  Send all error messages to 'stderr'.  */
   if (svn_cmdline_init(argv[0], stderr) != EXIT_SUCCESS)
@@ -755,7 +728,18 @@ main(int argc, const char *argv[])
 
   pool = svn_pool_create(NULL);
 
-  exit_code = sub_main(argc, argv, pool);
+  err = sub_main(&exit_code, argc, argv, pool);
+
+  /* Flush stdout and report if it fails. It would be flushed on exit anyway
+     but this makes sure that output is not silently lost if it fails. */
+  err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
+
+  if (err)
+    {
+      if (exit_code == 0)
+        exit_code = EXIT_FAILURE;
+      svn_cmdline_handle_exit_error(err, NULL, "svnauthz: ");
+    }
 
   svn_pool_destroy(pool);
   return exit_code;

Modified: subversion/branches/verify-keep-going/tools/server-side/svnpubsub/daemonize.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/tools/server-side/svnpubsub/daemonize.py?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/tools/server-side/svnpubsub/daemonize.py (original)
+++ subversion/branches/verify-keep-going/tools/server-side/svnpubsub/daemonize.py Wed Nov 27 11:52:35 2013
@@ -24,6 +24,7 @@ import os
 import signal
 import sys
 import time
+import multiprocessing  # requires Python 2.6
 
 
 # possible return values from Daemon.daemonize()
@@ -50,11 +51,11 @@ class Daemon(object):
   def daemonize_exit(self):
     try:
       result = self.daemonize()
-    except (ChildFailed, DaemonFailed) as e:
+    except (ChildFailed, DaemonFailed), e:
       # duplicate the exit code
       sys.exit(e.code)
     except (ChildTerminatedAbnormally, ChildForkFailed,
-            DaemonTerminatedAbnormally, DaemonForkFailed) as e:
+            DaemonTerminatedAbnormally, DaemonForkFailed), e:
       sys.stderr.write('ERROR: %s\n' % e)
       sys.exit(1)
     except ChildResumedIncorrectly:
@@ -71,29 +72,41 @@ class Daemon(object):
     # in original process. daemon is up and running. we're done.
 
   def daemonize(self):
-    # fork off a child that can detach itself from this process.
-    try:
-      pid = os.fork()
-    except OSError as e:
-      raise ChildForkFailed(e.errno, e.strerror)
-
-    if pid > 0:
-      # we're in the parent. let's wait for the child to finish setting
-      # things up -- on our exit, we want to ensure the child is accepting
-      # connections.
-      cpid, status = os.waitpid(pid, 0)
-      assert pid == cpid
-      if os.WIFEXITED(status):
-        code = os.WEXITSTATUS(status)
-        if code:
-          raise ChildFailed(code)
-        return DAEMON_RUNNING
-
-      # the child did not exit cleanly.
-      raise ChildTerminatedAbnormally(status)
+    ### review error situations. map to backwards compat. ??
+    ### be mindful of daemonize_exit().
+    ### we should try and raise ChildFailed / ChildTerminatedAbnormally.
+    ### ref: older revisions. OR: remove exceptions.
+
+    child_is_ready = multiprocessing.Event()
+    child_completed = multiprocessing.Event()
+
+    p = multiprocessing.Process(target=self._first_child,
+                                args=(child_is_ready, child_completed))
+    p.start()
+    
+    # Wait for the child to finish setting things up (in case we need
+    # to communicate with it). It will only exit when ready.
+    ### use a timeout here! (parameterized, of course)
+    p.join()
+
+    ### need to propagate errors, to adjust the return codes
+    if child_completed.is_set():
+      ### what was the exit status?
+      return DAEMON_COMPLETE
+    if child_is_ready.is_set():
+      return DAEMON_RUNNING
+
+    ### how did we get here?! the immediate child should not exit without
+    ### signalling ready/complete. some kind of error.
+    return DAEMON_STARTED
 
+  def _first_child(self, child_is_ready, child_completed):
     # we're in the child.
 
+    ### NOTE: the original design was a bit bunk. Exceptions raised from
+    ### this point are within the child processes. We need to signal the
+    ### errors to the parent in other ways.
+
     # decouple from the parent process
     os.chdir('/')
     os.umask(0)
@@ -102,56 +115,86 @@ class Daemon(object):
     # remember this pid so the second child can signal it.
     thispid = os.getpid()
 
-    # register a signal handler so the SIGUSR1 doesn't stop the process.
-    # this object will also record whether if got signalled.
-    daemon_accepting = SignalCatcher(signal.SIGUSR1)
-
-    # if the daemon process exits before sending SIGUSR1, then we need to see
-    # the problem. trap SIGCHLD with a SignalCatcher.
+    # if the daemon process exits before signalling readiness, then we
+    # need to see the problem. trap SIGCHLD with a SignalCatcher.
     daemon_exit = SignalCatcher(signal.SIGCHLD)
 
     # perform the second fork
     try:
       pid = os.fork()
-    except OSError as e:
+    except OSError, e:
+      ### this won't make it to the parent process
       raise DaemonForkFailed(e.errno, e.strerror)
 
     if pid > 0:
       # in the parent.
 
-      # we want to wait for the daemon to signal that it has created and
-      # bound the socket, and is (thus) ready for connections. if the
-      # daemon improperly exits before serving, we'll see SIGCHLD and the
-      # .pause will return.
-      ### we should add a timeout to this. allow an optional parameter to
-      ### specify the timeout, in case it takes a long time to start up.
-      signal.pause()
+
+      # Wait for the child to be ready for operation.
+      while True:
+        # The readiness event will invariably be signalled early/first.
+        # If it *doesn't* get signalled because the child has prematurely
+        # exited, then we will pause 10ms before noticing the exit. The
+        # pause is acceptable since that is aberrant/unexpected behavior.
+        ### is there a way to break this wait() on a signal such as SIGCHLD?
+        ### parameterize this wait, in case the app knows children may
+        ### fail quickly?
+        if child_is_ready.wait(timeout=0.010):
+          # The child signalled readiness. Yay!
+          break
+        if daemon_exit.signalled:
+          # Whoops. The child exited without signalling :-(
+          break
+        # Python 2.6 compat: .wait() may exit when set, but return None
+        if child_is_ready.is_set():
+          break
+        # A simple timeout. The child is taking a while to prepare. Go
+        # back and wait for readiness.
 
       if daemon_exit.signalled:
+        # Tell the parent that the child has exited.
+        ### we need to communicate the exit status, if possible.
+        child_completed.set()
+
         # reap the daemon process, getting its exit code. bubble it up.
         cpid, status = os.waitpid(pid, 0)
         assert pid == cpid
         if os.WIFEXITED(status):
           code = os.WEXITSTATUS(status)
           if code:
+            ### this won't make it to the parent process
             raise DaemonFailed(code)
+          ### this return value is ignored
           return DAEMON_NOT_RUNNING
 
         # the daemon did not exit cleanly.
+        ### this won't make it to the parent process
         raise DaemonTerminatedAbnormally(status)
 
-      if daemon_accepting.signalled:
-        # the daemon is up and running, so save the pid and return success.
-        if self.pidfile:
-          open(self.pidfile, 'w').write('%d\n' % pid)
-        return DAEMON_STARTED
+      # child_is_ready got asserted. the daemon is up and running, so
+      # save the pid and return success.
+      if self.pidfile:
+        # Be wary of symlink attacks
+        try:
+          os.remove(self.pidfile)
+        except OSError:
+          pass
+        fd = os.open(self.pidfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0444)
+        os.write(fd, '%d\n' % pid)
+        os.close(fd)
 
+      ### this return value is ignored
+      return DAEMON_STARTED
+
+      ### old code. what to do with this? throw ChildResumedIncorrectly
+      ### or just toss this and the exception.
       # some other signal popped us out of the pause. the daemon might not
       # be running.
+      ### this won't make it to the parent process
       raise ChildResumedIncorrectly()
 
-    # we're a deamon now. get rid of the final remnants of the parent.
-    # start by restoring default signal handlers
+    # we're a daemon now. get rid of the final remnants of the parent:
+    # restore the signal handlers and switch std* to the proper files.
     signal.signal(signal.SIGUSR1, signal.SIG_DFL)
     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
     sys.stdout.flush()
@@ -169,30 +212,31 @@ class Daemon(object):
     so.close()
     se.close()
 
-    # TEST: don't release the parent immediately. the whole parent stack
-    #       should pause along with this sleep.
+    ### TEST: don't release the parent immediately. the whole parent stack
+    ###       should pause along with this sleep.
     #time.sleep(10)
 
     # everything is set up. call the initialization function.
     self.setup()
 
-    # sleep for one second before signalling. we want to make sure the
-    # parent has called signal.pause()
-    ### we should think of a better wait around the race condition.
-    time.sleep(1)
+    ### TEST: exit before signalling.
+    #sys.exit(0)
+    #sys.exit(1)
 
-    # okay. the daemon is ready. signal the parent to tell it we're set.
-    os.kill(thispid, signal.SIGUSR1)
+    # the child is now ready for parent/anyone to communicate with it.
+    child_is_ready.set()
 
     # start the daemon now.
     self.run()
 
     # The daemon is shutting down, so toss the pidfile.
-    try:
-      os.remove(self.pidfile)
-    except OSError:
-      pass
+    if self.pidfile:
+      try:
+        os.remove(self.pidfile)
+      except OSError:
+        pass
 
+    ### this return value is ignored
     return DAEMON_COMPLETE
 
   def setup(self):
@@ -202,6 +246,34 @@ class Daemon(object):
     raise NotImplementedError
 
 
+class _Detacher(Daemon):
+  def __init__(self, target, logfile='/dev/null', pidfile=None,
+               args=(), kwargs={}):
+    Daemon.__init__(self, logfile, pidfile)
+    self.target = target
+    self.args = args
+    self.kwargs = kwargs
+
+  def setup(self):
+    pass
+
+  def run(self):
+    self.target(*self.args, **self.kwargs)
+
+    
+def run_detached(target, *args, **kwargs):
+  """Simple function to run TARGET as a detached daemon.
+  
+  The additional arguments/keywords will be passed along. This function
+  does not return -- sys.exit() will be called as appropriate.
+  
+  (capture SystemExit if logging/reporting is necessary)
+  ### if needed, a variant of this func could be written to not exit
+  """
+  d = _Detacher(target, args=args, kwargs=kwargs)
+  d.daemonize_exit()
+
+
 class SignalCatcher(object):
   def __init__(self, signum):
     self.signalled = False

Modified: subversion/branches/verify-keep-going/tools/server-side/svnpubsub/svnwcsub.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/tools/server-side/svnpubsub/svnwcsub.py?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/tools/server-side/svnpubsub/svnwcsub.py (original)
+++ subversion/branches/verify-keep-going/tools/server-side/svnpubsub/svnwcsub.py Wed Nov 27 11:52:35 2013
@@ -476,7 +476,15 @@ def handle_options(options):
     # Otherwise, we should write this (foreground) PID into the file.
     if options.pidfile and not options.daemon:
         pid = os.getpid()
-        open(options.pidfile, 'w').write('%s\n' % pid)
+        # Be wary of symlink attacks
+        try:
+            os.remove(options.pidfile)
+        except OSError:
+            pass
+        fd = os.open(options.pidfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL,
+                     0444)
+        os.write(fd, '%d\n' % pid)
+        os.close(fd)
         logging.info('pid %d written to %s', pid, options.pidfile)
 
     if options.gid:
@@ -536,7 +544,8 @@ def main(args):
 
     # We manage the logfile ourselves (along with possible rotation). The
     # daemon process can just drop stdout/stderr into /dev/null.
-    d = Daemon('/dev/null', options.pidfile, options.umask, bdec)
+    d = Daemon('/dev/null', os.path.abspath(options.pidfile),
+               options.umask, bdec)
     if options.daemon:
         # Daemonize the process and call sys.exit() with appropriate code
         d.daemonize_exit()

Modified: subversion/branches/verify-keep-going/win-tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/win-tests.py?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/win-tests.py (original)
+++ subversion/branches/verify-keep-going/win-tests.py Wed Nov 27 11:52:35 2013
@@ -84,6 +84,8 @@ def _usage_exit():
   print("  --disable-bulk-updates : Disable bulk updates on HTTP server")
   print("  --ssl-cert             : Path to SSL server certificate to trust.")
   print("  --javahl               : Run the javahl tests instead of the normal tests")
+  print("  --swig=language        : Run the swig perl/python/ruby tests instead of")
+  print("                           the normal tests")
   print("  --list                 : print test doc strings only")
   print("  --milestone-filter=RE  : RE is a regular expression pattern that (when")
   print("                           used with --list) limits the tests listed to")
@@ -108,29 +110,24 @@ CMDLINE_TEST_SCRIPT_NATIVE_PATH = CMDLIN
 sys.path.insert(0, os.path.join('build', 'generator'))
 sys.path.insert(1, 'build')
 
-import gen_win
+import gen_win_dependencies
+import gen_base
 version_header = os.path.join('subversion', 'include', 'svn_version.h')
 cp = configparser.ConfigParser()
 cp.read('gen-make.opts')
-gen_obj = gen_win.GeneratorBase('build.conf', version_header,
-                                cp.items('options'))
+gen_obj = gen_win_dependencies.GenDependenciesBase('build.conf', version_header,
+                                                   cp.items('options'))
 all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \
           + gen_obj.scripts + gen_obj.bdb_scripts
 client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)]
 
-svn_dlls = []
-for section in gen_obj.sections.values():
-  if section.options.get("msvc-export"):
-    dll_basename = section.name + "-" + str(gen_obj.version) + ".dll"
-    svn_dlls.append(os.path.join("subversion", section.name, dll_basename))
-
 opts, args = my_getopt(sys.argv[1:], 'hrdvqct:pu:f:',
                        ['release', 'debug', 'verbose', 'quiet', 'cleanup',
                         'test=', 'url=', 'svnserve-args=', 'fs-type=', 'asp.net-hack',
                         'httpd-dir=', 'httpd-port=', 'httpd-daemon',
                         'httpd-server', 'http-short-circuit', 'httpd-no-log',
                         'disable-http-v2', 'disable-bulk-updates', 'help',
-                        'fsfs-packing', 'fsfs-sharding=', 'javahl',
+                        'fsfs-packing', 'fsfs-sharding=', 'javahl', 'swig=',
                         'list', 'enable-sasl', 'bin=', 'parallel',
                         'config-file=', 'server-minor-version=', 'log-level=',
                         'log-to-stdout', 'mode-filter=', 'milestone-filter=',
@@ -156,6 +153,7 @@ http_bulk_updates = True
 list_tests = None
 milestone_filter = None
 test_javahl = None
+test_swig = None
 enable_sasl = None
 svn_bin = None
 parallel = None
@@ -216,6 +214,11 @@ for opt, val in opts:
     fsfs_packing = 1
   elif opt == '--javahl':
     test_javahl = 1
+  elif opt == '--swig':
+    if val not in ['perl', 'python', 'ruby']:
+      sys.stderr.write('Running \'%s\' swig tests not supported (yet).\n' 
+                        % (val,))
+    test_swig = val
   elif opt == '--list':
     list_tests = 1
   elif opt == '--milestone-filter':
@@ -289,12 +292,18 @@ def create_target_dir(dirname):
       print("mkdir: %s" % tgt_dir)
     os.makedirs(tgt_dir)
 
-def copy_changed_file(src, tgt):
+def copy_changed_file(src, tgt=None, to_dir=None, cleanup=True):
   if not os.path.isfile(src):
     print('Could not find ' + src)
     sys.exit(1)
-  if os.path.isdir(tgt):
-    tgt = os.path.join(tgt, os.path.basename(src))
+
+  if to_dir and not tgt:
+    tgt = os.path.join(to_dir, os.path.basename(src))
+  elif not tgt or (tgt and to_dir):
+    raise RuntimeError("Using 'tgt' *or* 'to_dir' is required" % (tgt,))
+  elif tgt and os.path.isdir(tgt):
+    raise RuntimeError("'%s' is a directory. Use to_dir=" % (tgt,))
+
   if os.path.exists(tgt):
     assert os.path.isfile(tgt)
     if filecmp.cmp(src, tgt):
@@ -306,57 +315,39 @@ def copy_changed_file(src, tgt):
     print("copy: %s" % src)
     print("  to: %s" % tgt)
   shutil.copy(src, tgt)
-  return 1
 
-def copy_execs(baton, dirname, names):
-  copied_execs = baton
-  for name in names:
-    if not name.endswith('.exe'):
-      continue
-    src = os.path.join(dirname, name)
-    tgt = os.path.join(abs_builddir, dirname, name)
-    create_target_dir(dirname)
-    if copy_changed_file(src, tgt):
-      copied_execs.append(tgt)
+  if cleanup:
+    copied_execs.append(tgt)
 
 def locate_libs():
   "Move DLLs to a known location and set env vars"
 
-  dlls = []
-
-  # look for APR 1.x dll's and use those if found
-  apr_test_path = os.path.join(gen_obj.apr_path, objdir, 'libapr-1.dll')
-  if os.path.exists(apr_test_path):
-    suffix = "-1"
-  else:
-    suffix = ""
-
-  if cp.has_option('options', '--with-static-apr'):
-    dlls.append(os.path.join(gen_obj.apr_path, objdir,
-                             'libapr%s.dll' % (suffix)))
-    dlls.append(os.path.join(gen_obj.apr_util_path, objdir,
-                             'libaprutil%s.dll' % (suffix)))
-
-  if gen_obj.libintl_path is not None:
-    dlls.append(os.path.join(gen_obj.libintl_path, 'bin', 'intl3_svn.dll'))
-
-  if gen_obj.bdb_lib is not None:
-    partial_path = os.path.join(gen_obj.bdb_path, 'bin', gen_obj.bdb_lib)
-    if objdir == 'Debug':
-      dlls.append(partial_path + 'd.dll')
-    else:
-      dlls.append(partial_path + '.dll')
-
-  if gen_obj.sasl_path is not None:
-    dlls.append(os.path.join(gen_obj.sasl_path, 'lib', 'libsasl.dll'))
+  debug = (objdir == 'Debug')
+  
+  for lib in gen_obj._libraries.values():
+
+    if debug:
+      name, dir = lib.debug_dll_name, lib.debug_dll_dir
+    else:
+      name, dir = lib.dll_name, lib.dll_dir
+      
+    if name and dir:
+      src = os.path.join(dir, name)
+      if os.path.exists(src):
+        copy_changed_file(src, to_dir=abs_builddir, cleanup=False)
+
+    for name in lib.extra_bin:
+      src = os.path.join(dir, name)
+      copy_changed_file(src, to_dir=abs_builddir)
 
-  for dll in dlls:
-    copy_changed_file(dll, abs_objdir)
 
   # Copy the Subversion library DLLs
-  if not cp.has_option('options', '--disable-shared'):
-    for svn_dll in svn_dlls:
-      copy_changed_file(os.path.join(abs_objdir, svn_dll), abs_objdir)
+  for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
+    if isinstance(i, gen_base.TargetLib) and i.msvc_export:
+      src = os.path.join(abs_objdir, i.filename)
+      if os.path.isfile(src):
+        copy_changed_file(src, to_dir=abs_builddir,
+                          cleanup=False)
 
   # Copy the Apache modules
   if run_httpd and cp.has_option('options', '--with-httpd'):
@@ -367,11 +358,11 @@ def locate_libs():
     mod_dontdothat_path = os.path.join(abs_objdir, 'tools', 'server-side',
                                         'mod_dontdothat', 'mod_dontdothat.so')
 
-    copy_changed_file(mod_dav_svn_path, abs_objdir)
-    copy_changed_file(mod_authz_svn_path, abs_objdir)
-    copy_changed_file(mod_dontdothat_path, abs_objdir)
+    copy_changed_file(mod_dav_svn_path, to_dir=abs_builddir, cleanup=False)
+    copy_changed_file(mod_authz_svn_path, to_dir=abs_builddir, cleanup=False)
+    copy_changed_file(mod_dontdothat_path, to_dir=abs_builddir, cleanup=False)
 
-  os.environ['PATH'] = abs_objdir + os.pathsep + os.environ['PATH']
+  os.environ['PATH'] = abs_builddir + os.pathsep + os.environ['PATH']
 
 def fix_case(path):
     path = os.path.normpath(path)
@@ -489,15 +480,9 @@ class Httpd:
     self._create_mime_types_file()
     self._create_dontdothat_file()
 
-    # Determine version.
-    if os.path.exists(os.path.join(self.httpd_dir,
-                                   'modules', 'mod_access_compat.so')):
-      self.httpd_ver = 2.3
-    elif os.path.exists(os.path.join(self.httpd_dir,
-                                     'modules', 'mod_auth_basic.so')):
-      self.httpd_ver = 2.2
-    else:
-      self.httpd_ver = 2.0
+    # Obtain version.
+    version_vals = gen_obj._libraries['httpd'].version.split('.')
+    self.httpd_ver = float('%s.%s' % (version_vals[0], version_vals[1]))
 
     # Create httpd config file
     fp = open(self.httpd_config, 'w')
@@ -608,7 +593,7 @@ class Httpd:
     return 'LoadModule ' + name + " " + self._quote(full_path) + '\n'
 
   def _svn_module(self, name, path):
-    full_path = os.path.join(self.abs_objdir, path)
+    full_path = os.path.join(self.abs_builddir, path)
     return 'LoadModule ' + name + ' ' + self._quote(full_path) + '\n'
 
   def _svn_repo(self, name):
@@ -688,21 +673,17 @@ class Httpd:
     print('Httpd.stop_daemon not implemented')
 
 # Move the binaries to the test directory
+create_target_dir(abs_builddir)
 locate_libs()
 if create_dirs:
-  old_cwd = os.getcwd()
-  try:
-    os.chdir(abs_objdir)
-    baton = copied_execs
-    for dirpath, dirs, files in os.walk('subversion'):
-      copy_execs(baton, dirpath, files)
-    for dirpath, dirs, files in os.walk('tools/server-side'):
-      copy_execs(baton, dirpath, files)
-  except:
-    os.chdir(old_cwd)
-    raise
-  else:
-    os.chdir(old_cwd)
+  for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
+    if isinstance(i, gen_base.TargetExe):
+      src = os.path.join(abs_objdir, i.filename)
+
+      if os.path.isfile(src):
+        dst = os.path.join(abs_builddir, i.filename)
+        create_target_dir(os.path.dirname(dst))
+        copy_changed_file(src, dst)
 
 # Create the base directory for Python tests
 create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH)
@@ -760,7 +741,7 @@ else:
   print('Testing %s configuration on %s' % (objdir, repo_loc))
 sys.path.insert(0, os.path.join(abs_srcdir, 'build'))
 
-if not test_javahl:
+if not test_javahl and not test_swig:
   import run_tests
   if log_to_stdout:
     log_file = None
@@ -788,44 +769,203 @@ if not test_javahl:
     raise
   else:
     os.chdir(old_cwd)
-else:
+elif test_javahl:
   failed = False
-  args = (
-          'java.exe',
-          '-Dtest.rootdir=' + os.path.join(abs_builddir, 'javahl'),
-          '-Dtest.srcdir=' + os.path.join(abs_srcdir,
-                                          'subversion/bindings/javahl'),
-          '-Dtest.rooturl=',
-          '-Dtest.fstype=' + fs_type ,
-          '-Dtest.tests=',
-
-          '-Djava.library.path='
-                    + os.path.join(abs_objdir,
-                                   'subversion/bindings/javahl/native'),
-          '-classpath',
-          os.path.join(abs_srcdir, 'subversion/bindings/javahl/classes') +';' +
-            gen_obj.junit_path
-         )
-
-  sys.stderr.flush()
-  print('Running org.apache.subversion tests:')
-  sys.stdout.flush()
-
-  r = subprocess.call(args + tuple(['org.apache.subversion.javahl.RunTests']))
-  sys.stdout.flush()
-  sys.stderr.flush()
-  if (r != 0):
-    print('[Test runner reported failure]')
-    failed = True
 
-  print('Running org.tigris.subversion tests:')
-  sys.stdout.flush()
-  r = subprocess.call(args + tuple(['org.tigris.subversion.javahl.RunTests']))
-  sys.stdout.flush()
-  sys.stderr.flush()
+  java_exe = None
+
+  for path in os.environ["PATH"].split(os.pathsep):
+    if os.path.isfile(os.path.join(path, 'java.exe')):
+      java_exe = os.path.join(path, 'java.exe')
+      break
+
+  if not java_exe and 'java_sdk' in gen_obj._libraries:
+    jdk = gen_obj._libraries['java_sdk']
+
+    if os.path.isfile(os.path.join(jdk.lib_dir, '../bin/java.exe')):
+      java_exe = os.path.join(jdk.lib_dir, '../bin/java.exe')
+
+  if not java_exe:
+    print('Java not found. Skipping Java tests')
+  else:
+    args = (
+            os.path.abspath(java_exe),
+            '-Dtest.rootdir=' + os.path.join(abs_builddir, 'javahl'),
+            '-Dtest.srcdir=' + os.path.join(abs_srcdir,
+                                            'subversion/bindings/javahl'),
+            '-Dtest.rooturl=',
+            '-Dtest.fstype=' + fs_type ,
+            '-Dtest.tests=',
+  
+            '-Djava.library.path='
+                      + os.path.join(abs_objdir,
+                                     'subversion/bindings/javahl/native'),
+            '-classpath',
+            os.path.join(abs_srcdir, 'subversion/bindings/javahl/classes') +';' +
+              gen_obj.junit_path
+           )
+
+    sys.stderr.flush()
+    print('Running org.apache.subversion tests:')
+    sys.stdout.flush()
+
+    r = subprocess.call(args + tuple(['org.apache.subversion.javahl.RunTests']))
+    sys.stdout.flush()
+    sys.stderr.flush()
+    if (r != 0):
+      print('[Test runner reported failure]')
+      failed = True
+
+    print('Running org.tigris.subversion tests:')
+    sys.stdout.flush()
+    r = subprocess.call(args + tuple(['org.tigris.subversion.javahl.RunTests']))
+    sys.stdout.flush()
+    sys.stderr.flush()
+    if (r != 0):
+      print('[Test runner reported failure]')
+      failed = True
+elif test_swig == 'perl':
+  failed = False
+  swig_dir = os.path.join(abs_builddir, 'swig')
+  swig_pl_dir = os.path.join(swig_dir, 'p5lib')
+  swig_pl_svn = os.path.join(swig_pl_dir, 'SVN')
+  swig_pl_auto_svn = os.path.join(swig_pl_dir, 'auto', 'SVN')
+
+  create_target_dir(swig_pl_svn)
+
+  for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
+    if isinstance(i, gen_base.TargetSWIG) and i.lang == 'perl':
+      mod_dir = os.path.join(swig_pl_auto_svn, '_' + i.name[5:].capitalize())
+      create_target_dir(mod_dir)
+      copy_changed_file(os.path.join(abs_objdir, i.filename), to_dir=mod_dir)
+
+    elif isinstance(i, gen_base.TargetSWIGLib) and i.lang == 'perl':
+      copy_changed_file(os.path.join(abs_objdir, i.filename),
+                        to_dir=abs_builddir)
+
+  pm_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'perl',
+                        'native')
+
+  tests = []
+
+  for root, dirs, files in os.walk(pm_src):
+    for name in files:
+      if name.endswith('.pm'):
+        fn = os.path.join(root, name)
+        copy_changed_file(fn, to_dir=swig_pl_svn)
+      elif name.endswith('.t'):
+        tests.append(os.path.relpath(os.path.join(root, name), pm_src))
+
+  perl5lib = swig_pl_dir
+  if 'PERL5LIB' in os.environ:
+    perl5lib += os.pathsep + os.environ['PERL5LIB']
+
+  perl_exe = 'perl.exe'
+
+  print('-- Running Swig Perl tests --')
+  old_cwd = os.getcwd()
+  try:
+    os.chdir(pm_src)
+
+    os.environ['PERL5LIB'] = perl5lib
+    os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES'
+
+    r = subprocess.call([
+              perl_exe,
+              '-MExtUtils::Command::MM',
+              '-e', 'test_harness()'
+              ] + tests)
+  finally:
+    os.chdir(old_cwd)
+
   if (r != 0):
     print('[Test runner reported failure]')
     failed = True
+  sys.exit(1)
+elif test_swig == 'python':
+  failed = False
+  swig_dir = os.path.join(abs_builddir, 'swig')
+  swig_py_dir = os.path.join(swig_dir, 'pylib')
+  swig_py_libsvn = os.path.join(swig_py_dir, 'libsvn')
+  swig_py_svn = os.path.join(swig_py_dir, 'svn')
+
+  create_target_dir(swig_py_libsvn)
+  create_target_dir(swig_py_svn)
+
+  for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
+    if (isinstance(i, gen_base.TargetSWIG)
+        or isinstance(i, gen_base.TargetSWIGLib)) and i.lang == 'python':
+
+      src = os.path.join(abs_objdir, i.filename)
+      copy_changed_file(src, to_dir=swig_py_libsvn)
+
+  py_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'python')
+
+  for py_file in os.listdir(py_src):
+    if py_file.endswith('.py'):
+      copy_changed_file(os.path.join(py_src, py_file),
+                        to_dir=swig_py_libsvn)
+
+  py_src_svn = os.path.join(py_src, 'svn')
+  for py_file in os.listdir(py_src_svn):
+    if py_file.endswith('.py'):
+      copy_changed_file(os.path.join(py_src_svn, py_file),
+                        to_dir=swig_py_svn)
+
+  print('-- Running Swig Python tests --')
+
+  pythonpath = swig_py_dir
+  if 'PYTHONPATH' in os.environ:
+    pythonpath += os.pathsep + os.environ['PYTHONPATH']
+
+  python_exe = 'python.exe'
+  old_cwd = os.getcwd()
+  try:
+    os.environ['PYTHONPATH'] = pythonpath
+
+    r = subprocess.call([
+              python_exe,
+              os.path.join(py_src, 'tests', 'run_all.py')
+              ])
+  finally:
+    os.chdir(old_cwd)
+
+    if (r != 0):
+      print('[Test runner reported failure]')
+      failed = True
+
+elif test_swig == 'ruby':
+  failed = False
+
+  if 'ruby' not in gen_obj._libraries:
+    print('Ruby not found. Skipping Ruby tests')
+  else:
+    ruby_lib = gen_obj._libraries['ruby']
+
+    ruby_exe = 'ruby.exe'
+    ruby_subdir = os.path.join('subversion', 'bindings', 'swig', 'ruby')
+    ruby_args = [
+        '-I', os.path.join(abs_srcdir, ruby_subdir),
+        os.path.join(abs_srcdir, ruby_subdir, 'test', 'run-test.rb'),
+        '--verbose'
+      ]
+
+    print('-- Running Swig Ruby tests --')
+    old_cwd = os.getcwd()
+    try:
+      os.chdir(ruby_subdir)
+
+      os.environ["BUILD_TYPE"] = objdir
+      os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES'
+      r = subprocess.call([ruby_exe] + ruby_args)
+    finally:
+      os.chdir(old_cwd)
+
+    sys.stdout.flush()
+    sys.stderr.flush()
+    if (r != 0):
+      print('[Test runner reported failure]')
+      failed = True
 
 # Stop service daemon, if any
 if daemon: