Add g_spawn_check_exit_status()
[platform/upstream/glib.git] / glib / gspawn.c
index 5d8d57b..c8dbc4f 100644 (file)
@@ -94,6 +94,12 @@ g_spawn_error_quark (void)
   return g_quark_from_static_string ("g-exec-error-quark");
 }
 
+GQuark
+g_spawn_exit_error_quark (void)
+{
+  return g_quark_from_static_string ("g-spawn-exit-error-quark");
+}
+
 /**
  * g_spawn_async:
  * @working_directory: (allow-none): child's current working directory, or %NULL to inherit parent's
@@ -232,13 +238,15 @@ read_data (GString *str,
  * if those parameters are non-%NULL. Note that you must set the  
  * %G_SPAWN_STDOUT_TO_DEV_NULL and %G_SPAWN_STDERR_TO_DEV_NULL flags when
  * passing %NULL for @standard_output and @standard_error.
- * If @exit_status is non-%NULL, the exit status of the child is stored
- * there as it would be returned by waitpid(); standard UNIX macros such 
- * as WIFEXITED() and WEXITSTATUS() must be used to evaluate the exit status.
- * Note that this function call waitpid() even if @exit_status is %NULL, and
- * does not accept the %G_SPAWN_DO_NOT_REAP_CHILD flag.
- * If an error occurs, no data is returned in @standard_output, 
- * @standard_error, or @exit_status. 
+ *
+ * If @exit_status is non-%NULL, the platform-specific exit status of
+ * the child is stored there; see the doucumentation of
+ * g_spawn_check_exit_status() for how to use and interpret this.
+ * Note that it is invalid to pass %G_SPAWN_DO_NOT_REAP_CHILD in
+ * @flags.
+ *
+ * If an error occurs, no data is returned in @standard_output,
+ * @standard_error, or @exit_status.
  *
  * This function calls g_spawn_async_with_pipes() internally; see that
  * function for full details on the other parameters and details on
@@ -701,9 +709,9 @@ g_spawn_async_with_pipes (const gchar          *working_directory,
  * appropriate. Possible errors are those from g_spawn_sync() and those
  * from g_shell_parse_argv().
  *
- * If @exit_status is non-%NULL, the exit status of the child is stored there as
- * it would be returned by waitpid(); standard UNIX macros such as WIFEXITED()
- * and WEXITSTATUS() must be used to evaluate the exit status.
+ * If @exit_status is non-%NULL, the platform-specific exit status of
+ * the child is stored there; see the documentation of
+ * g_spawn_check_exit_status() for how to use and interpret this.
  * 
  * On Windows, please note the implications of g_shell_parse_argv()
  * parsing @command_line. Parsing is done according to Unix shell rules, not 
@@ -793,6 +801,106 @@ g_spawn_command_line_async (const gchar *command_line,
   return retval;
 }
 
+/**
+ * g_spawn_check_exit_status:
+ * @exit_status: An exit code as returned from g_spawn_sync()
+ * @error: a #GError
+ *
+ * Set @error if @exit_status indicates the child exited abnormally
+ * (e.g. with a nonzero exit code, or via a fatal signal).
+ *
+ * The g_spawn_sync() and g_child_watch_add() family of APIs return an
+ * exit status for subprocesses encoded in a platform-specific way.
+ * On Unix, this is guaranteed to be in the same format
+ * <literal>waitpid(2)</literal> returns, and on Windows it is
+ * guaranteed to be the result of
+ * <literal>GetExitCodeProcess()</literal>.  Prior to the introduction
+ * of this function in GLib 2.34, interpreting @exit_status required
+ * use of platform-specific APIs, which is problematic for software
+ * using GLib as a cross-platform layer.
+ *
+ * Additionally, many programs simply want to determine whether or not
+ * the child exited successfully, and either propagate a #GError or
+ * print a message to standard error.  In that common case, this
+ * function can be used.  Note that the error message in @error will
+ * contain human-readable information about the exit status.
+ *
+ * The <literal>domain</literal> and <literal>code</literal> of @error
+ * have special semantics in the case where the process has an "exit
+ * code", as opposed to being killed by a signal.  On Unix, this
+ * happens if <literal>WIFEXITED</literal> would be true of
+ * @exit_status.  On Windows, it is always the case.
+ *
+ * The special semantics are that the actual exit code will be the
+ * code set in @error, and the domain will be %G_SPAWN_EXIT_ERROR.
+ * This allows you to differentiate between different exit codes.
+ *
+ * If the process was terminated by some means other than an exit
+ * status, the domain will be %G_SPAWN_ERROR, and the code will be
+ * %G_SPAWN_ERROR_FAILED.
+ *
+ * This function just offers convenience; you can of course also check
+ * the available platform via a macro such as %G_OS_UNIX, and use
+ * <literal>WIFEXITED()</literal> and <literal>WEXITSTATUS()</literal>
+ * on @exit_status directly.  Do not attempt to scan or parse the
+ * error message string; it may be translated and/or change in future
+ * versions of GLib.
+ *
+ * Returns: %TRUE if child exited successfully, %FALSE otherwise (and @error will be set)
+ * Since: 2.34
+ */
+gboolean
+g_spawn_check_exit_status (gint      exit_status,
+                          GError  **error)
+{
+  gboolean ret = FALSE;
+
+#ifdef G_OS_UNIX
+  if (WIFEXITED (exit_status))
+    {
+      if (WEXITSTATUS (exit_status) != 0)
+       {
+         g_set_error (error, G_SPAWN_EXIT_ERROR, WEXITSTATUS (exit_status),
+                      _("Child process exited with code %ld"),
+                      (long) WEXITSTATUS (exit_status));
+         goto out;
+       }
+    }
+  else if (WIFSIGNALED (exit_status))
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Child process killed by signal %ld"),
+                  (long) WTERMSIG (exit_status));
+      goto out;
+    }
+  else if (WIFSTOPPED (exit_status))
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Child process stopped by signal %ld"),
+                  (long) WSTOPSIG (exit_status));
+      goto out;
+    }
+  else
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Child process exited abnormally"));
+      goto out;
+    }
+#else
+  if (exit_status != 0)
+    {
+      g_set_error (error, G_SPAWN_EXIT_ERROR, exit_status,
+                  _("Child process exited with code %ld"),
+                  (long) exit_status);
+      goto out;
+    }
+#endif
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
 static gint
 exec_err_to_g_error (gint en)
 {