mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-05-05 14:38:10 +02:00
* dbus/*-win.*,bus/*-win.*: added win32 platform related
files. These files are only added to the cmake build system. The missing dbus-win32.patch file will be added later.
This commit is contained in:
parent
36ebfd411b
commit
70bfc74e54
6 changed files with 7089 additions and 0 deletions
|
|
@ -1,3 +1,9 @@
|
|||
2007-03-03 Ralf Habacker <ralf.habacker@freenet.de>
|
||||
|
||||
* dbus/*-win.*,bus/*-win.*: added win32 platform related
|
||||
files. These files are only added to the cmake build system.
|
||||
The missing dbus-win32.patch file will be added later.
|
||||
|
||||
2007-03-03 Ralf Habacker <ralf.habacker@freenet.de>
|
||||
|
||||
* cmake: new directory, contains cmake build support.
|
||||
|
|
|
|||
78
dbus/dbus-sockets-win.h
Normal file
78
dbus/dbus-sockets-win.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* -*- mode: C; c-file-style: "gnu" -*- */
|
||||
/* dbus-sockets.h Wrappers around socket features (internal to D-BUS implementation)
|
||||
*
|
||||
* Copyright (C) 2005 Novell, Inc.
|
||||
*
|
||||
* Licensed under the Academic Free License version 2.1
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DBUS_SOCKETS_H
|
||||
#define DBUS_SOCKETS_H
|
||||
|
||||
#if defined(DBUS_WIN) || defined(DBUS_WINCE)
|
||||
|
||||
|
||||
|
||||
#ifndef STRICT
|
||||
#define STRICT
|
||||
#include <winsock2.h>
|
||||
#undef STRICT
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
|
||||
#undef interface
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/* Make use of the fact that the WSAE* error codes don't
|
||||
* overlap with errno E* codes. Wrapper functions store
|
||||
* the return value from WSAGetLastError() in errno.
|
||||
*/
|
||||
#if defined(EPROTONOSUPPORT) || \
|
||||
defined(EAFNOSUPPORT) || \
|
||||
defined(EWOULDBLOCK)
|
||||
#error This does not look like Win32 and the Microsoft C library
|
||||
#endif
|
||||
|
||||
#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
|
||||
#define EAFNOSUPPORT WSAEAFNOSUPPORT
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
|
||||
#define DBUS_SOCKET_IS_INVALID(s) ((SOCKET)(s) == INVALID_SOCKET)
|
||||
#define DBUS_SOCKET_API_RETURNS_ERROR(n) ((n) == SOCKET_ERROR)
|
||||
#define DBUS_SOCKET_SET_ERRNO() errno = WSAGetLastError()
|
||||
|
||||
#define DBUS_CLOSE_SOCKET(s) closesocket(s)
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define DBUS_SOCKET_IS_INVALID(s) ((s) < 0)
|
||||
#define DBUS_SOCKET_API_RETURNS_ERROR(n) ((n) < 0)
|
||||
#define DBUS_SOCKET_SET_ERRNO() /* empty */
|
||||
|
||||
#define DBUS_CLOSE_SOCKET(s) close(s)
|
||||
|
||||
#endif /* !Win32 */
|
||||
|
||||
#endif /* DBUS_SOCKETS_H */
|
||||
832
dbus/dbus-spawn-win.c
Normal file
832
dbus/dbus-spawn-win.c
Normal file
|
|
@ -0,0 +1,832 @@
|
|||
#include "config.h"
|
||||
|
||||
#if !defined(DBUS_ENABLE_VERBOSE_MODE) || defined(_MSC_VER)
|
||||
#define PING()
|
||||
#else
|
||||
#define PING() fprintf (stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); fflush (stderr)
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef DBUS_WINCE
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
/* -*- mode: C; c-file-style: "gnu" -*- */
|
||||
/* dbus-spawn-win32.c Wrapper around g_spawn
|
||||
*
|
||||
* Copyright (C) 2002, 2003, 2004 Red Hat, Inc.
|
||||
* Copyright (C) 2003 CodeFactory AB
|
||||
* Copyright (C) 2005 Novell, Inc.
|
||||
*
|
||||
* Licensed under the Academic Free License version 2.1
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#include "dbus-spawn.h"
|
||||
#include "dbus-sysdeps.h"
|
||||
#include "dbus-sysdeps-win.h"
|
||||
#include "dbus-internals.h"
|
||||
#include "dbus-test.h"
|
||||
#include "dbus-protocol.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
//#define STRICT
|
||||
//#include <windows.h>
|
||||
//#undef STRICT
|
||||
#include <winsock2.h>
|
||||
#undef interface
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <process.h>
|
||||
|
||||
/**
|
||||
* Babysitter implementation details
|
||||
*/
|
||||
struct DBusBabysitter
|
||||
{
|
||||
int refcount;
|
||||
|
||||
HANDLE start_sync_event;
|
||||
#ifdef DBUS_BUILD_TESTS
|
||||
|
||||
HANDLE end_sync_event;
|
||||
#endif
|
||||
|
||||
char *executable;
|
||||
DBusSpawnChildSetupFunc child_setup;
|
||||
void *user_data;
|
||||
|
||||
int argc;
|
||||
char **argv;
|
||||
char **envp;
|
||||
|
||||
HANDLE child_handle;
|
||||
int socket_to_babysitter; /* Connection to the babysitter thread */
|
||||
int socket_to_main;
|
||||
|
||||
DBusWatchList *watches;
|
||||
DBusWatch *sitter_watch;
|
||||
|
||||
dbus_bool_t have_spawn_errno;
|
||||
int spawn_errno;
|
||||
dbus_bool_t have_child_status;
|
||||
int child_status;
|
||||
};
|
||||
|
||||
static DBusBabysitter*
|
||||
_dbus_babysitter_new (void)
|
||||
{
|
||||
DBusBabysitter *sitter;
|
||||
|
||||
sitter = dbus_new0 (DBusBabysitter, 1);
|
||||
if (sitter == NULL)
|
||||
return NULL;
|
||||
|
||||
sitter->refcount = 1;
|
||||
|
||||
sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||
if (sitter->start_sync_event == NULL)
|
||||
{
|
||||
_dbus_babysitter_unref (sitter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DBUS_BUILD_TESTS
|
||||
sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||
if (sitter->end_sync_event == NULL)
|
||||
{
|
||||
_dbus_babysitter_unref (sitter);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
sitter->child_handle = NULL;
|
||||
|
||||
sitter->socket_to_babysitter = sitter->socket_to_main = -1;
|
||||
|
||||
sitter->argc = 0;
|
||||
sitter->argv = NULL;
|
||||
sitter->envp = NULL;
|
||||
|
||||
sitter->watches = _dbus_watch_list_new ();
|
||||
if (sitter->watches == NULL)
|
||||
{
|
||||
_dbus_babysitter_unref (sitter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sitter->have_spawn_errno = FALSE;
|
||||
sitter->have_child_status = FALSE;
|
||||
|
||||
return sitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the reference count on the babysitter object.
|
||||
*
|
||||
* @param sitter the babysitter
|
||||
* @returns the babysitter
|
||||
*/
|
||||
DBusBabysitter *
|
||||
_dbus_babysitter_ref (DBusBabysitter *sitter)
|
||||
{
|
||||
PING();
|
||||
_dbus_assert (sitter != NULL);
|
||||
_dbus_assert (sitter->refcount > 0);
|
||||
|
||||
sitter->refcount += 1;
|
||||
|
||||
return sitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the reference count on the babysitter object.
|
||||
*
|
||||
* @param sitter the babysitter
|
||||
*/
|
||||
void
|
||||
_dbus_babysitter_unref (DBusBabysitter *sitter)
|
||||
{
|
||||
int i;
|
||||
|
||||
PING();
|
||||
_dbus_assert (sitter != NULL);
|
||||
_dbus_assert (sitter->refcount > 0);
|
||||
|
||||
sitter->refcount -= 1;
|
||||
|
||||
if (sitter->refcount == 0)
|
||||
{
|
||||
if (sitter->socket_to_babysitter != -1)
|
||||
{
|
||||
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
|
||||
sitter->socket_to_babysitter = -1;
|
||||
}
|
||||
|
||||
if (sitter->socket_to_main != -1)
|
||||
{
|
||||
_dbus_close_socket (sitter->socket_to_main, NULL);
|
||||
sitter->socket_to_main = -1;
|
||||
}
|
||||
|
||||
PING();
|
||||
if (sitter->argv != NULL)
|
||||
{
|
||||
for (i = 0; i < sitter->argc; i++)
|
||||
if (sitter->argv[i] != NULL)
|
||||
{
|
||||
dbus_free (sitter->argv[i]);
|
||||
sitter->argv[i] = NULL;
|
||||
}
|
||||
dbus_free (sitter->argv);
|
||||
sitter->argv = NULL;
|
||||
}
|
||||
|
||||
if (sitter->envp != NULL)
|
||||
{
|
||||
char **e = sitter->envp;
|
||||
|
||||
while (*e)
|
||||
dbus_free (*e++);
|
||||
dbus_free (sitter->envp);
|
||||
sitter->envp = NULL;
|
||||
}
|
||||
|
||||
if (sitter->child_handle != NULL)
|
||||
{
|
||||
CloseHandle (sitter->child_handle);
|
||||
sitter->child_handle = NULL;
|
||||
}
|
||||
|
||||
if (sitter->sitter_watch)
|
||||
{
|
||||
_dbus_watch_invalidate (sitter->sitter_watch);
|
||||
_dbus_watch_unref (sitter->sitter_watch);
|
||||
sitter->sitter_watch = NULL;
|
||||
}
|
||||
|
||||
if (sitter->watches)
|
||||
_dbus_watch_list_free (sitter->watches);
|
||||
|
||||
if (sitter->start_sync_event != NULL)
|
||||
{
|
||||
PING();
|
||||
CloseHandle (sitter->start_sync_event);
|
||||
sitter->end_sync_event = NULL;
|
||||
}
|
||||
|
||||
#ifdef DBUS_BUILD_TESTS
|
||||
if (sitter->end_sync_event != NULL)
|
||||
{
|
||||
CloseHandle (sitter->end_sync_event);
|
||||
sitter->end_sync_event = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
dbus_free (sitter->executable);
|
||||
|
||||
dbus_free (sitter);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_dbus_babysitter_kill_child (DBusBabysitter *sitter)
|
||||
{
|
||||
PING();
|
||||
if (sitter->child_handle == NULL)
|
||||
return; /* child is already dead, or we're so hosed we'll never recover */
|
||||
|
||||
PING();
|
||||
TerminateProcess (sitter->child_handle, 12345);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the child has exited, without blocking.
|
||||
*
|
||||
* @param sitter the babysitter
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
|
||||
{
|
||||
PING();
|
||||
return (sitter->child_handle == NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the #DBusError with an explanation of why the spawned
|
||||
* child process exited (on a signal, or whatever). If
|
||||
* the child process has not exited, does nothing (error
|
||||
* will remain unset).
|
||||
*
|
||||
* @param sitter the babysitter
|
||||
* @param error an error to fill in
|
||||
*/
|
||||
void
|
||||
_dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
|
||||
DBusError *error)
|
||||
{
|
||||
PING();
|
||||
if (!_dbus_babysitter_get_child_exited (sitter))
|
||||
return;
|
||||
|
||||
PING();
|
||||
if (sitter->have_spawn_errno)
|
||||
{
|
||||
dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
|
||||
"Failed to execute program %s: %s",
|
||||
sitter->executable, _dbus_strerror (sitter->spawn_errno));
|
||||
}
|
||||
else if (sitter->have_child_status)
|
||||
{
|
||||
PING();
|
||||
dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
|
||||
"Process %s exited with status %d",
|
||||
sitter->executable, sitter->child_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
PING();
|
||||
dbus_set_error (error, DBUS_ERROR_FAILED,
|
||||
"Process %s exited, status unknown",
|
||||
sitter->executable);
|
||||
}
|
||||
PING();
|
||||
}
|
||||
|
||||
dbus_bool_t
|
||||
_dbus_babysitter_set_watch_functions (DBusBabysitter *sitter,
|
||||
DBusAddWatchFunction add_function,
|
||||
DBusRemoveWatchFunction remove_function,
|
||||
DBusWatchToggledFunction toggled_function,
|
||||
void *data,
|
||||
DBusFreeFunction free_data_function)
|
||||
{
|
||||
PING();
|
||||
return _dbus_watch_list_set_functions (sitter->watches,
|
||||
add_function,
|
||||
remove_function,
|
||||
toggled_function,
|
||||
data,
|
||||
free_data_function);
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
handle_watch (DBusWatch *watch,
|
||||
unsigned int condition,
|
||||
void *data)
|
||||
{
|
||||
DBusBabysitter *sitter = data;
|
||||
|
||||
/* On Unix dbus-spawn uses a babysitter *process*, thus it has to
|
||||
* actually send the exit statuses, error codes and whatnot through
|
||||
* sockets and/or pipes. On Win32, the babysitter is jus a thread,
|
||||
* so it can set the status fields directly in the babysitter struct
|
||||
* just fine. The socket pipe is used just so we can watch it with
|
||||
* select(), as soon as anything is written to it we know that the
|
||||
* babysitter thread has recorded the status in the babysitter
|
||||
* struct.
|
||||
*/
|
||||
|
||||
PING();
|
||||
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
|
||||
PING();
|
||||
sitter->socket_to_babysitter = -1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
|
||||
static int
|
||||
protect_argv (char **argv,
|
||||
char ***new_argv)
|
||||
{
|
||||
int i;
|
||||
int argc = 0;
|
||||
|
||||
while (argv[argc])
|
||||
++argc;
|
||||
*new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
|
||||
if (*new_argv == NULL)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
(*new_argv)[i] = NULL;
|
||||
|
||||
/* Quote each argv 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
|
||||
* rather different than what Unix shells do. See stdargv.c in the C
|
||||
* runtime sources (in the Platform SDK, in src/crt).
|
||||
*
|
||||
* Note that an new_argv[0] constructed by this function should
|
||||
* *not* be passed as the filename argument to a spawn* or exec*
|
||||
* family function. That argument should be the real file name
|
||||
* without any quoting.
|
||||
*/
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
char *p = argv[i];
|
||||
char *q;
|
||||
int len = 0;
|
||||
int need_dblquotes = FALSE;
|
||||
while (*p)
|
||||
{
|
||||
if (*p == ' ' || *p == '\t')
|
||||
need_dblquotes = TRUE;
|
||||
else if (*p == '"')
|
||||
len++;
|
||||
else if (*p == '\\')
|
||||
{
|
||||
char *pp = p;
|
||||
while (*pp && *pp == '\\')
|
||||
pp++;
|
||||
if (*pp == '"')
|
||||
len++;
|
||||
}
|
||||
len++;
|
||||
p++;
|
||||
}
|
||||
|
||||
q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
|
||||
|
||||
if (q == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
p = argv[i];
|
||||
|
||||
if (need_dblquotes)
|
||||
*q++ = '"';
|
||||
|
||||
while (*p)
|
||||
{
|
||||
if (*p == '"')
|
||||
*q++ = '\\';
|
||||
else if (*p == '\\')
|
||||
{
|
||||
char *pp = p;
|
||||
while (*pp && *pp == '\\')
|
||||
pp++;
|
||||
if (*pp == '"')
|
||||
*q++ = '\\';
|
||||
}
|
||||
*q++ = *p;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (need_dblquotes)
|
||||
*q++ = '"';
|
||||
*q++ = '\0';
|
||||
/* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
|
||||
}
|
||||
(*new_argv)[argc] = NULL;
|
||||
|
||||
return argc;
|
||||
}
|
||||
|
||||
static unsigned __stdcall
|
||||
babysitter (void *parameter)
|
||||
{
|
||||
DBusBabysitter *sitter = (DBusBabysitter *) parameter;
|
||||
DBusSocket *sock;
|
||||
PING();
|
||||
_dbus_babysitter_ref (sitter);
|
||||
|
||||
if (sitter->child_setup)
|
||||
{
|
||||
PING();
|
||||
(*sitter->child_setup) (sitter->user_data);
|
||||
}
|
||||
|
||||
_dbus_verbose ("babysitter: spawning %s\n", sitter->executable);
|
||||
fprintf (stderr, "babysitter: spawning %s\n", sitter->executable);
|
||||
|
||||
PING();
|
||||
if (sitter->envp != NULL)
|
||||
sitter->child_handle = (HANDLE) spawnve (P_NOWAIT, sitter->executable,
|
||||
(const char * const *) sitter->argv,
|
||||
(const char * const *) sitter->envp);
|
||||
else
|
||||
sitter->child_handle = (HANDLE) spawnv (P_NOWAIT, sitter->executable,
|
||||
(const char * const *) sitter->argv);
|
||||
|
||||
PING();
|
||||
if (sitter->child_handle == (HANDLE) -1)
|
||||
{
|
||||
sitter->child_handle = NULL;
|
||||
sitter->have_spawn_errno = TRUE;
|
||||
sitter->spawn_errno = errno;
|
||||
}
|
||||
|
||||
PING();
|
||||
SetEvent (sitter->start_sync_event);
|
||||
|
||||
if (sitter->child_handle != NULL)
|
||||
{
|
||||
int ret;
|
||||
DWORD status;
|
||||
|
||||
PING();
|
||||
WaitForSingleObject (sitter->child_handle, INFINITE);
|
||||
|
||||
PING();
|
||||
ret = GetExitCodeProcess (sitter->child_handle, &status);
|
||||
|
||||
sitter->child_status = status;
|
||||
sitter->have_child_status = TRUE;
|
||||
|
||||
CloseHandle (sitter->child_handle);
|
||||
sitter->child_handle = NULL;
|
||||
}
|
||||
|
||||
#ifdef DBUS_BUILD_TESTS
|
||||
SetEvent (sitter->end_sync_event);
|
||||
#endif
|
||||
|
||||
PING();
|
||||
_dbus_handle_to_socket (sitter->socket_to_main, &sock);
|
||||
send (sock->fd, " ", 1, 0);
|
||||
|
||||
_dbus_babysitter_unref (sitter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbus_bool_t
|
||||
_dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p,
|
||||
char **argv,
|
||||
char **envp,
|
||||
DBusSpawnChildSetupFunc child_setup,
|
||||
void *user_data,
|
||||
DBusError *error)
|
||||
{
|
||||
DBusBabysitter *sitter;
|
||||
HANDLE sitter_thread;
|
||||
int sitter_thread_id;
|
||||
|
||||
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
|
||||
|
||||
*sitter_p = NULL;
|
||||
|
||||
PING();
|
||||
sitter = _dbus_babysitter_new ();
|
||||
if (sitter == NULL)
|
||||
{
|
||||
_DBUS_SET_OOM (error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sitter->child_setup = child_setup;
|
||||
sitter->user_data = user_data;
|
||||
|
||||
sitter->executable = _dbus_strdup (argv[0]);
|
||||
if (sitter->executable == NULL)
|
||||
{
|
||||
_DBUS_SET_OOM (error);
|
||||
goto out0;
|
||||
}
|
||||
|
||||
PING();
|
||||
if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter,
|
||||
&sitter->socket_to_main,
|
||||
FALSE, error))
|
||||
goto out0;
|
||||
|
||||
sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
|
||||
DBUS_WATCH_READABLE,
|
||||
TRUE, handle_watch, sitter, NULL);
|
||||
PING();
|
||||
if (sitter->sitter_watch == NULL)
|
||||
{
|
||||
_DBUS_SET_OOM (error);
|
||||
goto out0;
|
||||
}
|
||||
|
||||
PING();
|
||||
if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch))
|
||||
{
|
||||
_DBUS_SET_OOM (error);
|
||||
goto out0;
|
||||
}
|
||||
|
||||
sitter->argc = protect_argv (argv, &sitter->argv);
|
||||
if (sitter->argc == -1)
|
||||
{
|
||||
_DBUS_SET_OOM (error);
|
||||
goto out0;
|
||||
}
|
||||
sitter->envp = envp;
|
||||
|
||||
PING();
|
||||
sitter_thread = (HANDLE) _beginthreadex (NULL, 0, babysitter,
|
||||
sitter, 0, &sitter_thread_id);
|
||||
|
||||
if (sitter_thread == 0)
|
||||
{
|
||||
PING();
|
||||
dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
|
||||
"Failed to create new thread");
|
||||
goto out0;
|
||||
}
|
||||
CloseHandle (sitter_thread);
|
||||
|
||||
PING();
|
||||
WaitForSingleObject (sitter->start_sync_event, INFINITE);
|
||||
|
||||
PING();
|
||||
if (sitter_p != NULL)
|
||||
*sitter_p = sitter;
|
||||
else
|
||||
_dbus_babysitter_unref (sitter);
|
||||
|
||||
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
|
||||
|
||||
PING();
|
||||
return TRUE;
|
||||
|
||||
out0:
|
||||
_dbus_babysitter_unref (sitter);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef DBUS_BUILD_TESTS
|
||||
|
||||
#define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
|
||||
|
||||
static void
|
||||
_dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
|
||||
{
|
||||
if (sitter->child_handle == NULL)
|
||||
return;
|
||||
|
||||
WaitForSingleObject (sitter->end_sync_event, INFINITE);
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
check_spawn_nonexistent (void *data)
|
||||
{
|
||||
char *argv[4] = { NULL, NULL, NULL, NULL };
|
||||
DBusBabysitter *sitter;
|
||||
DBusError error;
|
||||
|
||||
sitter = NULL;
|
||||
|
||||
dbus_error_init (&error);
|
||||
|
||||
/*** Test launching nonexistent binary */
|
||||
|
||||
argv[0] = "/this/does/not/exist/32542sdgafgafdg";
|
||||
if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
|
||||
NULL, NULL,
|
||||
&error))
|
||||
{
|
||||
_dbus_babysitter_block_for_child_exit (sitter);
|
||||
_dbus_babysitter_set_child_exit_error (sitter, &error);
|
||||
}
|
||||
|
||||
if (sitter)
|
||||
_dbus_babysitter_unref (sitter);
|
||||
|
||||
if (!dbus_error_is_set (&error))
|
||||
{
|
||||
_dbus_warn ("Did not get an error launching nonexistent executable\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
|
||||
dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
|
||||
{
|
||||
_dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
|
||||
error.name, error.message);
|
||||
dbus_error_free (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dbus_error_free (&error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
check_spawn_segfault (void *data)
|
||||
{
|
||||
char *argv[4] = { NULL, NULL, NULL, NULL };
|
||||
DBusBabysitter *sitter;
|
||||
DBusError error;
|
||||
|
||||
sitter = NULL;
|
||||
|
||||
dbus_error_init (&error);
|
||||
|
||||
/*** Test launching segfault binary */
|
||||
|
||||
argv[0] = TEST_SEGFAULT_BINARY;
|
||||
if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
|
||||
NULL, NULL,
|
||||
&error))
|
||||
{
|
||||
_dbus_babysitter_block_for_child_exit (sitter);
|
||||
_dbus_babysitter_set_child_exit_error (sitter, &error);
|
||||
}
|
||||
|
||||
if (sitter)
|
||||
_dbus_babysitter_unref (sitter);
|
||||
|
||||
if (!dbus_error_is_set (&error))
|
||||
{
|
||||
_dbus_warn ("Did not get an error launching segfaulting binary\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
|
||||
dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
|
||||
{
|
||||
_dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
|
||||
error.name, error.message);
|
||||
dbus_error_free (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dbus_error_free (&error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
check_spawn_exit (void *data)
|
||||
{
|
||||
char *argv[4] = { NULL, NULL, NULL, NULL };
|
||||
DBusBabysitter *sitter;
|
||||
DBusError error;
|
||||
|
||||
sitter = NULL;
|
||||
|
||||
dbus_error_init (&error);
|
||||
|
||||
/*** Test launching exit failure binary */
|
||||
|
||||
argv[0] = TEST_EXIT_BINARY;
|
||||
if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
|
||||
NULL, NULL,
|
||||
&error))
|
||||
{
|
||||
_dbus_babysitter_block_for_child_exit (sitter);
|
||||
_dbus_babysitter_set_child_exit_error (sitter, &error);
|
||||
}
|
||||
|
||||
if (sitter)
|
||||
_dbus_babysitter_unref (sitter);
|
||||
|
||||
if (!dbus_error_is_set (&error))
|
||||
{
|
||||
_dbus_warn ("Did not get an error launching binary that exited with failure code\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
|
||||
dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
|
||||
{
|
||||
_dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
|
||||
error.name, error.message);
|
||||
dbus_error_free (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dbus_error_free (&error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
check_spawn_and_kill (void *data)
|
||||
{
|
||||
char *argv[4] = { NULL, NULL, NULL, NULL };
|
||||
DBusBabysitter *sitter;
|
||||
DBusError error;
|
||||
|
||||
sitter = NULL;
|
||||
|
||||
dbus_error_init (&error);
|
||||
|
||||
/*** Test launching sleeping binary then killing it */
|
||||
|
||||
argv[0] = TEST_SLEEP_FOREVER_BINARY;
|
||||
if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
|
||||
NULL, NULL,
|
||||
&error))
|
||||
{
|
||||
_dbus_babysitter_kill_child (sitter);
|
||||
|
||||
_dbus_babysitter_block_for_child_exit (sitter);
|
||||
|
||||
_dbus_babysitter_set_child_exit_error (sitter, &error);
|
||||
}
|
||||
|
||||
if (sitter)
|
||||
_dbus_babysitter_unref (sitter);
|
||||
|
||||
if (!dbus_error_is_set (&error))
|
||||
{
|
||||
_dbus_warn ("Did not get an error after killing spawned binary\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
|
||||
dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
|
||||
{
|
||||
_dbus_warn ("Not expecting error when killing executable: %s: %s\n",
|
||||
error.name, error.message);
|
||||
dbus_error_free (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dbus_error_free (&error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
dbus_bool_t
|
||||
_dbus_spawn_test (const char *test_data_dir)
|
||||
{
|
||||
if (!_dbus_test_oom_handling ("spawn_nonexistent",
|
||||
check_spawn_nonexistent,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
/* Don't run the obnoxious segfault test by default,
|
||||
* it's a pain to have to click all those error boxes.
|
||||
*/
|
||||
if (getenv ("DO_SEGFAULT_TEST"))
|
||||
if (!_dbus_test_oom_handling ("spawn_segfault",
|
||||
check_spawn_segfault,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
if (!_dbus_test_oom_handling ("spawn_exit",
|
||||
check_spawn_exit,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
if (!_dbus_test_oom_handling ("spawn_and_kill",
|
||||
check_spawn_and_kill,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
821
dbus/dbus-sysdeps-util-win.c
Normal file
821
dbus/dbus-sysdeps-util-win.c
Normal file
|
|
@ -0,0 +1,821 @@
|
|||
/* -*- mode: C; c-file-style: "gnu" -*- */
|
||||
/* dbus-sysdeps-util.c Would be in dbus-sysdeps.c, but not used in libdbus
|
||||
*
|
||||
* Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
|
||||
* Copyright (C) 2003 CodeFactory AB
|
||||
*
|
||||
* Licensed under the Academic Free License version 2.1
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#undef open
|
||||
|
||||
#define STRSAFE_NO_DEPRECATE
|
||||
|
||||
#include "dbus-sysdeps.h"
|
||||
#include "dbus-internals.h"
|
||||
#include "dbus-protocol.h"
|
||||
#include "dbus-string.h"
|
||||
#include "dbus-sysdeps.h"
|
||||
#include "dbus-sysdeps-win.h"
|
||||
#include "dbus-memory.h"
|
||||
|
||||
#include <io.h>
|
||||
#include <sys/stat.h>
|
||||
#include <aclapi.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/**
|
||||
* Does the chdir, fork, setsid, etc. to become a daemon process.
|
||||
*
|
||||
* @param pidfile #NULL, or pidfile to create
|
||||
* @param print_pid_fd file descriptor to print daemon's pid to, or -1 for none
|
||||
* @param error return location for errors
|
||||
* @returns #FALSE on failure
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_become_daemon (const DBusString *pidfile,
|
||||
int print_pid_fd,
|
||||
DBusError *error)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file containing the process ID.
|
||||
*
|
||||
* @param filename the filename to write to
|
||||
* @param pid our process ID
|
||||
* @param error return location for errors
|
||||
* @returns #FALSE on failure
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_write_pid_file (const DBusString *filename,
|
||||
unsigned long pid,
|
||||
DBusError *error)
|
||||
{
|
||||
const char *cfilename;
|
||||
DBusFile file;
|
||||
FILE *f;
|
||||
|
||||
cfilename = _dbus_string_get_const_data (filename);
|
||||
|
||||
if (!_dbus_open_file(&file, cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644))
|
||||
{
|
||||
dbus_set_error (error, _dbus_error_from_errno (errno),
|
||||
"Failed to open \"%s\": %s", cfilename,
|
||||
_dbus_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((f = fdopen (file.FDATA, "w")) == NULL)
|
||||
{
|
||||
dbus_set_error (error, _dbus_error_from_errno (errno),
|
||||
"Failed to fdopen fd %d: %s", file.FDATA, _dbus_strerror (errno));
|
||||
_dbus_close_file (&file, NULL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (fprintf (f, "%lu\n", pid) < 0)
|
||||
{
|
||||
dbus_set_error (error, _dbus_error_from_errno (errno),
|
||||
"Failed to write to \"%s\": %s", cfilename,
|
||||
_dbus_strerror (errno));
|
||||
|
||||
fclose (f);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (fclose (f) == EOF)
|
||||
{
|
||||
dbus_set_error (error, _dbus_error_from_errno (errno),
|
||||
"Failed to close \"%s\": %s", cfilename,
|
||||
_dbus_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the user and group the bus is running as.
|
||||
*
|
||||
* @param uid the new user ID
|
||||
* @param gid the new group ID
|
||||
* @param error return location for errors
|
||||
* @returns #FALSE on failure
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_change_identity (dbus_uid_t uid,
|
||||
dbus_gid_t gid,
|
||||
DBusError *error)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/** Checks if user is at the console
|
||||
*
|
||||
* @param username user to check
|
||||
* @param error return location for errors
|
||||
* @returns #TRUE is the user is at the consolei and there are no errors
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_user_at_console(const char *username,
|
||||
DBusError *error)
|
||||
{
|
||||
#ifdef DBUS_WINCE
|
||||
return TRUE;
|
||||
#else
|
||||
dbus_bool_t retval = FALSE;
|
||||
wchar_t *wusername;
|
||||
DWORD sid_length;
|
||||
PSID user_sid, console_user_sid;
|
||||
HWINSTA winsta;
|
||||
|
||||
wusername = _dbus_win_utf8_to_utf16 (username, error);
|
||||
if (!wusername)
|
||||
return FALSE;
|
||||
|
||||
if (!_dbus_win_account_to_sid (wusername, &user_sid, error))
|
||||
goto out0;
|
||||
|
||||
/* Now we have the SID for username. Get the SID of the
|
||||
* user at the "console" (window station WinSta0)
|
||||
*/
|
||||
if (!(winsta = OpenWindowStation ("WinSta0", FALSE, READ_CONTROL)))
|
||||
{
|
||||
_dbus_win_set_error_from_win_error (error, GetLastError ());
|
||||
goto out2;
|
||||
}
|
||||
|
||||
sid_length = 0;
|
||||
GetUserObjectInformation (winsta, UOI_USER_SID,
|
||||
NULL, 0, &sid_length);
|
||||
if (sid_length == 0)
|
||||
{
|
||||
/* Nobody is logged on */
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (sid_length < 0 || sid_length > 1000)
|
||||
{
|
||||
dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid SID length");
|
||||
goto out3;
|
||||
}
|
||||
|
||||
console_user_sid = dbus_malloc (sid_length);
|
||||
if (!console_user_sid)
|
||||
{
|
||||
_DBUS_SET_OOM (error);
|
||||
goto out3;
|
||||
}
|
||||
|
||||
if (!GetUserObjectInformation (winsta, UOI_USER_SID,
|
||||
console_user_sid, sid_length, &sid_length))
|
||||
{
|
||||
_dbus_win_set_error_from_win_error (error, GetLastError ());
|
||||
goto out4;
|
||||
}
|
||||
|
||||
if (!IsValidSid (console_user_sid))
|
||||
{
|
||||
dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid SID");
|
||||
goto out4;
|
||||
}
|
||||
|
||||
retval = EqualSid (user_sid, console_user_sid);
|
||||
|
||||
out4:
|
||||
dbus_free (console_user_sid);
|
||||
out3:
|
||||
CloseWindowStation (winsta);
|
||||
out2:
|
||||
dbus_free (user_sid);
|
||||
out0:
|
||||
dbus_free (wusername);
|
||||
|
||||
return retval;
|
||||
#endif //DBUS_WINCE
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory; Directory must be empty
|
||||
*
|
||||
* @param filename directory filename
|
||||
* @param error initialized error object
|
||||
* @returns #TRUE on success
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_delete_directory (const DBusString *filename,
|
||||
DBusError *error)
|
||||
{
|
||||
const char *filename_c;
|
||||
|
||||
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
|
||||
|
||||
filename_c = _dbus_string_get_const_data (filename);
|
||||
|
||||
if (rmdir (filename_c) != 0)
|
||||
{
|
||||
dbus_set_error (error, DBUS_ERROR_FAILED,
|
||||
"Failed to remove directory %s: %s\n",
|
||||
filename_c, _dbus_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/** Installs a signal handler
|
||||
*
|
||||
* @param sig the signal to handle
|
||||
* @param handler the handler
|
||||
*/
|
||||
void
|
||||
_dbus_set_signal_handler (int sig,
|
||||
DBusSignalHandler handler)
|
||||
{
|
||||
_dbus_verbose ("_dbus_set_signal_handler() has to be implemented\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* stat() wrapper.
|
||||
*
|
||||
* @param filename the filename to stat
|
||||
* @param statbuf the stat info to fill in
|
||||
* @param error return location for error
|
||||
* @returns #FALSE if error was set
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_stat(const DBusString *filename,
|
||||
DBusStat *statbuf,
|
||||
DBusError *error)
|
||||
{
|
||||
#ifdef DBUS_WINCE
|
||||
return TRUE;
|
||||
//TODO
|
||||
#else
|
||||
const char *filename_c;
|
||||
#if !defined(DBUS_WIN) && !defined(DBUS_WINCE)
|
||||
|
||||
struct stat sb;
|
||||
#else
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA wfad;
|
||||
char *lastdot;
|
||||
DWORD rc;
|
||||
PSID owner_sid, group_sid;
|
||||
PSECURITY_DESCRIPTOR sd;
|
||||
#endif
|
||||
|
||||
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
|
||||
|
||||
filename_c = _dbus_string_get_const_data (filename);
|
||||
|
||||
if (!GetFileAttributesEx (filename_c, GetFileExInfoStandard, &wfad))
|
||||
{
|
||||
_dbus_win_set_error_from_win_error (error, GetLastError ());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
statbuf->mode = _S_IFDIR;
|
||||
else
|
||||
statbuf->mode = _S_IFREG;
|
||||
|
||||
statbuf->mode |= _S_IREAD;
|
||||
if (wfad.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
statbuf->mode |= _S_IWRITE;
|
||||
|
||||
lastdot = strrchr (filename_c, '.');
|
||||
if (lastdot && stricmp (lastdot, ".exe") == 0)
|
||||
statbuf->mode |= _S_IEXEC;
|
||||
|
||||
statbuf->mode |= (statbuf->mode & 0700) >> 3;
|
||||
statbuf->mode |= (statbuf->mode & 0700) >> 6;
|
||||
|
||||
statbuf->nlink = 1;
|
||||
|
||||
sd = NULL;
|
||||
rc = GetNamedSecurityInfo ((char *) filename_c, SE_FILE_OBJECT,
|
||||
OWNER_SECURITY_INFORMATION |
|
||||
GROUP_SECURITY_INFORMATION,
|
||||
&owner_sid, &group_sid,
|
||||
NULL, NULL,
|
||||
&sd);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
_dbus_win_set_error_from_win_error (error, rc);
|
||||
if (sd != NULL)
|
||||
LocalFree (sd);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
statbuf->uid = _dbus_win_sid_to_uid_t (owner_sid);
|
||||
statbuf->gid = _dbus_win_sid_to_uid_t (group_sid);
|
||||
|
||||
LocalFree (sd);
|
||||
|
||||
statbuf->size = ((dbus_int64_t) wfad.nFileSizeHigh << 32) + wfad.nFileSizeLow;
|
||||
|
||||
statbuf->atime =
|
||||
(((dbus_int64_t) wfad.ftLastAccessTime.dwHighDateTime << 32) +
|
||||
wfad.ftLastAccessTime.dwLowDateTime) / 10000000 - DBUS_INT64_CONSTANT (116444736000000000);
|
||||
|
||||
statbuf->mtime =
|
||||
(((dbus_int64_t) wfad.ftLastWriteTime.dwHighDateTime << 32) +
|
||||
wfad.ftLastWriteTime.dwLowDateTime) / 10000000 - DBUS_INT64_CONSTANT (116444736000000000);
|
||||
|
||||
statbuf->ctime =
|
||||
(((dbus_int64_t) wfad.ftCreationTime.dwHighDateTime << 32) +
|
||||
wfad.ftCreationTime.dwLowDateTime) / 10000000 - DBUS_INT64_CONSTANT (116444736000000000);
|
||||
|
||||
return TRUE;
|
||||
#endif //DBUS_WINCE
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DIRENT_H
|
||||
|
||||
// mingw ships with dirent.h
|
||||
#include <dirent.h>
|
||||
#define _dbus_opendir opendir
|
||||
#define _dbus_readdir readdir
|
||||
#define _dbus_closedir closedir
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HAVE_IO_H
|
||||
#include <io.h> // win32 file functions
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* This file is part of the KDE project
|
||||
Copyright (C) 2000 Werner Almesberger
|
||||
|
||||
libc/sys/linux/sys/dirent.h - Directory entry as returned by readdir
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this program; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#define HAVE_NO_D_NAMLEN /* no struct dirent->d_namlen */
|
||||
#define HAVE_DD_LOCK /* have locking mechanism */
|
||||
|
||||
#define MAXNAMLEN 255 /* sizeof(struct dirent.d_name)-1 */
|
||||
|
||||
#define __dirfd(dir) (dir)->dd_fd
|
||||
|
||||
/* struct dirent - same as Unix */
|
||||
struct dirent
|
||||
{
|
||||
long d_ino; /* inode (always 1 in WIN32) */
|
||||
off_t d_off; /* offset to this dirent */
|
||||
unsigned short d_reclen; /* length of d_name */
|
||||
char d_name[_MAX_FNAME+1]; /* filename (null terminated) */
|
||||
};
|
||||
|
||||
/* typedef DIR - not the same as Unix */
|
||||
typedef struct
|
||||
{
|
||||
long handle; /* _findfirst/_findnext handle */
|
||||
short offset; /* offset into directory */
|
||||
short finished; /* 1 if there are not more files */
|
||||
struct _finddata_t fileinfo; /* from _findfirst/_findnext */
|
||||
char *dir; /* the dir we are reading */
|
||||
struct dirent dent; /* the dirent to return */
|
||||
}
|
||||
DIR;
|
||||
|
||||
/**********************************************************************
|
||||
* Implement dirent-style opendir/readdir/closedir on Window 95/NT
|
||||
*
|
||||
* Functions defined are opendir(), readdir() and closedir() with the
|
||||
* same prototypes as the normal dirent.h implementation.
|
||||
*
|
||||
* Does not implement telldir(), seekdir(), rewinddir() or scandir().
|
||||
* The dirent struct is compatible with Unix, except that d_ino is
|
||||
* always 1 and d_off is made up as we go along.
|
||||
*
|
||||
* The DIR typedef is not compatible with Unix.
|
||||
**********************************************************************/
|
||||
|
||||
DIR * _dbus_opendir(const char *dir)
|
||||
{
|
||||
DIR *dp;
|
||||
char *filespec;
|
||||
long handle;
|
||||
int index;
|
||||
|
||||
filespec = malloc(strlen(dir) + 2 + 1);
|
||||
strcpy(filespec, dir);
|
||||
index = strlen(filespec) - 1;
|
||||
if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))
|
||||
filespec[index] = '\0';
|
||||
strcat(filespec, "\\*");
|
||||
|
||||
dp = (DIR *)malloc(sizeof(DIR));
|
||||
dp->offset = 0;
|
||||
dp->finished = 0;
|
||||
dp->dir = strdup(dir);
|
||||
|
||||
if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
dp->finished = 1;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dp->handle = handle;
|
||||
free(filespec);
|
||||
|
||||
return dp;
|
||||
}
|
||||
|
||||
struct dirent * _dbus_readdir(DIR *dp)
|
||||
{
|
||||
if (!dp || dp->finished)
|
||||
return NULL;
|
||||
|
||||
if (dp->offset != 0)
|
||||
{
|
||||
if (_findnext(dp->handle, &(dp->fileinfo)) < 0)
|
||||
{
|
||||
dp->finished = 1;
|
||||
errno = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
dp->offset++;
|
||||
|
||||
strncpy(dp->dent.d_name, dp->fileinfo.name, _MAX_FNAME);
|
||||
dp->dent.d_ino = 1;
|
||||
dp->dent.d_reclen = strlen(dp->dent.d_name);
|
||||
dp->dent.d_off = dp->offset;
|
||||
|
||||
return &(dp->dent);
|
||||
}
|
||||
|
||||
|
||||
int _dbus_closedir(DIR *dp)
|
||||
{
|
||||
if (!dp)
|
||||
return 0;
|
||||
_findclose(dp->handle);
|
||||
if (dp->dir)
|
||||
free(dp->dir);
|
||||
if (dp)
|
||||
free(dp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif //#ifdef HAVE_DIRENT_H
|
||||
|
||||
/**
|
||||
* Internals of directory iterator
|
||||
*/
|
||||
struct DBusDirIter
|
||||
{
|
||||
DIR *d; /**< The DIR* from opendir() */
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a directory to iterate over.
|
||||
*
|
||||
* @param filename the directory name
|
||||
* @param error exception return object or #NULL
|
||||
* @returns new iterator, or #NULL on error
|
||||
*/
|
||||
DBusDirIter*
|
||||
_dbus_directory_open (const DBusString *filename,
|
||||
DBusError *error)
|
||||
{
|
||||
DIR *d;
|
||||
DBusDirIter *iter;
|
||||
const char *filename_c;
|
||||
|
||||
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
|
||||
|
||||
filename_c = _dbus_string_get_const_data (filename);
|
||||
|
||||
d = _dbus_opendir (filename_c);
|
||||
if (d == NULL)
|
||||
{
|
||||
dbus_set_error (error, _dbus_error_from_errno (errno),
|
||||
"Failed to read directory \"%s\": %s",
|
||||
filename_c,
|
||||
_dbus_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
iter = dbus_new0 (DBusDirIter, 1);
|
||||
if (iter == NULL)
|
||||
{
|
||||
_dbus_closedir (d);
|
||||
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
|
||||
"Could not allocate memory for directory iterator");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iter->d = d;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next file in the directory. Will not return "." or ".." on
|
||||
* UNIX. If an error occurs, the contents of "filename" are
|
||||
* undefined. The error is never set if the function succeeds.
|
||||
*
|
||||
* @todo for thread safety, I think we have to use
|
||||
* readdir_r(). (GLib has the same issue, should file a bug.)
|
||||
*
|
||||
* @param iter the iterator
|
||||
* @param filename string to be set to the next file in the dir
|
||||
* @param error return location for error
|
||||
* @returns #TRUE if filename was filled in with a new filename
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_directory_get_next_file (DBusDirIter *iter,
|
||||
DBusString *filename,
|
||||
DBusError *error)
|
||||
{
|
||||
struct dirent *ent;
|
||||
|
||||
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
|
||||
|
||||
again:
|
||||
errno = 0;
|
||||
ent = _dbus_readdir (iter->d);
|
||||
if (ent == NULL)
|
||||
{
|
||||
if (errno != 0)
|
||||
dbus_set_error (error,
|
||||
_dbus_error_from_errno (errno),
|
||||
"%s", _dbus_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
else if (ent->d_name[0] == '.' &&
|
||||
(ent->d_name[1] == '\0' ||
|
||||
(ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
|
||||
goto again;
|
||||
else
|
||||
{
|
||||
_dbus_string_set_length (filename, 0);
|
||||
if (!_dbus_string_append (filename, ent->d_name))
|
||||
{
|
||||
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
|
||||
"No memory to read directory entry");
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a directory iteration.
|
||||
*/
|
||||
void
|
||||
_dbus_directory_close (DBusDirIter *iter)
|
||||
{
|
||||
_dbus_closedir (iter->d);
|
||||
dbus_free (iter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the filename is an absolute path
|
||||
*
|
||||
* @param filename the filename
|
||||
* @returns #TRUE if an absolute path
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_path_is_absolute (const DBusString *filename)
|
||||
{
|
||||
if (_dbus_string_get_length (filename) > 0)
|
||||
return _dbus_string_get_byte (filename, 1) == ':'
|
||||
|| _dbus_string_get_byte (filename, 0) == '\\'
|
||||
|| _dbus_string_get_byte (filename, 0) == '/';
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static dbus_bool_t
|
||||
fill_group_info(DBusGroupInfo *info,
|
||||
dbus_gid_t gid,
|
||||
const DBusString *groupname,
|
||||
DBusError *error)
|
||||
{
|
||||
const char *group_c_str;
|
||||
|
||||
_dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
|
||||
_dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
|
||||
|
||||
if (groupname)
|
||||
group_c_str = _dbus_string_get_const_data (groupname);
|
||||
else
|
||||
group_c_str = NULL;
|
||||
|
||||
if (group_c_str)
|
||||
{
|
||||
PSID group_sid;
|
||||
wchar_t *wgroupname = _dbus_win_utf8_to_utf16 (group_c_str, error);
|
||||
|
||||
if (!wgroupname)
|
||||
return FALSE;
|
||||
|
||||
if (!_dbus_win_account_to_sid (wgroupname, &group_sid, error))
|
||||
{
|
||||
dbus_free (wgroupname);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
info->gid = _dbus_win_sid_to_uid_t (group_sid);
|
||||
info->groupname = _dbus_strdup (group_c_str);
|
||||
|
||||
dbus_free (group_sid);
|
||||
dbus_free (wgroupname);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbus_bool_t retval = FALSE;
|
||||
wchar_t *wname, *wdomain;
|
||||
char *name, *domain;
|
||||
|
||||
info->gid = gid;
|
||||
|
||||
if (!_dbus_win_sid_to_name_and_domain (gid, &wname, &wdomain, error))
|
||||
return FALSE;
|
||||
|
||||
name = _dbus_win_utf16_to_utf8 (wname, error);
|
||||
if (!name)
|
||||
goto out0;
|
||||
|
||||
domain = _dbus_win_utf16_to_utf8 (wdomain, error);
|
||||
if (!domain)
|
||||
goto out1;
|
||||
|
||||
info->groupname = dbus_malloc (strlen (domain) + 1 + strlen (name) + 1);
|
||||
|
||||
strcpy (info->groupname, domain);
|
||||
strcat (info->groupname, "\\");
|
||||
strcat (info->groupname, name);
|
||||
|
||||
retval = TRUE;
|
||||
|
||||
dbus_free (domain);
|
||||
out1:
|
||||
dbus_free (name);
|
||||
out0:
|
||||
dbus_free (wname);
|
||||
dbus_free (wdomain);
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the given DBusGroupInfo struct
|
||||
* with information about the given group ID.
|
||||
*
|
||||
* @param info the group info struct
|
||||
* @param gid group ID
|
||||
* @param error the error return
|
||||
* @returns #FALSE if error is set
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_group_info_fill_gid (DBusGroupInfo *info,
|
||||
dbus_gid_t gid,
|
||||
DBusError *error)
|
||||
{
|
||||
return fill_group_info (info, gid, NULL, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the given DBusGroupInfo struct
|
||||
* with information about the given group name.
|
||||
*
|
||||
* @param info the group info struct
|
||||
* @param groupname name of group
|
||||
* @param error the error return
|
||||
* @returns #FALSE if error is set
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_group_info_fill (DBusGroupInfo *info,
|
||||
const DBusString *groupname,
|
||||
DBusError *error)
|
||||
{
|
||||
return fill_group_info (info, DBUS_GID_UNSET,
|
||||
groupname, error);
|
||||
}
|
||||
|
||||
/** @} */ /* End of DBusInternalsUtils functions */
|
||||
|
||||
/**
|
||||
* @addtogroup DBusString
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* Get the directory name from a complete filename
|
||||
* @param filename the filename
|
||||
* @param dirname string to append directory name to
|
||||
* @returns #FALSE if no memory
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_string_get_dirname(const DBusString *filename,
|
||||
DBusString *dirname)
|
||||
{
|
||||
int sep;
|
||||
|
||||
_dbus_assert (filename != dirname);
|
||||
_dbus_assert (filename != NULL);
|
||||
_dbus_assert (dirname != NULL);
|
||||
|
||||
/* Ignore any separators on the end */
|
||||
sep = _dbus_string_get_length (filename);
|
||||
if (sep == 0)
|
||||
return _dbus_string_append (dirname, "."); /* empty string passed in */
|
||||
|
||||
while (sep > 0 &&
|
||||
(_dbus_string_get_byte (filename, sep - 1) == '/' ||
|
||||
_dbus_string_get_byte (filename, sep - 1) == '\\'))
|
||||
--sep;
|
||||
|
||||
_dbus_assert (sep >= 0);
|
||||
|
||||
if (sep == 0 ||
|
||||
(sep == 2 &&
|
||||
_dbus_string_get_byte (filename, 1) == ':' &&
|
||||
isalpha (_dbus_string_get_byte (filename, 0))))
|
||||
return _dbus_string_copy_len (filename, 0, sep + 1,
|
||||
dirname, _dbus_string_get_length (dirname));
|
||||
|
||||
{
|
||||
int sep1, sep2;
|
||||
_dbus_string_find_byte_backward (filename, sep, '/', &sep1);
|
||||
_dbus_string_find_byte_backward (filename, sep, '\\', &sep2);
|
||||
|
||||
sep = MAX (sep1, sep2);
|
||||
}
|
||||
if (sep < 0)
|
||||
return _dbus_string_append (dirname, ".");
|
||||
|
||||
while (sep > 0 &&
|
||||
(_dbus_string_get_byte (filename, sep - 1) == '/' ||
|
||||
_dbus_string_get_byte (filename, sep - 1) == '\\'))
|
||||
--sep;
|
||||
|
||||
_dbus_assert (sep >= 0);
|
||||
|
||||
if ((sep == 0 ||
|
||||
(sep == 2 &&
|
||||
_dbus_string_get_byte (filename, 1) == ':' &&
|
||||
isalpha (_dbus_string_get_byte (filename, 0))))
|
||||
&&
|
||||
(_dbus_string_get_byte (filename, sep) == '/' ||
|
||||
_dbus_string_get_byte (filename, sep) == '\\'))
|
||||
return _dbus_string_copy_len (filename, 0, sep + 1,
|
||||
dirname, _dbus_string_get_length (dirname));
|
||||
else
|
||||
return _dbus_string_copy_len (filename, 0, sep - 0,
|
||||
dirname, _dbus_string_get_length (dirname));
|
||||
}
|
||||
|
||||
/** @} */ /* DBusString stuff */
|
||||
|
||||
5178
dbus/dbus-sysdeps-win.c
Normal file
5178
dbus/dbus-sysdeps-win.c
Normal file
File diff suppressed because it is too large
Load diff
174
dbus/dbus-sysdeps-win.h
Normal file
174
dbus/dbus-sysdeps-win.h
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/* -*- mode: C; c-file-style: "gnu" -*- */
|
||||
/* dbus-sysdeps.c Wrappers around system/libc features (internal to D-BUS implementation)
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Red Hat, Inc.
|
||||
* Copyright (C) 2003 CodeFactory AB
|
||||
* Copyright (C) 2005 Novell, Inc.
|
||||
*
|
||||
* Licensed under the Academic Free License version 2.1
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DBUS_SYSDEPS_WIN_H
|
||||
#define DBUS_SYSDEPS_WIN_H
|
||||
|
||||
#define _WINSOCKAPI_
|
||||
|
||||
#include "dbus-hash.h"
|
||||
#include "dbus-string.h"
|
||||
#include <ctype.h>
|
||||
#include <malloc.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <aclapi.h>
|
||||
#include <lm.h>
|
||||
#include <io.h>
|
||||
#include <share.h>
|
||||
#include <direct.h>
|
||||
|
||||
#define mkdir(path, mode) _mkdir (path)
|
||||
|
||||
#ifndef DBUS_WINCE
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Declarations missing in mingw's headers */
|
||||
extern BOOL WINAPI ConvertStringSidToSidA (LPCSTR StringSid, PSID *Sid);
|
||||
extern BOOL WINAPI ConvertSidToStringSidA (PSID Sid, LPSTR *StringSid);
|
||||
|
||||
|
||||
#define DBUS_CONSOLE_DIR "/var/run/console/"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd; /* File descriptor, SOCKET or file HANDLE */
|
||||
int port_file_fd; /* File descriptor for file containing
|
||||
* port number for "pseudo-unix" sockets
|
||||
*/
|
||||
DBusString port_file; /* File name for said file */
|
||||
dbus_bool_t close_on_exec;
|
||||
dbus_bool_t non_blocking;
|
||||
int is_used;
|
||||
}
|
||||
DBusSocket;
|
||||
|
||||
extern DBusSocket *win_fds;
|
||||
extern int win32_n_fds;
|
||||
|
||||
|
||||
void _dbus_win_startup_winsock (void);
|
||||
void _dbus_win_warn_win_error (const char *message,
|
||||
int code);
|
||||
extern const char* _dbus_lm_strerror (int error_number);
|
||||
|
||||
|
||||
dbus_bool_t
|
||||
fill_win_user_info_from_uid (dbus_uid_t uid,
|
||||
DBusUserInfo *info,
|
||||
DBusError *error);
|
||||
dbus_bool_t
|
||||
fill_win_user_info_from_name (wchar_t *wname,
|
||||
DBusUserInfo *info,
|
||||
DBusError *error);
|
||||
|
||||
dbus_bool_t _dbus_win_account_to_sid (const wchar_t *waccount,
|
||||
void **ppsid,
|
||||
DBusError *error);
|
||||
|
||||
dbus_bool_t
|
||||
_dbus_win32_sid_to_name_and_domain (dbus_uid_t uid,
|
||||
wchar_t **wname,
|
||||
wchar_t **wdomain,
|
||||
DBusError *error);
|
||||
|
||||
|
||||
/* Don't define DBUS_CONSOLE_DIR on Win32 */
|
||||
|
||||
wchar_t *_dbus_win_utf8_to_utf16 (const char *str,
|
||||
DBusError *error);
|
||||
char *_dbus_win_utf16_to_utf8 (const wchar_t *str,
|
||||
DBusError *error);
|
||||
|
||||
void _dbus_win_set_error_from_win_error (DBusError *error, int code);
|
||||
|
||||
dbus_uid_t _dbus_win_sid_to_uid_t (void *psid);
|
||||
dbus_bool_t _dbus_uid_t_to_win_sid (dbus_uid_t uid,
|
||||
void **ppsid);
|
||||
dbus_bool_t
|
||||
_dbus_account_to_win_sid (const wchar_t *waccount,
|
||||
void **ppsid,
|
||||
DBusError *error);
|
||||
dbus_bool_t
|
||||
_dbus_win_sid_to_name_and_domain (dbus_uid_t uid,
|
||||
wchar_t **wname,
|
||||
wchar_t **wdomain,
|
||||
DBusError *error);
|
||||
|
||||
typedef struct DBusFile DBusFile;
|
||||
|
||||
dbus_bool_t _dbus_open_file (DBusFile *file,
|
||||
const char *filename,
|
||||
int oflag,
|
||||
int pmode);
|
||||
|
||||
dbus_bool_t _dbus_close_file (DBusFile *file,
|
||||
DBusError *error);
|
||||
|
||||
|
||||
int _dbus_read_file (DBusFile *file,
|
||||
DBusString *buffer,
|
||||
int count);
|
||||
|
||||
int _dbus_write_file (DBusFile *file,
|
||||
const DBusString *buffer,
|
||||
int start,
|
||||
int len);
|
||||
|
||||
#define FDATA private_data
|
||||
struct DBusFile
|
||||
{
|
||||
int FDATA;
|
||||
};
|
||||
|
||||
|
||||
void _dbus_handle_to_socket (int handle,
|
||||
DBusSocket **socket);
|
||||
int _dbus_socket_to_handle (DBusSocket *socket);
|
||||
|
||||
dbus_bool_t
|
||||
fill_user_info (DBusUserInfo *info,
|
||||
dbus_uid_t uid,
|
||||
const DBusString *username,
|
||||
DBusError *error);
|
||||
|
||||
// replace with a windows version
|
||||
dbus_bool_t _dbus_open_unix_socket (int *fd,
|
||||
DBusError *error);
|
||||
int _dbus_connect_unix_socket (const char *path,
|
||||
dbus_bool_t abstract,
|
||||
DBusError *error);
|
||||
int _dbus_listen_unix_socket (const char *path,
|
||||
dbus_bool_t abstract,
|
||||
DBusError *error);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} end of sysdeps-win.h */
|
||||
|
||||
|
||||
Loading…
Add table
Reference in a new issue