Windows: Always assign newly-created processes to our job.
authorGerald Combs <gerald@wireshark.org>
Mon, 12 Mar 2018 15:49:12 +0000 (08:49 -0700)
committerAnders Broman <a.broman58@gmail.com>
Tue, 13 Mar 2018 17:18:30 +0000 (17:18 +0000)
Move ws_pipe_kill_child_on_exit to win32-utils. Add win32_create_process,
which calls CreateProcess + AssignProcessToJobObject. Use
win32_create_process instead of CreateProcess everywhere.

Bug: 1419
Change-Id: I7a1f17dddf6a73f6973d54621f271b69311400d1
Reviewed-on: https://code.wireshark.org/review/26448
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
capchild/capture_sync.c
docbook/release-notes.asciidoc
sharkd_daemon.c
wsutil/win32-utils.c
wsutil/win32-utils.h
wsutil/ws_pipe.c
wsutil/ws_pipe.h

index e0c7e330e872e26a9e96ba1433bf03729ad98190..1dc12b35d016b1711b193a6ec1515b8b6e4a496e 100644 (file)
@@ -23,6 +23,7 @@
 #ifdef _WIN32
 #include <wsutil/unicode-utils.h>
 #include <wsutil/win32-utils.h>
+#include <wsutil/ws_pipe.h>
 #endif
 
 #ifdef HAVE_SYS_WAIT_H
@@ -219,7 +220,6 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
     HANDLE signal_pipe;                     /* named pipe used to send messages from parent to child (currently only stop) */
     GString *args = g_string_sized_new(200);
     gchar *quoted_arg;
-    gunichar2 *wcommandline;
     SECURITY_ATTRIBUTES sa;
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
@@ -519,11 +519,10 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
         g_string_append(args, quoted_arg);
         g_free(quoted_arg);
     }
-    wcommandline = g_utf8_to_utf16(args->str, (glong)args->len, NULL, NULL, NULL);
 
     /* call dumpcap */
-    if(!CreateProcess(utf_8to16(argv[0]), wcommandline, NULL, NULL, TRUE,
-                      CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
+    if(!win32_create_process(argv[0], args->str, NULL, NULL, TRUE,
+                               CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
         report_failure("Couldn't run %s in child process: %s",
                        args->str, win32strerror(GetLastError()));
         ws_close(sync_pipe_read_fd);    /* Should close sync_pipe_read */
@@ -531,14 +530,12 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
         CloseHandle(signal_pipe);
         free_argv(argv, argc);
         g_string_free(args, TRUE);
-        g_free(wcommandline);
         return FALSE;
     }
     cap_session->fork_child = pi.hProcess;
     /* We may need to store this and close it later */
     CloseHandle(pi.hThread);
     g_string_free(args, TRUE);
-    g_free(wcommandline);
 
     cap_session->signal_pipe_write_fd = signal_pipe_write_fd;
 
@@ -646,7 +643,6 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd,
     HANDLE data_pipe[2];                    /* pipe used to send data from child to parent */
     GString *args = g_string_sized_new(200);
     gchar *quoted_arg;
-    gunichar2 *wcommandline;
     SECURITY_ATTRIBUTES sa;
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
@@ -752,11 +748,10 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd,
         g_string_append(args, quoted_arg);
         g_free(quoted_arg);
     }
-    wcommandline = g_utf8_to_utf16(args->str, (glong)args->len, NULL, NULL, NULL);
 
     /* call dumpcap */
-    if(!CreateProcess(utf_8to16(argv[0]), wcommandline, NULL, NULL, TRUE,
-                      CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
+    if(!win32_create_process(argv[0], args->str, NULL, NULL, TRUE,
+                               CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
         *msg = g_strdup_printf("Couldn't run %s in child process: %s",
                                args->str, win32strerror(GetLastError()));
         ws_close(*data_read_fd);       /* Should close data_pipe[PIPE_READ] */
@@ -764,14 +759,12 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd,
         ws_close(*message_read_fd);    /* Should close sync_pipe[PIPE_READ] */
         CloseHandle(sync_pipe[PIPE_WRITE]);
         g_string_free(args, TRUE);
-        g_free(wcommandline);
         return -1;
     }
     *fork_child = pi.hProcess;
     /* We may need to store this and close it later */
     CloseHandle(pi.hThread);
     g_string_free(args, TRUE);
-    g_free(wcommandline);
 #else /* _WIN32 */
     /* Create a pipe for the child process to send us messages */
     if (pipe(sync_pipe) < 0) {
index 0696b793a3a2509c38cea96bc2ca3411e08c5e87..aba8659ac49beb09291d751fa962b3f88c3010f2 100644 (file)
@@ -34,6 +34,9 @@ Features” section below for more details.
 
 //_Non-empty section placeholder._
 
+Dumpcap might not quit if Wireshark or TShark crashes.
+(ws_buglink:1419[])
+
 === New and Updated Features
 
 The following features are new (or have been significantly updated)
@@ -172,9 +175,6 @@ locations on your system.
 
 == Known Problems
 
-Dumpcap might not quit if Wireshark or TShark crashes.
-(ws_buglink:1419[])
-
 The BER dissector might infinitely loop.
 (ws_buglink:1516[])
 
index 4f98ef48cbfc3f27cb57a813bad9bd5c04c1ddf9..89af1ebcb5c1e53f7345e142b99f4c2951c1a5c1 100644 (file)
@@ -37,6 +37,7 @@
 #endif
 
 #include <wsutil/strtoi.h>
+#include <wsutil/win32-utils.h>
 
 #include "sharkd.h"
 
@@ -232,7 +233,6 @@ sharkd_loop(void)
                PROCESS_INFORMATION pi;
                STARTUPINFO si;
                char *exename;
-               gunichar2 *commandline;
 #endif
                socket_handle_t fd;
 
@@ -273,11 +273,10 @@ sharkd_loop(void)
                si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
 
                exename = g_strdup_printf("%s\\%s", get_progfile_dir(), "sharkd.exe");
-               commandline = g_utf8_to_utf16("sharkd.exe -", -1, NULL, NULL, NULL);
 
-               if (!CreateProcess(utf_8to16(exename), commandline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
+               if (!win32_create_process(exename, "sharkd.exe -", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
                {
-                       fprintf(stderr, "CreateProcess(%s) failed\n", exename);
+                       fprintf(stderr, "win32_create_process(%s) failed\n", exename);
                }
                else
                {
@@ -285,7 +284,6 @@ sharkd_loop(void)
                }
 
                g_free(exename);
-               g_free(commandline);
 #endif
 
                closesocket(fd);
index 9de0723652b70e9be94072cb76cddcb0ba5187d1..b0541a202142f278d620d26f2c5f0c52a5e71e60 100644 (file)
@@ -8,8 +8,14 @@
  * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
+#include <config.h>
+
 #include "win32-utils.h"
 
+#include <log.h>
+
+#include <tchar.h>
+
 /* Quote the argument element if necessary, so that it will get
  * reconstructed correctly in the C runtime startup code.  Note that
  * the unquoting algorithm in the C runtime is really weird, and
@@ -149,15 +155,78 @@ win32strexception(DWORD exception)
     return errbuf;
 }
 
+// This appears to be the closest equivalent to SIGPIPE on Windows.
+// https://blogs.msdn.microsoft.com/oldnewthing/20131209-00/?p=2433
+// https://stackoverflow.com/a/53214/82195
+
+static void win32_kill_child_on_exit(HANDLE child_handle) {
+    static HANDLE cjo_handle = NULL;
+    if (!cjo_handle) {
+        cjo_handle = CreateJobObject(NULL, _T("Local\\Wireshark child process cleanup"));
+
+        if (!cjo_handle) {
+            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create child cleanup job object: %s",
+                win32strerror(GetLastError()));
+            return;
+        }
+
+        JOBOBJECT_EXTENDED_LIMIT_INFORMATION cjo_jel_info = { 0 };
+        cjo_jel_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+        BOOL sijo_ret = SetInformationJobObject(cjo_handle, JobObjectExtendedLimitInformation,
+            &cjo_jel_info, sizeof(cjo_jel_info));
+        if (!sijo_ret) {
+            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not set child cleanup limits: %s",
+                win32strerror(GetLastError()));
+        }
+    }
+
+    BOOL aptjo_ret = AssignProcessToJobObject(cjo_handle, child_handle);
+    if (!aptjo_ret) {
+        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not assign child cleanup process: %s",
+            win32strerror(GetLastError()));
+    }
+}
+
+BOOL win32_create_process(const char *application_name, const char *command_line, LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD creation_flags, LPVOID environment, const char *current_directory, LPSTARTUPINFO startup_info, LPPROCESS_INFORMATION process_information)
+{
+    gunichar2 *wappname = NULL, *wcurrentdirectory = NULL;
+    gunichar2 *wcommandline = g_utf8_to_utf16(command_line, -1, NULL, NULL, NULL);
+    // CREATE_SUSPENDED: Suspend the child so that we can cleanly call
+    //     AssignProcessToJobObject.
+    // CREATE_BREAKAWAY_FROM_JOB: The main application might be associated with
+    //     a job, e.g. if we're running from ConEmu or Visual Studio. Break away
+    //     from it so that we can cleanly call AssignProcessToJobObject on *our* job.
+    DWORD wcreationflags = creation_flags|CREATE_SUSPENDED|CREATE_BREAKAWAY_FROM_JOB;
+
+    if (application_name) {
+        wappname = g_utf8_to_utf16(application_name, -1, NULL, NULL, NULL);
+    }
+    if (current_directory) {
+        wcurrentdirectory = g_utf8_to_utf16(current_directory, -1, NULL, NULL, NULL);
+    }
+    BOOL cp_res = CreateProcess(wappname, wcommandline, process_attributes, thread_attributes,
+        inherit_handles, wcreationflags, environment, wcurrentdirectory, startup_info,
+        process_information);
+    if (cp_res) {
+        win32_kill_child_on_exit(process_information->hProcess);
+        ResumeThread(process_information->hThread);
+    }
+
+    g_free(wappname);
+    g_free(wcommandline);
+    g_free(wcurrentdirectory);
+    return cp_res;
+}
+
 /*
  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
  *
  * Local Variables:
- * c-basic-offset: 2
+ * c-basic-offset: 4
  * tab-width: 8
  * indent-tabs-mode: nil
  * End:
  *
- * ex: set shiftwidth=2 tabstop=8 expandtab:
- * :indentSize=2:tabSize=8:noTabs=true:
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
  */
index 6a84dd0fa0c9492fdf96836b35470c359e63634e..553d8f19213e5fa3f391e3ccbd1b7d8c2c217895 100644 (file)
@@ -64,6 +64,28 @@ const char * win32strerror(DWORD error);
 WS_DLL_PUBLIC
 const char * win32strexception(DWORD exception);
 
+/**
+ * @brief ws_pipe_create_process Create a process and assign it to the main application
+ *        job object so that it will be killed the the main application exits.
+ * @param application_name Application name. Will be converted to its UTF-16 equivalent or NULL.
+ * @param command_line Command line. Will be converted to its UTF-16 equivalent.
+ * @param process_attributes Same as CreateProcess.
+ * @param thread_attributes Same as CreateProcess.
+ * @param inherit_handles Same as CreateProcess.
+ * @param creation_flags Will be ORed with CREATE_SUSPENDED|CREATE_BREAKAWAY_FROM_JOB.
+ * @param environment Same as CreateProcess.
+ * @param current_directory Current directory. Will be converted to its UTF-16 equivalent or NULL.
+ * @param startup_info Same as CreateProcess.
+ * @param process_information Same as CreateProcess.
+ * @return
+ */
+WS_DLL_PUBLIC
+BOOL win32_create_process(const char *application_name, const char *command_line,
+    LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes,
+    BOOL inherit_handles, DWORD creation_flags, LPVOID environment,
+    const char *current_directory, LPSTARTUPINFO startup_info, LPPROCESS_INFORMATION process_information
+);
+
 #ifdef __cplusplus
 }
 #endif
index d683b925f6ebb25cb66a584a189d2c2893de70bf..a18897adfb1a0dd40c18ca3780a2b16e87a31a5b 100644 (file)
 #include "wsutil/filesystem.h"
 #include "wsutil/ws_pipe.h"
 
-#ifdef _WIN32
-#include <tchar.h>
-#include "wsutil/win32-utils.h"
-
-// This appears to be the closest equivalent to SIGPIPE on Windows.
-// https://blogs.msdn.microsoft.com/oldnewthing/20131209-00/?p=2433
-// https://stackoverflow.com/a/53214/82195
-// We might want to make it public and use it for our other external
-// processes.
-
-// CreateProcess flags.
-// CREATE_NEW_CONSOLE: Don't interfere with the main application console.
-// CREATE_SUSPENDED: Suspend the child so that we can cleanly call
-//     AssignProcessToJobObject.
-// CREATE_BREAKAWAY_FROM_JOB: The main application might be associated with
-//     a job, e.g. if we're running from ConEmu or Visual Studio. Break away
-//     from it so that we can cleanly call AssignProcessToJobObject on *our* job.
-#define WS_CP_JOB_FLAGS (CREATE_NEW_CONSOLE|CREATE_SUSPENDED|CREATE_BREAKAWAY_FROM_JOB)
-
-static void ws_pipe_kill_child_on_exit(ws_pipe_handle child_handle) {
-    static HANDLE cjo_handle = NULL;
-    if (!cjo_handle) {
-        cjo_handle = CreateJobObject(NULL, _T("Local\\Wireshark child process cleanup"));
-
-        if (!cjo_handle) {
-            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create child cleanup job object: %s",
-                win32strerror(GetLastError()));
-            return;
-        }
-
-        JOBOBJECT_EXTENDED_LIMIT_INFORMATION cjo_jel_info = { 0 };
-        cjo_jel_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
-        BOOL sijo_ret = SetInformationJobObject(cjo_handle, JobObjectExtendedLimitInformation,
-            &cjo_jel_info, sizeof(cjo_jel_info));
-        if (!sijo_ret) {
-            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not set child cleanup limits: %s",
-                win32strerror(GetLastError()));
-        }
-    }
-
-    BOOL aptjo_ret = AssignProcessToJobObject(cjo_handle, child_handle);
-    if (!aptjo_ret) {
-        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not assign child cleanup process: %s",
-            win32strerror(GetLastError()));
-    }
-}
-#endif // _WIN32
-
 gboolean ws_pipe_spawn_sync(gchar *dirname, gchar *command, gint argc, gchar **args, gchar **command_output)
 {
     gboolean status = FALSE;
@@ -94,7 +46,6 @@ gboolean ws_pipe_spawn_sync(gchar *dirname, gchar *command, gint argc, gchar **a
 
     GString *winargs = g_string_sized_new(200);
     gchar *quoted_arg;
-    gunichar2 *wcommandline;
 
     STARTUPINFO info;
     PROCESS_INFORMATION processInfo;
@@ -158,8 +109,6 @@ gboolean ws_pipe_spawn_sync(gchar *dirname, gchar *command, gint argc, gchar **a
         g_free(quoted_arg);
     }
 
-    wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL);
-
     memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
     memset(&info, 0, sizeof(STARTUPINFO));
 
@@ -169,12 +118,10 @@ gboolean ws_pipe_spawn_sync(gchar *dirname, gchar *command, gint argc, gchar **a
     info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
     info.wShowWindow = SW_HIDE;
 
-    if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, WS_CP_JOB_FLAGS, NULL, NULL, &info, &processInfo))
+    if (win32_create_process(NULL, winargs->str, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
     {
         gchar* buffer;
 
-        ws_pipe_kill_child_on_exit(processInfo.hProcess);
-        ResumeThread(processInfo.hThread);
         WaitForSingleObject(processInfo.hProcess, INFINITE);
         buffer = (gchar*)g_malloc(BUFFER_SIZE);
         status = ws_read_string_from_pipe(child_stdout_rd, buffer, BUFFER_SIZE);
@@ -237,7 +184,6 @@ GPid ws_pipe_spawn_async(ws_pipe_t *ws_pipe, GPtrArray *args)
 
     GString *winargs = g_string_sized_new(200);
     gchar *quoted_arg;
-    gunichar2 *wcommandline;
 
     STARTUPINFO info;
     PROCESS_INFORMATION processInfo;
@@ -290,8 +236,6 @@ GPid ws_pipe_spawn_async(ws_pipe_t *ws_pipe, GPtrArray *args)
         g_free(quoted_arg);
     }
 
-    wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL);
-
     memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
     memset(&info, 0, sizeof(STARTUPINFO));
 
@@ -302,10 +246,8 @@ GPid ws_pipe_spawn_async(ws_pipe_t *ws_pipe, GPtrArray *args)
     info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
     info.wShowWindow = SW_HIDE;
 
-    if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, WS_CP_JOB_FLAGS, NULL, NULL, &info, &processInfo))
+    if (win32_create_process(NULL, winargs->str, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
     {
-        ws_pipe_kill_child_on_exit(processInfo.hProcess);
-        ResumeThread(processInfo.hThread);
         ws_pipe->stdin_fd = _open_osfhandle((intptr_t)(child_stdin_wr), _O_BINARY);
         ws_pipe->stdout_fd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY);
         ws_pipe->stderr_fd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY);
index a5f37a104069539795e790ad523f68a473a015d2..bd9fca63a3a8977e7b58312d9b94cf251a9c755c 100644 (file)
@@ -73,6 +73,7 @@ static inline gboolean ws_pipe_valid(ws_pipe_t *ws_pipe)
  */
 WS_DLL_PUBLIC GPid ws_pipe_spawn_async (ws_pipe_t * ws_pipe, GPtrArray * args );
 
+#ifdef _WIN32
 /**
  * @brief Wait for a set of handles using WaitForMultipleObjects. Windows only.
  * @param pipe_handles An array of handles
@@ -80,7 +81,6 @@ WS_DLL_PUBLIC GPid ws_pipe_spawn_async (ws_pipe_t * ws_pipe, GPtrArray * args );
  * @param pid Child process PID.
  * @return TRUE on success or FALSE on failure.
  */
-#ifdef _WIN32
 WS_DLL_PUBLIC gboolean ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid);
 #endif