D-Bus
1.10.12
|
00001 #include <config.h> 00002 00003 //#define SPAWN_DEBUG 00004 00005 #if !defined(SPAWN_DEBUG) || defined(_MSC_VER) 00006 #define PING() 00007 #else 00008 #define PING() fprintf (stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); fflush (stderr) 00009 #endif 00010 00011 #include <stdio.h> 00012 00013 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 00014 /* dbus-spawn-win32.c Wrapper around g_spawn 00015 * 00016 * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. 00017 * Copyright (C) 2003 CodeFactory AB 00018 * Copyright (C) 2005 Novell, Inc. 00019 * 00020 * Licensed under the Academic Free License version 2.1 00021 * 00022 * This program is free software; you can redistribute it and/or modify 00023 * it under the terms of the GNU General Public License as published by 00024 * the Free Software Foundation; either version 2 of the License, or 00025 * (at your option) any later version. 00026 * 00027 * This program is distributed in the hope that it will be useful, 00028 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00029 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00030 * GNU General Public License for more details. 00031 * 00032 * You should have received a copy of the GNU General Public License 00033 * along with this program; if not, write to the Free Software 00034 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00035 * 00036 */ 00037 #include "dbus-spawn.h" 00038 #include "dbus-sysdeps.h" 00039 #include "dbus-sysdeps-win.h" 00040 #include "dbus-internals.h" 00041 #include "dbus-test.h" 00042 #include "dbus-protocol.h" 00043 00044 #define WIN32_LEAN_AND_MEAN 00045 #include <windows.h> 00046 //#define STRICT 00047 //#include <windows.h> 00048 //#undef STRICT 00049 #include <winsock2.h> 00050 #undef interface 00051 00052 #include <stdlib.h> 00053 00054 #ifndef DBUS_WINCE 00055 #include <process.h> 00056 #endif 00057 00061 struct DBusBabysitter 00062 { 00063 DBusAtomic refcount; 00064 00065 HANDLE start_sync_event; 00066 #ifdef DBUS_ENABLE_EMBEDDED_TESTS 00067 00068 HANDLE end_sync_event; 00069 #endif 00070 00071 char *log_name; 00072 DBusSpawnChildSetupFunc child_setup; 00073 void *user_data; 00074 00075 int argc; 00076 char **argv; 00077 char **envp; 00078 00079 HANDLE child_handle; 00080 DBusSocket socket_to_babysitter; /* Connection to the babysitter thread */ 00081 DBusSocket socket_to_main; 00082 00083 DBusWatchList *watches; 00084 DBusWatch *sitter_watch; 00085 DBusBabysitterFinishedFunc finished_cb; 00086 void *finished_data; 00087 00088 dbus_bool_t have_spawn_errno; 00089 int spawn_errno; 00090 dbus_bool_t have_child_status; 00091 int child_status; 00092 }; 00093 00094 static void 00095 _dbus_babysitter_trace_ref (DBusBabysitter *sitter, 00096 int old_refcount, 00097 int new_refcount, 00098 const char *why) 00099 { 00100 #ifdef DBUS_ENABLE_VERBOSE_MODE 00101 static int enabled = -1; 00102 00103 _dbus_trace_ref ("DBusBabysitter", sitter, old_refcount, new_refcount, why, 00104 "DBUS_BABYSITTER_TRACE", &enabled); 00105 #endif 00106 } 00107 00108 static DBusBabysitter* 00109 _dbus_babysitter_new (void) 00110 { 00111 DBusBabysitter *sitter; 00112 dbus_int32_t old_refcount; 00113 00114 sitter = dbus_new0 (DBusBabysitter, 1); 00115 if (sitter == NULL) 00116 return NULL; 00117 00118 old_refcount = _dbus_atomic_inc (&sitter->refcount); 00119 00120 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount+1, __FUNCTION__); 00121 00122 sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL); 00123 if (sitter->start_sync_event == NULL) 00124 { 00125 _dbus_babysitter_unref (sitter); 00126 return NULL; 00127 } 00128 00129 #ifdef DBUS_ENABLE_EMBEDDED_TESTS 00130 sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL); 00131 if (sitter->end_sync_event == NULL) 00132 { 00133 _dbus_babysitter_unref (sitter); 00134 return NULL; 00135 } 00136 #endif 00137 00138 sitter->child_handle = NULL; 00139 00140 sitter->socket_to_babysitter = sitter->socket_to_main = _dbus_socket_get_invalid (); 00141 00142 sitter->argc = 0; 00143 sitter->argv = NULL; 00144 sitter->envp = NULL; 00145 00146 sitter->watches = _dbus_watch_list_new (); 00147 if (sitter->watches == NULL) 00148 { 00149 _dbus_babysitter_unref (sitter); 00150 return NULL; 00151 } 00152 00153 sitter->have_spawn_errno = FALSE; 00154 sitter->have_child_status = FALSE; 00155 00156 return sitter; 00157 } 00158 00165 DBusBabysitter * 00166 _dbus_babysitter_ref (DBusBabysitter *sitter) 00167 { 00168 dbus_int32_t old_refcount; 00169 PING(); 00170 _dbus_assert (sitter != NULL); 00171 00172 old_refcount = _dbus_atomic_inc (&sitter->refcount); 00173 _dbus_assert (old_refcount > 0); 00174 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount+1, __FUNCTION__); 00175 00176 return sitter; 00177 } 00178 00179 static void 00180 close_socket_to_babysitter (DBusBabysitter *sitter) 00181 { 00182 _dbus_verbose ("Closing babysitter\n"); 00183 00184 if (sitter->sitter_watch != NULL) 00185 { 00186 _dbus_assert (sitter->watches != NULL); 00187 _dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch); 00188 _dbus_watch_invalidate (sitter->sitter_watch); 00189 _dbus_watch_unref (sitter->sitter_watch); 00190 sitter->sitter_watch = NULL; 00191 } 00192 00193 if (sitter->socket_to_babysitter.sock != INVALID_SOCKET) 00194 { 00195 _dbus_close_socket (sitter->socket_to_babysitter, NULL); 00196 sitter->socket_to_babysitter.sock = INVALID_SOCKET; 00197 } 00198 } 00199 00205 void 00206 _dbus_babysitter_unref (DBusBabysitter *sitter) 00207 { 00208 int i; 00209 dbus_int32_t old_refcount; 00210 00211 PING(); 00212 _dbus_assert (sitter != NULL); 00213 00214 old_refcount = _dbus_atomic_dec (&sitter->refcount); 00215 _dbus_assert (old_refcount > 0); 00216 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount-1, __FUNCTION__); 00217 00218 if (old_refcount == 1) 00219 { 00220 close_socket_to_babysitter (sitter); 00221 00222 if (sitter->socket_to_main.sock != INVALID_SOCKET) 00223 { 00224 _dbus_close_socket (sitter->socket_to_main, NULL); 00225 sitter->socket_to_main.sock = INVALID_SOCKET; 00226 } 00227 00228 PING(); 00229 if (sitter->argv != NULL) 00230 { 00231 for (i = 0; i < sitter->argc; i++) 00232 if (sitter->argv[i] != NULL) 00233 { 00234 dbus_free (sitter->argv[i]); 00235 sitter->argv[i] = NULL; 00236 } 00237 dbus_free (sitter->argv); 00238 sitter->argv = NULL; 00239 } 00240 00241 if (sitter->envp != NULL) 00242 { 00243 char **e = sitter->envp; 00244 00245 while (*e) 00246 dbus_free (*e++); 00247 dbus_free (sitter->envp); 00248 sitter->envp = NULL; 00249 } 00250 00251 if (sitter->child_handle != NULL) 00252 { 00253 CloseHandle (sitter->child_handle); 00254 sitter->child_handle = NULL; 00255 } 00256 00257 if (sitter->sitter_watch) 00258 { 00259 _dbus_watch_invalidate (sitter->sitter_watch); 00260 _dbus_watch_unref (sitter->sitter_watch); 00261 sitter->sitter_watch = NULL; 00262 } 00263 00264 if (sitter->watches) 00265 _dbus_watch_list_free (sitter->watches); 00266 00267 if (sitter->start_sync_event != NULL) 00268 { 00269 PING(); 00270 CloseHandle (sitter->start_sync_event); 00271 sitter->start_sync_event = NULL; 00272 } 00273 00274 #ifdef DBUS_ENABLE_EMBEDDED_TESTS 00275 if (sitter->end_sync_event != NULL) 00276 { 00277 CloseHandle (sitter->end_sync_event); 00278 sitter->end_sync_event = NULL; 00279 } 00280 #endif 00281 00282 dbus_free (sitter->log_name); 00283 00284 dbus_free (sitter); 00285 } 00286 } 00287 00288 void 00289 _dbus_babysitter_kill_child (DBusBabysitter *sitter) 00290 { 00291 PING(); 00292 if (sitter->child_handle == NULL) 00293 return; /* child is already dead, or we're so hosed we'll never recover */ 00294 00295 PING(); 00296 TerminateProcess (sitter->child_handle, 12345); 00297 } 00298 00304 dbus_bool_t 00305 _dbus_babysitter_get_child_exited (DBusBabysitter *sitter) 00306 { 00307 PING(); 00308 return (sitter->child_handle == NULL); 00309 } 00310 00323 dbus_bool_t 00324 _dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter, 00325 int *status) 00326 { 00327 if (!_dbus_babysitter_get_child_exited (sitter)) 00328 _dbus_assert_not_reached ("Child has not exited"); 00329 00330 if (!sitter->have_child_status || 00331 sitter->child_status == STILL_ACTIVE) 00332 return FALSE; 00333 00334 *status = sitter->child_status; 00335 return TRUE; 00336 } 00337 00347 void 00348 _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter, 00349 DBusError *error) 00350 { 00351 PING(); 00352 if (!_dbus_babysitter_get_child_exited (sitter)) 00353 return; 00354 00355 PING(); 00356 if (sitter->have_spawn_errno) 00357 { 00358 char *emsg = _dbus_win_error_string (sitter->spawn_errno); 00359 dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, 00360 "Failed to execute program %s: %s", 00361 sitter->log_name, emsg); 00362 _dbus_win_free_error_string (emsg); 00363 } 00364 else if (sitter->have_child_status) 00365 { 00366 PING(); 00367 dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED, 00368 "Process %s exited with status %d", 00369 sitter->log_name, sitter->child_status); 00370 } 00371 else 00372 { 00373 PING(); 00374 dbus_set_error (error, DBUS_ERROR_FAILED, 00375 "Process %s exited, status unknown", 00376 sitter->log_name); 00377 } 00378 PING(); 00379 } 00380 00381 dbus_bool_t 00382 _dbus_babysitter_set_watch_functions (DBusBabysitter *sitter, 00383 DBusAddWatchFunction add_function, 00384 DBusRemoveWatchFunction remove_function, 00385 DBusWatchToggledFunction toggled_function, 00386 void *data, 00387 DBusFreeFunction free_data_function) 00388 { 00389 PING(); 00390 return _dbus_watch_list_set_functions (sitter->watches, 00391 add_function, 00392 remove_function, 00393 toggled_function, 00394 data, 00395 free_data_function); 00396 } 00397 00398 static dbus_bool_t 00399 handle_watch (DBusWatch *watch, 00400 unsigned int condition, 00401 void *data) 00402 { 00403 DBusBabysitter *sitter = data; 00404 00405 /* On Unix dbus-spawn uses a babysitter *process*, thus it has to 00406 * actually send the exit statuses, error codes and whatnot through 00407 * sockets and/or pipes. On Win32, the babysitter is jus a thread, 00408 * so it can set the status fields directly in the babysitter struct 00409 * just fine. The socket pipe is used just so we can watch it with 00410 * select(), as soon as anything is written to it we know that the 00411 * babysitter thread has recorded the status in the babysitter 00412 * struct. 00413 */ 00414 00415 PING(); 00416 close_socket_to_babysitter (sitter); 00417 PING(); 00418 00419 if (_dbus_babysitter_get_child_exited (sitter) && 00420 sitter->finished_cb != NULL) 00421 { 00422 sitter->finished_cb (sitter, sitter->finished_data); 00423 sitter->finished_cb = NULL; 00424 } 00425 00426 return TRUE; 00427 } 00428 00429 /* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */ 00430 static int 00431 protect_argv (char **argv, 00432 char ***new_argv) 00433 { 00434 int i; 00435 int argc = 0; 00436 00437 while (argv[argc]) 00438 ++argc; 00439 *new_argv = dbus_malloc ((argc + 1) * sizeof (char *)); 00440 if (*new_argv == NULL) 00441 return -1; 00442 00443 for (i = 0; i < argc; i++) 00444 (*new_argv)[i] = NULL; 00445 00446 /* Quote each argv element if necessary, so that it will get 00447 * reconstructed correctly in the C runtime startup code. Note that 00448 * the unquoting algorithm in the C runtime is really weird, and 00449 * rather different than what Unix shells do. See stdargv.c in the C 00450 * runtime sources (in the Platform SDK, in src/crt). 00451 * 00452 * Note that an new_argv[0] constructed by this function should 00453 * *not* be passed as the filename argument to a spawn* or exec* 00454 * family function. That argument should be the real file name 00455 * without any quoting. 00456 */ 00457 for (i = 0; i < argc; i++) 00458 { 00459 char *p = argv[i]; 00460 char *q; 00461 int len = 0; 00462 int need_dblquotes = FALSE; 00463 while (*p) 00464 { 00465 if (*p == ' ' || *p == '\t') 00466 need_dblquotes = TRUE; 00467 else if (*p == '"') 00468 len++; 00469 else if (*p == '\\') 00470 { 00471 char *pp = p; 00472 while (*pp && *pp == '\\') 00473 pp++; 00474 if (*pp == '"') 00475 len++; 00476 } 00477 len++; 00478 p++; 00479 } 00480 00481 q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1); 00482 00483 if (q == NULL) 00484 return -1; 00485 00486 00487 p = argv[i]; 00488 00489 if (need_dblquotes) 00490 *q++ = '"'; 00491 00492 while (*p) 00493 { 00494 if (*p == '"') 00495 *q++ = '\\'; 00496 else if (*p == '\\') 00497 { 00498 char *pp = p; 00499 while (*pp && *pp == '\\') 00500 pp++; 00501 if (*pp == '"') 00502 *q++ = '\\'; 00503 } 00504 *q++ = *p; 00505 p++; 00506 } 00507 00508 if (need_dblquotes) 00509 *q++ = '"'; 00510 *q++ = '\0'; 00511 /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */ 00512 } 00513 (*new_argv)[argc] = NULL; 00514 00515 return argc; 00516 } 00517 00518 00519 /* From GPGME, relicensed by g10 Code GmbH. */ 00520 static char * 00521 compose_string (char **strings, char separator) 00522 { 00523 int i; 00524 int n = 0; 00525 char *buf; 00526 char *p; 00527 00528 if (!strings || !strings[0]) 00529 return 0; 00530 for (i = 0; strings[i]; i++) 00531 n += strlen (strings[i]) + 1; 00532 n++; 00533 00534 buf = p = malloc (n); 00535 if (!buf) 00536 return NULL; 00537 for (i = 0; strings[i]; i++) 00538 { 00539 strcpy (p, strings[i]); 00540 p += strlen (strings[i]); 00541 *(p++) = separator; 00542 } 00543 p--; 00544 *(p++) = '\0'; 00545 *p = '\0'; 00546 00547 return buf; 00548 } 00549 00550 static char * 00551 build_commandline (char **argv) 00552 { 00553 return compose_string (argv, ' '); 00554 } 00555 00556 static char * 00557 build_env_string (char** envp) 00558 { 00559 return compose_string (envp, '\0'); 00560 } 00561 00562 static HANDLE 00563 spawn_program (char* name, char** argv, char** envp) 00564 { 00565 PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; 00566 STARTUPINFOA si; 00567 char *arg_string, *env_string; 00568 BOOL result; 00569 00570 #ifdef DBUS_WINCE 00571 if (argv && argv[0]) 00572 arg_string = build_commandline (argv + 1); 00573 else 00574 arg_string = NULL; 00575 #else 00576 arg_string = build_commandline (argv); 00577 #endif 00578 if (!arg_string) 00579 return INVALID_HANDLE_VALUE; 00580 00581 env_string = build_env_string(envp); 00582 00583 memset (&si, 0, sizeof (si)); 00584 si.cb = sizeof (si); 00585 #ifdef DBUS_WINCE 00586 result = CreateProcessA (name, arg_string, NULL, NULL, FALSE, 0, 00587 #else 00588 result = CreateProcessA (NULL, arg_string, NULL, NULL, FALSE, 0, 00589 #endif 00590 (LPVOID)env_string, NULL, &si, &pi); 00591 free (arg_string); 00592 if (env_string) 00593 free (env_string); 00594 00595 if (!result) 00596 return INVALID_HANDLE_VALUE; 00597 00598 CloseHandle (pi.hThread); 00599 return pi.hProcess; 00600 } 00601 00602 00603 static DWORD __stdcall 00604 babysitter (void *parameter) 00605 { 00606 int ret = 0; 00607 DBusBabysitter *sitter = (DBusBabysitter *) parameter; 00608 HANDLE handle; 00609 00610 PING(); 00611 if (sitter->child_setup) 00612 { 00613 PING(); 00614 (*sitter->child_setup) (sitter->user_data); 00615 } 00616 00617 _dbus_verbose ("babysitter: spawning %s\n", sitter->log_name); 00618 00619 PING(); 00620 handle = spawn_program (sitter->log_name, sitter->argv, sitter->envp); 00621 00622 PING(); 00623 if (handle != INVALID_HANDLE_VALUE) 00624 { 00625 sitter->child_handle = handle; 00626 } 00627 else 00628 { 00629 sitter->child_handle = NULL; 00630 sitter->have_spawn_errno = TRUE; 00631 sitter->spawn_errno = GetLastError(); 00632 } 00633 00634 PING(); 00635 SetEvent (sitter->start_sync_event); 00636 00637 if (sitter->child_handle != NULL) 00638 { 00639 DWORD status; 00640 00641 PING(); 00642 // wait until process finished 00643 WaitForSingleObject (sitter->child_handle, INFINITE); 00644 00645 PING(); 00646 ret = GetExitCodeProcess (sitter->child_handle, &status); 00647 if (ret) 00648 { 00649 sitter->child_status = status; 00650 sitter->have_child_status = TRUE; 00651 } 00652 00653 CloseHandle (sitter->child_handle); 00654 sitter->child_handle = NULL; 00655 } 00656 00657 #ifdef DBUS_ENABLE_EMBEDDED_TESTS 00658 SetEvent (sitter->end_sync_event); 00659 #endif 00660 00661 PING(); 00662 send (sitter->socket_to_main.sock, " ", 1, 0); 00663 00664 _dbus_babysitter_unref (sitter); 00665 00666 return ret ? 0 : 1; 00667 } 00668 00669 dbus_bool_t 00670 _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, 00671 const char *log_name, 00672 char **argv, 00673 char **envp, 00674 DBusSpawnChildSetupFunc child_setup, 00675 void *user_data, 00676 DBusError *error) 00677 { 00678 DBusBabysitter *sitter; 00679 HANDLE sitter_thread; 00680 DWORD sitter_thread_id; 00681 00682 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00683 _dbus_assert (argv[0] != NULL); 00684 00685 if (sitter_p != NULL) 00686 *sitter_p = NULL; 00687 00688 PING(); 00689 sitter = _dbus_babysitter_new (); 00690 if (sitter == NULL) 00691 { 00692 _DBUS_SET_OOM (error); 00693 return FALSE; 00694 } 00695 00696 sitter->child_setup = child_setup; 00697 sitter->user_data = user_data; 00698 00699 sitter->log_name = _dbus_strdup (log_name); 00700 if (sitter->log_name == NULL && log_name != NULL) 00701 { 00702 _DBUS_SET_OOM (error); 00703 goto out0; 00704 } 00705 00706 if (sitter->log_name == NULL) 00707 sitter->log_name = _dbus_strdup (argv[0]); 00708 00709 if (sitter->log_name == NULL) 00710 { 00711 _DBUS_SET_OOM (error); 00712 goto out0; 00713 } 00714 00715 PING(); 00716 if (!_dbus_socketpair (&sitter->socket_to_babysitter, 00717 &sitter->socket_to_main, 00718 FALSE, error)) 00719 goto out0; 00720 00721 sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter, 00722 DBUS_WATCH_READABLE, 00723 TRUE, handle_watch, sitter, NULL); 00724 PING(); 00725 if (sitter->sitter_watch == NULL) 00726 { 00727 _DBUS_SET_OOM (error); 00728 goto out0; 00729 } 00730 00731 PING(); 00732 if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch)) 00733 { 00734 /* we need to free it early so the destructor won't try to remove it 00735 * without it having been added, which DBusLoop doesn't allow */ 00736 _dbus_watch_invalidate (sitter->sitter_watch); 00737 _dbus_watch_unref (sitter->sitter_watch); 00738 sitter->sitter_watch = NULL; 00739 00740 _DBUS_SET_OOM (error); 00741 goto out0; 00742 } 00743 00744 sitter->argc = protect_argv (argv, &sitter->argv); 00745 if (sitter->argc == -1) 00746 { 00747 _DBUS_SET_OOM (error); 00748 goto out0; 00749 } 00750 sitter->envp = envp; 00751 00752 PING(); 00753 sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter, 00754 _dbus_babysitter_ref (sitter), 0, &sitter_thread_id); 00755 00756 if (sitter_thread == 0) 00757 { 00758 PING(); 00759 dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED, 00760 "Failed to create new thread"); 00761 goto out0; 00762 } 00763 CloseHandle (sitter_thread); 00764 00765 PING(); 00766 WaitForSingleObject (sitter->start_sync_event, INFINITE); 00767 00768 PING(); 00769 if (sitter_p != NULL) 00770 *sitter_p = sitter; 00771 else 00772 _dbus_babysitter_unref (sitter); 00773 00774 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00775 00776 PING(); 00777 return TRUE; 00778 00779 out0: 00780 _dbus_babysitter_unref (sitter); 00781 00782 return FALSE; 00783 } 00784 00785 void 00786 _dbus_babysitter_set_result_function (DBusBabysitter *sitter, 00787 DBusBabysitterFinishedFunc finished, 00788 void *user_data) 00789 { 00790 sitter->finished_cb = finished; 00791 sitter->finished_data = user_data; 00792 } 00793 00794 #ifdef DBUS_ENABLE_EMBEDDED_TESTS 00795 00796 static char * 00797 get_test_exec (const char *exe, 00798 DBusString *scratch_space) 00799 { 00800 const char *dbus_test_exec; 00801 00802 dbus_test_exec = _dbus_getenv ("DBUS_TEST_EXEC"); 00803 00804 if (dbus_test_exec == NULL) 00805 dbus_test_exec = DBUS_TEST_EXEC; 00806 00807 if (!_dbus_string_init (scratch_space)) 00808 return NULL; 00809 00810 if (!_dbus_string_append_printf (scratch_space, "%s/%s%s", 00811 dbus_test_exec, exe, DBUS_EXEEXT)) 00812 { 00813 _dbus_string_free (scratch_space); 00814 return NULL; 00815 } 00816 00817 return _dbus_string_get_data (scratch_space); 00818 } 00819 00820 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL) 00821 00822 static void 00823 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter) 00824 { 00825 if (sitter->child_handle == NULL) 00826 return; 00827 00828 WaitForSingleObject (sitter->end_sync_event, INFINITE); 00829 } 00830 00831 static dbus_bool_t 00832 check_spawn_nonexistent (void *data) 00833 { 00834 char *argv[4] = { NULL, NULL, NULL, NULL }; 00835 DBusBabysitter *sitter; 00836 DBusError error; 00837 00838 sitter = NULL; 00839 00840 dbus_error_init (&error); 00841 00842 /*** Test launching nonexistent binary */ 00843 00844 argv[0] = "/this/does/not/exist/32542sdgafgafdg"; 00845 if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_nonexistent", argv, NULL, 00846 NULL, NULL, 00847 &error)) 00848 { 00849 _dbus_babysitter_block_for_child_exit (sitter); 00850 _dbus_babysitter_set_child_exit_error (sitter, &error); 00851 } 00852 00853 if (sitter) 00854 _dbus_babysitter_unref (sitter); 00855 00856 if (!dbus_error_is_set (&error)) 00857 { 00858 _dbus_warn ("Did not get an error launching nonexistent executable\n"); 00859 return FALSE; 00860 } 00861 00862 if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || 00863 dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED))) 00864 { 00865 _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n", 00866 error.name, error.message); 00867 dbus_error_free (&error); 00868 return FALSE; 00869 } 00870 00871 dbus_error_free (&error); 00872 00873 return TRUE; 00874 } 00875 00876 static dbus_bool_t 00877 check_spawn_segfault (void *data) 00878 { 00879 char *argv[4] = { NULL, NULL, NULL, NULL }; 00880 DBusBabysitter *sitter; 00881 DBusError error; 00882 DBusString argv0; 00883 00884 sitter = NULL; 00885 00886 dbus_error_init (&error); 00887 00888 /*** Test launching segfault binary */ 00889 00890 argv[0] = get_test_exec ("test-segfault", &argv0); 00891 00892 if (argv[0] == NULL) 00893 { 00894 /* OOM was simulated, never mind */ 00895 return TRUE; 00896 } 00897 00898 if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_segfault", argv, NULL, 00899 NULL, NULL, 00900 &error)) 00901 { 00902 _dbus_babysitter_block_for_child_exit (sitter); 00903 _dbus_babysitter_set_child_exit_error (sitter, &error); 00904 } 00905 00906 _dbus_string_free (&argv0); 00907 00908 if (sitter) 00909 _dbus_babysitter_unref (sitter); 00910 00911 if (!dbus_error_is_set (&error)) 00912 { 00913 _dbus_warn ("Did not get an error launching segfaulting binary\n"); 00914 return FALSE; 00915 } 00916 00917 if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || 00918 dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED))) 00919 { 00920 _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n", 00921 error.name, error.message); 00922 dbus_error_free (&error); 00923 return FALSE; 00924 } 00925 00926 dbus_error_free (&error); 00927 00928 return TRUE; 00929 } 00930 00931 static dbus_bool_t 00932 check_spawn_exit (void *data) 00933 { 00934 char *argv[4] = { NULL, NULL, NULL, NULL }; 00935 DBusBabysitter *sitter; 00936 DBusError error; 00937 DBusString argv0; 00938 00939 sitter = NULL; 00940 00941 dbus_error_init (&error); 00942 00943 /*** Test launching exit failure binary */ 00944 00945 argv[0] = get_test_exec ("test-exit", &argv0); 00946 00947 if (argv[0] == NULL) 00948 { 00949 /* OOM was simulated, never mind */ 00950 return TRUE; 00951 } 00952 00953 if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_exit", argv, NULL, 00954 NULL, NULL, 00955 &error)) 00956 { 00957 _dbus_babysitter_block_for_child_exit (sitter); 00958 _dbus_babysitter_set_child_exit_error (sitter, &error); 00959 } 00960 00961 _dbus_string_free (&argv0); 00962 00963 if (sitter) 00964 _dbus_babysitter_unref (sitter); 00965 00966 if (!dbus_error_is_set (&error)) 00967 { 00968 _dbus_warn ("Did not get an error launching binary that exited with failure code\n"); 00969 return FALSE; 00970 } 00971 00972 if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || 00973 dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED))) 00974 { 00975 _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n", 00976 error.name, error.message); 00977 dbus_error_free (&error); 00978 return FALSE; 00979 } 00980 00981 dbus_error_free (&error); 00982 00983 return TRUE; 00984 } 00985 00986 static dbus_bool_t 00987 check_spawn_and_kill (void *data) 00988 { 00989 char *argv[4] = { NULL, NULL, NULL, NULL }; 00990 DBusBabysitter *sitter; 00991 DBusError error; 00992 DBusString argv0; 00993 00994 sitter = NULL; 00995 00996 dbus_error_init (&error); 00997 00998 /*** Test launching sleeping binary then killing it */ 00999 01000 argv[0] = get_test_exec ("test-sleep-forever", &argv0); 01001 01002 if (argv[0] == NULL) 01003 { 01004 /* OOM was simulated, never mind */ 01005 return TRUE; 01006 } 01007 01008 if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_and_kill", argv, NULL, 01009 NULL, NULL, 01010 &error)) 01011 { 01012 _dbus_babysitter_kill_child (sitter); 01013 01014 _dbus_babysitter_block_for_child_exit (sitter); 01015 01016 _dbus_babysitter_set_child_exit_error (sitter, &error); 01017 } 01018 01019 _dbus_string_free (&argv0); 01020 01021 if (sitter) 01022 _dbus_babysitter_unref (sitter); 01023 01024 if (!dbus_error_is_set (&error)) 01025 { 01026 _dbus_warn ("Did not get an error after killing spawned binary\n"); 01027 return FALSE; 01028 } 01029 01030 if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || 01031 dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED))) 01032 { 01033 _dbus_warn ("Not expecting error when killing executable: %s: %s\n", 01034 error.name, error.message); 01035 dbus_error_free (&error); 01036 return FALSE; 01037 } 01038 01039 dbus_error_free (&error); 01040 01041 return TRUE; 01042 } 01043 01044 dbus_bool_t 01045 _dbus_spawn_test (const char *test_data_dir) 01046 { 01047 if (!_dbus_test_oom_handling ("spawn_nonexistent", 01048 check_spawn_nonexistent, 01049 NULL)) 01050 return FALSE; 01051 01052 /* Don't run the obnoxious segfault test by default, 01053 * it's a pain to have to click all those error boxes. 01054 */ 01055 if (getenv ("DO_SEGFAULT_TEST")) 01056 if (!_dbus_test_oom_handling ("spawn_segfault", 01057 check_spawn_segfault, 01058 NULL)) 01059 return FALSE; 01060 01061 if (!_dbus_test_oom_handling ("spawn_exit", 01062 check_spawn_exit, 01063 NULL)) 01064 return FALSE; 01065 01066 if (!_dbus_test_oom_handling ("spawn_and_kill", 01067 check_spawn_and_kill, 01068 NULL)) 01069 return FALSE; 01070 01071 return TRUE; 01072 } 01073 #endif