mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 03:16:17 +01:00
Remove !g_thread_supported() codepaths in gio
In particular, remove the libasyncns import, which was only used by GUnixResolver, which is only used when threads are not available. Likewise remove GWin32Resolver, and the hacky broken non-threaded parts of GIOScheduler. https://bugzilla.gnome.org/show_bug.cgi?id=616754
This commit is contained in:
parent
aa586f6354
commit
5a30712dc7
@ -1065,11 +1065,7 @@ fi
|
||||
AC_CHECK_FUNCS(getprotobyname_r endservent)
|
||||
AC_CHECK_HEADERS([netdb.h wspiapi.h arpa/nameser_compat.h])
|
||||
|
||||
# For gio/libasyncns
|
||||
if test $glib_native_win32 = no; then
|
||||
AC_CHECK_FUNCS(strndup setresuid setreuid)
|
||||
AC_CHECK_HEADERS(sys/prctl.h)
|
||||
|
||||
# We can't just use AC_CHECK_FUNC/AC_CHECK_LIB here. Bug 586150
|
||||
NETWORK_LIBS=""
|
||||
AC_MSG_CHECKING([for res_query])
|
||||
@ -3842,7 +3838,6 @@ gio/gdbus-2.0/codegen/Makefile
|
||||
gio/gdbus-2.0/codegen/config.py
|
||||
gio/xdgmime/Makefile
|
||||
gio/inotify/Makefile
|
||||
gio/libasyncns/Makefile
|
||||
gio/fen/Makefile
|
||||
gio/fam/Makefile
|
||||
gio/win32/Makefile
|
||||
|
@ -5,7 +5,7 @@ NULL =
|
||||
SUBDIRS = gdbus-2.0/codegen
|
||||
|
||||
if OS_UNIX
|
||||
SUBDIRS += libasyncns xdgmime
|
||||
SUBDIRS += xdgmime
|
||||
endif
|
||||
|
||||
if OS_WIN32_AND_DLL_COMPILATION
|
||||
@ -200,8 +200,8 @@ endif
|
||||
|
||||
if OS_UNIX
|
||||
appinfo_sources += gdesktopappinfo.c
|
||||
platform_libadd += libasyncns/libasyncns.la xdgmime/libxdgmime.la
|
||||
platform_deps += libasyncns/libasyncns.la xdgmime/libxdgmime.la
|
||||
platform_libadd += xdgmime/libxdgmime.la $(RESOLVER_LIBADD)
|
||||
platform_deps += xdgmime/libxdgmime.la
|
||||
unix_sources = \
|
||||
gfiledescriptorbased.c \
|
||||
gunixconnection.c \
|
||||
@ -211,8 +211,6 @@ unix_sources = \
|
||||
gunixmount.c \
|
||||
gunixmount.h \
|
||||
gunixmounts.c \
|
||||
gunixresolver.c \
|
||||
gunixresolver.h \
|
||||
gunixsocketaddress.c \
|
||||
gunixvolume.c \
|
||||
gunixvolume.h \
|
||||
@ -241,8 +239,6 @@ endif
|
||||
win32_actual_sources = \
|
||||
gwin32mount.c \
|
||||
gwin32mount.h \
|
||||
gwin32resolver.c \
|
||||
gwin32resolver.h \
|
||||
gwin32volumemonitor.c \
|
||||
gwin32volumemonitor.h \
|
||||
gwin32inputstream.c \
|
||||
|
@ -79,7 +79,7 @@ g_cancellable_class_init (GCancellableClass *klass)
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GCancellablePrivate));
|
||||
|
||||
if (cancellable_cond == NULL && g_thread_supported ())
|
||||
if (cancellable_cond == NULL)
|
||||
cancellable_cond = g_cond_new ();
|
||||
|
||||
gobject_class->finalize = g_cancellable_finalize;
|
||||
|
@ -875,12 +875,6 @@ g_resolver_lookup_service
|
||||
g_resolver_lookup_service_async
|
||||
g_resolver_lookup_service_finish
|
||||
g_threaded_resolver_get_type
|
||||
#ifdef G_OS_UNIX
|
||||
g_unix_resolver_get_type
|
||||
#endif
|
||||
#ifdef G_OS_WIN32
|
||||
g_win32_resolver_get_type
|
||||
#endif
|
||||
g_srv_target_get_type
|
||||
g_srv_target_new
|
||||
g_srv_target_copy
|
||||
|
@ -32,8 +32,7 @@
|
||||
* @include: gio/gio.h
|
||||
*
|
||||
* Schedules asynchronous I/O operations. #GIOScheduler integrates
|
||||
* into the main event loop (#GMainLoop) and may use threads if they
|
||||
* are available.
|
||||
* into the main event loop (#GMainLoop) and uses threads.
|
||||
*
|
||||
* <para id="io-priority"><indexterm><primary>I/O priority</primary></indexterm>
|
||||
* Each I/O operation has a priority, and the scheduler uses the priorities
|
||||
@ -55,8 +54,6 @@ struct _GIOSchedulerJob {
|
||||
gint io_priority;
|
||||
GCancellable *cancellable;
|
||||
GMainContext *context;
|
||||
|
||||
guint idle_tag;
|
||||
};
|
||||
|
||||
G_LOCK_DEFINE_STATIC(active_jobs);
|
||||
@ -188,23 +185,6 @@ io_job_thread (gpointer data,
|
||||
job_destroy (job);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
run_job_at_idle (gpointer data)
|
||||
{
|
||||
GIOSchedulerJob *job = data;
|
||||
gboolean result;
|
||||
|
||||
if (job->cancellable)
|
||||
g_cancellable_push_current (job->cancellable);
|
||||
|
||||
result = job->job_func (job, job->cancellable, job->data);
|
||||
|
||||
if (job->cancellable)
|
||||
g_cancellable_pop_current (job->cancellable);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_io_scheduler_push_job:
|
||||
* @job_func: a #GIOSchedulerJobFunc.
|
||||
@ -253,20 +233,8 @@ g_io_scheduler_push_job (GIOSchedulerJobFunc job_func,
|
||||
job->active_link = active_jobs;
|
||||
G_UNLOCK (active_jobs);
|
||||
|
||||
if (g_thread_supported())
|
||||
{
|
||||
g_once (&once_init, init_scheduler, NULL);
|
||||
g_thread_pool_push (job_thread_pool, job, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Threads not available, instead do the i/o sync inside a
|
||||
* low prio idle handler
|
||||
*/
|
||||
job->idle_tag = g_idle_add_full (io_priority,
|
||||
run_job_at_idle,
|
||||
job, job_destroy);
|
||||
}
|
||||
g_once (&once_init, init_scheduler, NULL);
|
||||
g_thread_pool_push (job_thread_pool, job, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,17 +338,6 @@ g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
|
||||
g_return_val_if_fail (job != NULL, FALSE);
|
||||
g_return_val_if_fail (func != NULL, FALSE);
|
||||
|
||||
if (job->idle_tag)
|
||||
{
|
||||
/* We just immediately re-enter in the case of idles (non-threads)
|
||||
* Anything else would just deadlock. If you can't handle this, enable threads.
|
||||
*/
|
||||
ret_val = func (user_data);
|
||||
if (notify)
|
||||
notify (user_data);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
proxy = g_new0 (MainLoopProxy, 1);
|
||||
proxy->func = func;
|
||||
proxy->data = user_data;
|
||||
@ -435,17 +392,6 @@ g_io_scheduler_job_send_to_mainloop_async (GIOSchedulerJob *job,
|
||||
g_return_if_fail (job != NULL);
|
||||
g_return_if_fail (func != NULL);
|
||||
|
||||
if (job->idle_tag)
|
||||
{
|
||||
/* We just immediately re-enter in the case of idles (non-threads)
|
||||
* Anything else would just deadlock. If you can't handle this, enable threads.
|
||||
*/
|
||||
func (user_data);
|
||||
if (notify)
|
||||
notify (user_data);
|
||||
return;
|
||||
}
|
||||
|
||||
proxy = g_new0 (MainLoopProxy, 1);
|
||||
proxy->func = func;
|
||||
proxy->data = user_data;
|
||||
|
@ -31,14 +31,11 @@
|
||||
#include "ginetsocketaddress.h"
|
||||
#include "gsimpleasyncresult.h"
|
||||
#include "gsrvtarget.h"
|
||||
#include "gthreadedresolver.h"
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include "gunixresolver.h"
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef G_OS_WIN32
|
||||
#include "gwin32resolver.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -153,18 +150,7 @@ GResolver *
|
||||
g_resolver_get_default (void)
|
||||
{
|
||||
if (!default_resolver)
|
||||
{
|
||||
if (g_thread_supported ())
|
||||
default_resolver = g_object_new (G_TYPE_THREADED_RESOLVER, NULL);
|
||||
else
|
||||
{
|
||||
#if defined(G_OS_UNIX)
|
||||
default_resolver = g_object_new (G_TYPE_UNIX_RESOLVER, NULL);
|
||||
#elif defined(G_OS_WIN32)
|
||||
default_resolver = g_object_new (G_TYPE_WIN32_RESOLVER, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
default_resolver = g_object_new (G_TYPE_THREADED_RESOLVER, NULL);
|
||||
|
||||
return g_object_ref (default_resolver);
|
||||
}
|
||||
|
@ -42,9 +42,8 @@ static void threaded_resolver_thread (gpointer thread_data, gpointer pool_data);
|
||||
static void
|
||||
g_threaded_resolver_init (GThreadedResolver *gtr)
|
||||
{
|
||||
if (g_thread_supported ())
|
||||
gtr->thread_pool = g_thread_pool_new (threaded_resolver_thread, gtr,
|
||||
-1, FALSE, NULL);
|
||||
gtr->thread_pool = g_thread_pool_new (threaded_resolver_thread, gtr,
|
||||
-1, FALSE, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -186,8 +185,7 @@ g_threaded_resolver_request_new (GThreadedResolverResolveFunc resolve_func,
|
||||
/* Initial refcount is 2; one for the caller and one for resolve_func */
|
||||
req->ref_count = 2;
|
||||
|
||||
if (g_thread_supported ())
|
||||
req->mutex = g_mutex_new ();
|
||||
req->mutex = g_mutex_new ();
|
||||
/* Initially locked; caller must unlock */
|
||||
g_mutex_lock (req->mutex);
|
||||
|
||||
|
@ -1,516 +0,0 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <glib.h>
|
||||
#include "glibintl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gunixresolver.h"
|
||||
#include "gnetworkingprivate.h"
|
||||
|
||||
#include "gcancellable.h"
|
||||
#include "gsimpleasyncresult.h"
|
||||
#include "gsocketaddress.h"
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GUnixResolver, g_unix_resolver, G_TYPE_THREADED_RESOLVER)
|
||||
|
||||
static gboolean g_unix_resolver_watch (GIOChannel *iochannel,
|
||||
GIOCondition condition,
|
||||
gpointer user_data);
|
||||
static void g_unix_resolver_reload (GResolver *resolver);
|
||||
|
||||
static void
|
||||
g_unix_resolver_init (GUnixResolver *gur)
|
||||
{
|
||||
g_unix_resolver_reload (G_RESOLVER (gur));
|
||||
}
|
||||
|
||||
static void
|
||||
g_unix_resolver_finalize (GObject *object)
|
||||
{
|
||||
GUnixResolver *gur = G_UNIX_RESOLVER (object);
|
||||
|
||||
if (gur->watch)
|
||||
g_source_remove (gur->watch);
|
||||
_g_asyncns_free (gur->asyncns);
|
||||
|
||||
G_OBJECT_CLASS (g_unix_resolver_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_unix_resolver_reload (GResolver *resolver)
|
||||
{
|
||||
GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
|
||||
gint fd;
|
||||
GIOChannel *io;
|
||||
|
||||
if (gur->asyncns)
|
||||
{
|
||||
if (_g_asyncns_getnqueries (gur->asyncns) == 0)
|
||||
{
|
||||
g_source_remove (gur->watch);
|
||||
_g_asyncns_free (gur->asyncns);
|
||||
}
|
||||
/* else, we will free it later from g_unix_resolver_watch */
|
||||
}
|
||||
|
||||
/* FIXME: how many workers? */
|
||||
gur->asyncns = _g_asyncns_new (2);
|
||||
|
||||
fd = _g_asyncns_fd (gur->asyncns);
|
||||
io = g_io_channel_unix_new (fd);
|
||||
gur->watch = g_io_add_watch (io, G_IO_IN | G_IO_HUP | G_IO_ERR,
|
||||
g_unix_resolver_watch, gur->asyncns);
|
||||
g_io_channel_unref (io);
|
||||
}
|
||||
|
||||
/* The various request possibilities:
|
||||
*
|
||||
* 1. Synchronous: handed off to the base class (GThreadedResolver);
|
||||
* since it's never possible to cancel a synchronous request in a
|
||||
* single-threaded program, the request is done in the calling
|
||||
* thread.
|
||||
*
|
||||
* 2. Asynchronous: An appropriate _g_asyncns_query_t is created, and
|
||||
* then a GUnixResolverRequest is created with that query and a
|
||||
* GSimpleAsyncResult. Two sub-possibilities:
|
||||
*
|
||||
* a. The resolution completes: g_unix_resolver_watch() sees that
|
||||
* the request has completed, and calls
|
||||
* g_unix_resolver_request_complete(), which detaches the
|
||||
* "cancelled" signal handler (if it was present), queues the
|
||||
* async_result to be completed, and then unrefs it.
|
||||
*
|
||||
* b. The resolution is cancelled: request_cancelled() calls
|
||||
* _g_asyncns_cancel() to cancel the resolution. Then it calls
|
||||
* g_unix_resolver_request_complete(), which detaches the
|
||||
* signal handler, and queues async_result to complete in an
|
||||
* idle handler. Because the asyncns resolution was cancelled,
|
||||
* g_unix_resolver_watch() will never be triggered for this
|
||||
* req.
|
||||
*
|
||||
* Since there's only a single thread, it's not possible for the
|
||||
* request to both complete and be cancelled "at the same time",
|
||||
* and each of the two possibilities takes steps to block the other
|
||||
* from being able to happen later, so it's always safe to free req
|
||||
* after the async_result completes.
|
||||
*/
|
||||
|
||||
typedef struct _GUnixResolverRequest GUnixResolverRequest;
|
||||
typedef void (*GUnixResolverFunc) (GUnixResolverRequest *);
|
||||
|
||||
struct _GUnixResolverRequest {
|
||||
GUnixResolver *gur;
|
||||
_g_asyncns_t *asyncns;
|
||||
|
||||
_g_asyncns_query_t *qy;
|
||||
union {
|
||||
struct {
|
||||
gchar *hostname;
|
||||
GList *addresses;
|
||||
} name;
|
||||
|
||||
struct {
|
||||
GInetAddress *address;
|
||||
gchar *hostname;
|
||||
} address;
|
||||
|
||||
struct {
|
||||
gchar *service;
|
||||
GList *targets;
|
||||
} service;
|
||||
} u;
|
||||
GUnixResolverFunc process_func, free_func;
|
||||
|
||||
GCancellable *cancellable;
|
||||
GSimpleAsyncResult *async_result;
|
||||
|
||||
};
|
||||
|
||||
static void g_unix_resolver_request_free (GUnixResolverRequest *req);
|
||||
static void request_cancelled (GCancellable *cancellable,
|
||||
gpointer user_data);
|
||||
|
||||
static GUnixResolverRequest *
|
||||
g_unix_resolver_request_new (GUnixResolver *gur,
|
||||
_g_asyncns_query_t *qy,
|
||||
GUnixResolverFunc process_func,
|
||||
GUnixResolverFunc free_func,
|
||||
GCancellable *cancellable,
|
||||
GSimpleAsyncResult *async_result)
|
||||
{
|
||||
GUnixResolverRequest *req;
|
||||
|
||||
req = g_slice_new0 (GUnixResolverRequest);
|
||||
req->gur = g_object_ref (gur);
|
||||
req->asyncns = gur->asyncns;
|
||||
req->qy = qy;
|
||||
req->process_func = process_func;
|
||||
req->free_func = free_func;
|
||||
|
||||
if (cancellable)
|
||||
{
|
||||
req->cancellable = g_object_ref (cancellable);
|
||||
g_signal_connect (cancellable, "cancelled",
|
||||
G_CALLBACK (request_cancelled), req);
|
||||
}
|
||||
|
||||
req->async_result = g_object_ref (async_result);
|
||||
|
||||
g_simple_async_result_set_op_res_gpointer (req->async_result, req, (GDestroyNotify)g_unix_resolver_request_free);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void
|
||||
g_unix_resolver_request_free (GUnixResolverRequest *req)
|
||||
{
|
||||
req->free_func (req);
|
||||
|
||||
/* We don't have to free req->cancellable and req->async_result,
|
||||
* since they must already have been freed if we're here.
|
||||
*/
|
||||
|
||||
/* Check if this was the last request remaining on an old asyncns. */
|
||||
if (req->asyncns != req->gur->asyncns &&
|
||||
_g_asyncns_getnqueries (req->asyncns) == 0)
|
||||
_g_asyncns_free (req->asyncns);
|
||||
|
||||
g_object_unref (req->gur);
|
||||
g_slice_free (GUnixResolverRequest, req);
|
||||
}
|
||||
|
||||
static void
|
||||
g_unix_resolver_request_complete (GUnixResolverRequest *req)
|
||||
{
|
||||
if (req->cancellable)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
|
||||
g_object_unref (req->cancellable);
|
||||
req->cancellable = NULL;
|
||||
}
|
||||
|
||||
/* We always complete_in_idle, even if we were called from
|
||||
* g_unix_resolver_watch(), since we might have been started under a
|
||||
* non-default g_main_context_get_thread_default().
|
||||
*/
|
||||
g_simple_async_result_complete_in_idle (req->async_result);
|
||||
g_object_unref (req->async_result);
|
||||
}
|
||||
|
||||
static void
|
||||
request_cancelled (GCancellable *cancellable,
|
||||
gpointer user_data)
|
||||
{
|
||||
GUnixResolverRequest *req = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
_g_asyncns_cancel (req->asyncns, req->qy);
|
||||
req->qy = NULL;
|
||||
|
||||
g_cancellable_set_error_if_cancelled (cancellable, &error);
|
||||
g_simple_async_result_take_error (req->async_result, error);
|
||||
|
||||
g_unix_resolver_request_complete (req);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_unix_resolver_watch (GIOChannel *iochannel,
|
||||
GIOCondition condition,
|
||||
gpointer user_data)
|
||||
{
|
||||
_g_asyncns_t *asyncns = user_data;
|
||||
_g_asyncns_query_t *qy;
|
||||
GUnixResolverRequest *req;
|
||||
|
||||
if (condition & (G_IO_HUP | G_IO_ERR))
|
||||
{
|
||||
/* Will happen if we reload, and then eventually kill the old
|
||||
* _g_asyncns_t when it's done processing requests.
|
||||
*/
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (_g_asyncns_wait (asyncns, FALSE) == 0 &&
|
||||
(qy = _g_asyncns_getnext (asyncns)) != NULL)
|
||||
{
|
||||
req = _g_asyncns_getuserdata (asyncns, qy);
|
||||
req->process_func (req);
|
||||
g_unix_resolver_request_complete (req);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GUnixResolverRequest *
|
||||
resolve_async (GUnixResolver *gur,
|
||||
_g_asyncns_query_t *qy,
|
||||
GUnixResolverFunc process_func,
|
||||
GUnixResolverFunc free_func,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data,
|
||||
gpointer tag)
|
||||
{
|
||||
GSimpleAsyncResult *result;
|
||||
GUnixResolverRequest *req;
|
||||
|
||||
result = g_simple_async_result_new (G_OBJECT (gur), callback, user_data, tag);
|
||||
req = g_unix_resolver_request_new (gur, qy, process_func, free_func,
|
||||
cancellable, result);
|
||||
g_object_unref (result);
|
||||
_g_asyncns_setuserdata (gur->asyncns, qy, req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_by_name_process (GUnixResolverRequest *req)
|
||||
{
|
||||
struct addrinfo *res;
|
||||
gint retval;
|
||||
GError *error = NULL;
|
||||
|
||||
retval = _g_asyncns_getaddrinfo_done (req->asyncns, req->qy, &res);
|
||||
req->u.name.addresses =
|
||||
_g_resolver_addresses_from_addrinfo (req->u.name.hostname,
|
||||
res, retval, &error);
|
||||
if (res)
|
||||
freeaddrinfo (res);
|
||||
|
||||
if (error)
|
||||
g_simple_async_result_take_error (req->async_result, error);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_by_name_free (GUnixResolverRequest *req)
|
||||
{
|
||||
g_free (req->u.name.hostname);
|
||||
if (req->u.name.addresses)
|
||||
g_resolver_free_addresses (req->u.name.addresses);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_by_name_async (GResolver *resolver,
|
||||
const gchar *hostname,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
|
||||
GUnixResolverRequest *req;
|
||||
_g_asyncns_query_t *qy;
|
||||
|
||||
qy = _g_asyncns_getaddrinfo (gur->asyncns, hostname, NULL,
|
||||
&_g_resolver_addrinfo_hints);
|
||||
req = resolve_async (gur, qy, lookup_by_name_process, lookup_by_name_free,
|
||||
cancellable, callback, user_data, lookup_by_name_async);
|
||||
req->u.name.hostname = g_strdup (hostname);
|
||||
}
|
||||
|
||||
static GList *
|
||||
lookup_by_name_finish (GResolver *resolver,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
GSimpleAsyncResult *simple;
|
||||
GUnixResolverRequest *req;
|
||||
GList *addresses;
|
||||
|
||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), NULL);
|
||||
|
||||
simple = G_SIMPLE_ASYNC_RESULT (result);
|
||||
|
||||
if (g_simple_async_result_propagate_error (simple, error))
|
||||
return NULL;
|
||||
|
||||
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||
addresses = req->u.name.addresses;
|
||||
req->u.name.addresses = NULL;
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lookup_by_address_process (GUnixResolverRequest *req)
|
||||
{
|
||||
gchar host[NI_MAXHOST];
|
||||
gint retval;
|
||||
GError *error = NULL;
|
||||
|
||||
retval = _g_asyncns_getnameinfo_done (req->asyncns, req->qy,
|
||||
host, sizeof (host), NULL, 0);
|
||||
req->u.address.hostname =
|
||||
_g_resolver_name_from_nameinfo (req->u.address.address,
|
||||
host, retval, &error);
|
||||
|
||||
if (error)
|
||||
g_simple_async_result_take_error (req->async_result, error);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_by_address_free (GUnixResolverRequest *req)
|
||||
{
|
||||
g_object_unref (req->u.address.address);
|
||||
if (req->u.address.hostname)
|
||||
g_free (req->u.address.hostname);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_by_address_async (GResolver *resolver,
|
||||
GInetAddress *address,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
|
||||
GUnixResolverRequest *req;
|
||||
_g_asyncns_query_t *qy;
|
||||
struct sockaddr_storage sockaddr;
|
||||
gsize sockaddr_size;
|
||||
|
||||
_g_resolver_address_to_sockaddr (address, &sockaddr, &sockaddr_size);
|
||||
qy = _g_asyncns_getnameinfo (gur->asyncns,
|
||||
(struct sockaddr *)&sockaddr, sockaddr_size,
|
||||
NI_NAMEREQD, TRUE, FALSE);
|
||||
req = resolve_async (gur, qy, lookup_by_address_process,
|
||||
lookup_by_address_free, cancellable,
|
||||
callback, user_data, lookup_by_address_async);
|
||||
req->u.address.address = g_object_ref (address);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
lookup_by_address_finish (GResolver *resolver,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
GSimpleAsyncResult *simple;
|
||||
GUnixResolverRequest *req;
|
||||
gchar *name;
|
||||
|
||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), NULL);
|
||||
|
||||
simple = G_SIMPLE_ASYNC_RESULT (result);
|
||||
|
||||
if (g_simple_async_result_propagate_error (simple, error))
|
||||
return NULL;
|
||||
|
||||
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||
name = req->u.address.hostname;
|
||||
req->u.address.hostname = NULL;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lookup_service_process (GUnixResolverRequest *req)
|
||||
{
|
||||
guchar *answer;
|
||||
gint len, herr;
|
||||
GError *error = NULL;
|
||||
|
||||
len = _g_asyncns_res_done (req->asyncns, req->qy, &answer);
|
||||
if (len < 0)
|
||||
herr = h_errno;
|
||||
else
|
||||
herr = 0;
|
||||
|
||||
req->u.service.targets =
|
||||
_g_resolver_targets_from_res_query (req->u.service.service,
|
||||
answer, len, herr, &error);
|
||||
_g_asyncns_freeanswer (answer);
|
||||
|
||||
if (error)
|
||||
g_simple_async_result_take_error (req->async_result, error);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_service_free (GUnixResolverRequest *req)
|
||||
{
|
||||
g_free (req->u.service.service);
|
||||
if (req->u.service.targets)
|
||||
g_resolver_free_targets (req->u.service.targets);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_service_async (GResolver *resolver,
|
||||
const char *rrname,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
|
||||
GUnixResolverRequest *req;
|
||||
_g_asyncns_query_t *qy;
|
||||
|
||||
qy = _g_asyncns_res_query (gur->asyncns, rrname, C_IN, T_SRV);
|
||||
req = resolve_async (gur, qy, lookup_service_process, lookup_service_free,
|
||||
cancellable, callback, user_data, lookup_service_async);
|
||||
req->u.service.service = g_strdup (rrname);
|
||||
}
|
||||
|
||||
static GList *
|
||||
lookup_service_finish (GResolver *resolver,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
GSimpleAsyncResult *simple;
|
||||
GUnixResolverRequest *req;
|
||||
GList *targets;
|
||||
|
||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), NULL);
|
||||
|
||||
simple = G_SIMPLE_ASYNC_RESULT (result);
|
||||
|
||||
if (g_simple_async_result_propagate_error (simple, error))
|
||||
return NULL;
|
||||
|
||||
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||
targets = req->u.service.targets;
|
||||
req->u.service.targets = NULL;
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
g_unix_resolver_class_init (GUnixResolverClass *unix_class)
|
||||
{
|
||||
GResolverClass *resolver_class = G_RESOLVER_CLASS (unix_class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (unix_class);
|
||||
|
||||
resolver_class->reload = g_unix_resolver_reload;
|
||||
resolver_class->lookup_by_name_async = lookup_by_name_async;
|
||||
resolver_class->lookup_by_name_finish = lookup_by_name_finish;
|
||||
resolver_class->lookup_by_address_async = lookup_by_address_async;
|
||||
resolver_class->lookup_by_address_finish = lookup_by_address_finish;
|
||||
resolver_class->lookup_service_async = lookup_service_async;
|
||||
resolver_class->lookup_service_finish = lookup_service_finish;
|
||||
|
||||
object_class->finalize = g_unix_resolver_finalize;
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __G_UNIX_RESOLVER_H__
|
||||
#define __G_UNIX_RESOLVER_H__
|
||||
|
||||
#include <gio/gthreadedresolver.h>
|
||||
#include "libasyncns/asyncns.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_UNIX_RESOLVER (g_unix_resolver_get_type ())
|
||||
#define G_UNIX_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNIX_RESOLVER, GUnixResolver))
|
||||
#define G_UNIX_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_UNIX_RESOLVER, GUnixResolverClass))
|
||||
#define G_IS_UNIX_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNIX_RESOLVER))
|
||||
#define G_IS_UNIX_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_UNIX_RESOLVER))
|
||||
#define G_UNIX_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_UNIX_RESOLVER, GUnixResolverClass))
|
||||
|
||||
typedef struct {
|
||||
GThreadedResolver parent_instance;
|
||||
|
||||
_g_asyncns_t *asyncns;
|
||||
guint watch;
|
||||
|
||||
} GUnixResolver;
|
||||
|
||||
typedef struct {
|
||||
GThreadedResolverClass parent_class;
|
||||
|
||||
} GUnixResolverClass;
|
||||
|
||||
GType g_unix_resolver_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_RESOLVER_H__ */
|
@ -1,483 +0,0 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <glib.h>
|
||||
#include "glibintl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gwin32resolver.h"
|
||||
#include "gnetworkingprivate.h"
|
||||
|
||||
#include "gcancellable.h"
|
||||
#include "gsimpleasyncresult.h"
|
||||
#include "gsocketaddress.h"
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GWin32Resolver, g_win32_resolver, G_TYPE_THREADED_RESOLVER)
|
||||
|
||||
static void
|
||||
g_win32_resolver_init (GWin32Resolver *gwr)
|
||||
{
|
||||
}
|
||||
|
||||
/* This is simpler than GThreadedResolver since we don't have to worry
|
||||
* about multiple application-level threads, but more complicated than
|
||||
* GUnixResolver, since we do have to deal with multiple threads of
|
||||
* our own.
|
||||
*
|
||||
* The various request possibilities:
|
||||
*
|
||||
* 1. Synchronous: handed off to the base class (GThreadedResolver);
|
||||
* since it's never possible to cancel a synchronous request in a
|
||||
* single-threaded program, the request is done in the calling
|
||||
* thread.
|
||||
*
|
||||
* 2. Asynchronous: A GWin32ResolverRequest is created with
|
||||
* appropriate query-specific information, a Windows event handle,
|
||||
* and a GSimpleAsyncResult. This is then handed to the
|
||||
* Windows-internal thread pool, which does the raw DNS query part
|
||||
* of the operation (being careful to not call any glib methods
|
||||
* that might fail when called from another thread when
|
||||
* g_thread_init() has not been called). The main thread sets up a
|
||||
* GSource to asynchronously poll the event handle. There are two
|
||||
* sub-possibilities:
|
||||
*
|
||||
* a. The resolution completes: the threadpool function calls
|
||||
* SetEvent() on the event handle and then returns.
|
||||
*
|
||||
* b. The resolution is cancelled: request_cancelled()
|
||||
* disconnects the "cancelled" signal handler, and queues an
|
||||
* idle handler to complete the async_result.
|
||||
*
|
||||
* Since we can't free the request from the threadpool thread
|
||||
* (because of glib locking issues), we *always* have to have it
|
||||
* call SetEvent and trigger the callback to indicate that it is
|
||||
* done. But this means that it's possible for the request to be
|
||||
* cancelled (queuing an idle handler to return that result) and
|
||||
* then have the resolution thread complete before the idle handler
|
||||
* runs. So the event callback and the idle handler need to both
|
||||
* watch out for this, making sure we don't complete the same
|
||||
* result twice.
|
||||
*/
|
||||
|
||||
typedef struct GWin32ResolverRequest GWin32ResolverRequest;
|
||||
typedef void (*GWin32ResolverRequestFreeFunc) (GWin32ResolverRequest *);
|
||||
|
||||
struct GWin32ResolverRequest {
|
||||
GWin32ResolverRequestFreeFunc free_func;
|
||||
|
||||
GCancellable *cancellable;
|
||||
GError *error;
|
||||
|
||||
HANDLE *event;
|
||||
GSimpleAsyncResult *async_result;
|
||||
gboolean complete;
|
||||
GSource *cancelled_idle;
|
||||
|
||||
union {
|
||||
struct {
|
||||
gchar *name;
|
||||
gint retval;
|
||||
struct addrinfo *res;
|
||||
} name;
|
||||
|
||||
struct {
|
||||
GInetAddress *iaddr;
|
||||
struct sockaddr_storage addr;
|
||||
gsize addrlen;
|
||||
gint retval;
|
||||
gchar *namebuf;
|
||||
} address;
|
||||
|
||||
struct {
|
||||
gchar *rrname;
|
||||
DNS_STATUS retval;
|
||||
DNS_RECORD *results;
|
||||
} service;
|
||||
} u;
|
||||
|
||||
};
|
||||
|
||||
static GSource *g_win32_handle_source_add (HANDLE handle,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data);
|
||||
|
||||
static gboolean request_completed (gpointer user_data);
|
||||
static void request_cancelled (GCancellable *cancellable,
|
||||
gpointer user_data);
|
||||
|
||||
GWin32ResolverRequest *
|
||||
g_win32_resolver_request_new (GResolver *resolver,
|
||||
GWin32ResolverRequestFreeFunc free_func,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data,
|
||||
gpointer tag)
|
||||
{
|
||||
GWin32ResolverRequest *req;
|
||||
|
||||
req = g_slice_new0 (GWin32ResolverRequest);
|
||||
req->free_func = free_func;
|
||||
|
||||
req->async_result = g_simple_async_result_new (G_OBJECT (resolver), callback,
|
||||
user_data, tag);
|
||||
g_simple_async_result_set_op_res_gpointer (req->async_result, req, NULL);
|
||||
|
||||
req->event = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||
g_win32_handle_source_add (req->event, request_completed, req);
|
||||
|
||||
if (cancellable)
|
||||
{
|
||||
req->cancellable = g_object_ref (cancellable);
|
||||
g_signal_connect (cancellable, "cancelled",
|
||||
G_CALLBACK (request_cancelled), req);
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
request_completed (gpointer user_data)
|
||||
{
|
||||
GWin32ResolverRequest *req = user_data;
|
||||
|
||||
/* Clean up cancellation-related stuff first */
|
||||
if (req->cancelled_idle)
|
||||
{
|
||||
g_source_destroy (req->cancelled_idle);
|
||||
g_source_unref (req->cancelled_idle);
|
||||
req->cancelled_idle = NULL;
|
||||
}
|
||||
if (req->cancellable)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
|
||||
g_object_unref (req->cancellable);
|
||||
}
|
||||
|
||||
/* Now complete the result (assuming it wasn't already completed) */
|
||||
if (req->async_result)
|
||||
{
|
||||
g_simple_async_result_complete (req->async_result);
|
||||
g_object_unref (req->async_result);
|
||||
}
|
||||
|
||||
/* And free req */
|
||||
CloseHandle (req->event);
|
||||
req->free_func (req);
|
||||
g_slice_free (GWin32ResolverRequest, req);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
request_cancelled_idle (gpointer user_data)
|
||||
{
|
||||
GWin32ResolverRequest *req = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
g_source_unref (req->cancelled_idle);
|
||||
req->cancelled_idle = NULL;
|
||||
|
||||
g_cancellable_set_error_if_cancelled (req->cancellable, &error);
|
||||
g_simple_async_result_set_from_error (req->async_result, error);
|
||||
g_simple_async_result_complete (req->async_result);
|
||||
|
||||
g_object_unref (req->async_result);
|
||||
req->async_result = NULL;
|
||||
|
||||
/* request_completed will eventually be called to free req */
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
request_cancelled (GCancellable *cancellable,
|
||||
gpointer user_data)
|
||||
{
|
||||
GWin32ResolverRequest *req = user_data;
|
||||
|
||||
if (req->cancellable)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
|
||||
g_object_unref (req->cancellable);
|
||||
req->cancellable = NULL;
|
||||
}
|
||||
|
||||
/* We need to wait until main-loop-time to actually complete the
|
||||
* result; we don't use _complete_in_idle() here because we need to
|
||||
* keep track of the source so we can potentially cancel it before
|
||||
* it runs.
|
||||
*/
|
||||
req->cancelled_idle = g_idle_source_new ();
|
||||
g_source_set_callback (req->cancelled_idle,
|
||||
(GSourceFunc)request_cancelled_idle, req, NULL);
|
||||
g_source_attach (req->cancelled_idle, g_main_context_get_thread_default ());
|
||||
}
|
||||
|
||||
static DWORD WINAPI
|
||||
lookup_by_name_in_thread (LPVOID data)
|
||||
{
|
||||
GWin32ResolverRequest *req = data;
|
||||
|
||||
req->u.name.retval = getaddrinfo (req->u.name.name, NULL,
|
||||
&_g_resolver_addrinfo_hints,
|
||||
&req->u.name.res);
|
||||
SetEvent (req->event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
free_lookup_by_name (GWin32ResolverRequest *req)
|
||||
{
|
||||
g_free (req->u.name.name);
|
||||
if (req->u.name.res)
|
||||
freeaddrinfo (req->u.name.res);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_by_name_async (GResolver *resolver,
|
||||
const gchar *hostname,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GWin32ResolverRequest *req;
|
||||
|
||||
req = g_win32_resolver_request_new (resolver, free_lookup_by_name,
|
||||
cancellable, callback, user_data,
|
||||
lookup_by_name_async);
|
||||
req->u.name.name = g_strdup (hostname);
|
||||
|
||||
QueueUserWorkItem (lookup_by_name_in_thread, req, 0);
|
||||
}
|
||||
|
||||
static GList *
|
||||
lookup_by_name_finish (GResolver *resolver,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
GSimpleAsyncResult *simple;
|
||||
GWin32ResolverRequest *req;
|
||||
|
||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), NULL);
|
||||
simple = G_SIMPLE_ASYNC_RESULT (result);
|
||||
|
||||
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||
return _g_resolver_addresses_from_addrinfo (req->u.name.name, req->u.name.res,
|
||||
req->u.name.retval, error);
|
||||
}
|
||||
|
||||
|
||||
static DWORD WINAPI
|
||||
lookup_by_addresses_in_thread (LPVOID data)
|
||||
{
|
||||
GWin32ResolverRequest *req = data;
|
||||
|
||||
req->u.address.retval =
|
||||
getnameinfo ((struct sockaddr *)&req->u.address.addr,
|
||||
req->u.address.addrlen,
|
||||
req->u.address.namebuf, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
|
||||
SetEvent (req->event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
free_lookup_by_address (GWin32ResolverRequest *req)
|
||||
{
|
||||
g_object_unref (req->u.address.iaddr);
|
||||
g_free (req->u.address.namebuf);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_by_address_async (GResolver *resolver,
|
||||
GInetAddress *address,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GWin32ResolverRequest *req;
|
||||
|
||||
req = g_win32_resolver_request_new (resolver, free_lookup_by_address,
|
||||
cancellable, callback, user_data,
|
||||
lookup_by_address_async);
|
||||
|
||||
req->u.address.iaddr = g_object_ref (address);
|
||||
_g_resolver_address_to_sockaddr (address, &req->u.address.addr,
|
||||
&req->u.address.addrlen);
|
||||
req->u.address.namebuf = g_malloc (NI_MAXHOST);
|
||||
|
||||
QueueUserWorkItem (lookup_by_addresses_in_thread, req, 0);
|
||||
}
|
||||
|
||||
static char *
|
||||
lookup_by_address_finish (GResolver *resolver,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
GSimpleAsyncResult *simple;
|
||||
GWin32ResolverRequest *req;
|
||||
|
||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), NULL);
|
||||
simple = G_SIMPLE_ASYNC_RESULT (result);
|
||||
|
||||
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||
return _g_resolver_name_from_nameinfo (req->u.address.iaddr,
|
||||
req->u.address.namebuf,
|
||||
req->u.address.retval, error);
|
||||
}
|
||||
|
||||
|
||||
static DWORD WINAPI
|
||||
lookup_service_in_thread (LPVOID data)
|
||||
{
|
||||
GWin32ResolverRequest *req = data;
|
||||
|
||||
req->u.service.retval =
|
||||
DnsQuery_A (req->u.service.rrname, DNS_TYPE_SRV, DNS_QUERY_STANDARD,
|
||||
NULL, &req->u.service.results, NULL);
|
||||
SetEvent (req->event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
free_lookup_service (GWin32ResolverRequest *req)
|
||||
{
|
||||
g_free (req->u.service.rrname);
|
||||
if (req->u.service.results)
|
||||
DnsRecordListFree (req->u.service.results, DnsFreeRecordList);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_service_async (GResolver *resolver,
|
||||
const char *rrname,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GWin32ResolverRequest *req;
|
||||
|
||||
req = g_win32_resolver_request_new (resolver, free_lookup_service,
|
||||
cancellable, callback, user_data,
|
||||
lookup_service_async);
|
||||
req->u.service.rrname = g_strdup (rrname);
|
||||
|
||||
QueueUserWorkItem (lookup_service_in_thread, req, 0);
|
||||
}
|
||||
|
||||
static GList *
|
||||
lookup_service_finish (GResolver *resolver,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
GSimpleAsyncResult *simple;
|
||||
GWin32ResolverRequest *req;
|
||||
|
||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), NULL);
|
||||
simple = G_SIMPLE_ASYNC_RESULT (result);
|
||||
|
||||
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||
return _g_resolver_targets_from_DnsQuery (req->u.service.rrname,
|
||||
req->u.service.retval,
|
||||
req->u.service.results, error);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
g_win32_resolver_class_init (GWin32ResolverClass *win32_class)
|
||||
{
|
||||
GResolverClass *resolver_class = G_RESOLVER_CLASS (win32_class);
|
||||
|
||||
resolver_class->lookup_by_name_async = lookup_by_name_async;
|
||||
resolver_class->lookup_by_name_finish = lookup_by_name_finish;
|
||||
resolver_class->lookup_by_address_async = lookup_by_address_async;
|
||||
resolver_class->lookup_by_address_finish = lookup_by_address_finish;
|
||||
resolver_class->lookup_service_async = lookup_service_async;
|
||||
resolver_class->lookup_service_finish = lookup_service_finish;
|
||||
}
|
||||
|
||||
|
||||
/* Windows HANDLE GSource */
|
||||
|
||||
typedef struct {
|
||||
GSource source;
|
||||
GPollFD pollfd;
|
||||
} GWin32HandleSource;
|
||||
|
||||
static gboolean
|
||||
g_win32_handle_source_prepare (GSource *source,
|
||||
gint *timeout)
|
||||
{
|
||||
*timeout = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_win32_handle_source_check (GSource *source)
|
||||
{
|
||||
GWin32HandleSource *hsource = (GWin32HandleSource *)source;
|
||||
|
||||
return hsource->pollfd.revents;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_win32_handle_source_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
return (*callback) (user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
g_win32_handle_source_finalize (GSource *source)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
GSourceFuncs g_win32_handle_source_funcs = {
|
||||
g_win32_handle_source_prepare,
|
||||
g_win32_handle_source_check,
|
||||
g_win32_handle_source_dispatch,
|
||||
g_win32_handle_source_finalize
|
||||
};
|
||||
|
||||
static GSource *
|
||||
g_win32_handle_source_add (HANDLE handle,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GWin32HandleSource *hsource;
|
||||
GSource *source;
|
||||
|
||||
source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource));
|
||||
hsource = (GWin32HandleSource *)source;
|
||||
hsource->pollfd.fd = (gint)handle;
|
||||
hsource->pollfd.events = G_IO_IN;
|
||||
hsource->pollfd.revents = 0;
|
||||
g_source_add_poll (source, &hsource->pollfd);
|
||||
|
||||
g_source_set_callback (source, callback, user_data, NULL);
|
||||
g_source_attach (source, g_main_context_get_thread_default ());
|
||||
return source;
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __G_WIN32_RESOLVER_H__
|
||||
#define __G_WIN32_RESOLVER_H__
|
||||
|
||||
#include <gio/gthreadedresolver.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_WIN32_RESOLVER (g_win32_resolver_get_type ())
|
||||
#define G_WIN32_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_RESOLVER, GWin32Resolver))
|
||||
#define G_WIN32_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_WIN32_RESOLVER, GWin32ResolverClass))
|
||||
#define G_IS_WIN32_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_RESOLVER))
|
||||
#define G_IS_WIN32_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_RESOLVER))
|
||||
#define G_WIN32_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_RESOLVER, GWin32ResolverClass))
|
||||
|
||||
typedef struct {
|
||||
GThreadedResolver parent_instance;
|
||||
|
||||
} GWin32Resolver;
|
||||
|
||||
typedef struct {
|
||||
GThreadedResolverClass parent_class;
|
||||
|
||||
} GWin32ResolverClass;
|
||||
|
||||
GType g_win32_resolver_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_RESOLVER_H__ */
|
@ -1,15 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
include $(top_srcdir)/Makefile.decl
|
||||
|
||||
INCLUDES = $(config_h_INCLUDES)
|
||||
|
||||
noinst_LTLIBRARIES = libasyncns.la
|
||||
|
||||
libasyncns_la_SOURCES = \
|
||||
asyncns.c \
|
||||
asyncns.h \
|
||||
g-asyncns.h
|
||||
|
||||
libasyncns_la_LIBADD = $(NETWORK_LIBS)
|
||||
|
||||
EXTRA_DIST += README update.sh
|
@ -1,7 +0,0 @@
|
||||
The sources are derived from Lennart Poettering's libasyncns library:
|
||||
|
||||
http://0pointer.de/lennart/projects/libasyncns/
|
||||
|
||||
The 'update.sh' script in this directory, when pointed at
|
||||
the original sources updates the files in this directory
|
||||
to the new version
|
File diff suppressed because it is too large
Load Diff
@ -1,163 +0,0 @@
|
||||
#ifndef fooasyncnshfoo
|
||||
#define fooasyncnshfoo
|
||||
|
||||
/***
|
||||
This file is part of libasyncns.
|
||||
|
||||
Copyright 2005-2008 Lennart Poettering
|
||||
|
||||
libasyncns is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
libasyncns 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with libasyncns. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
/** \mainpage
|
||||
*
|
||||
* \section moo Method of operation
|
||||
*
|
||||
* To use libasyncns allocate an _g_asyncns_t object with
|
||||
* _g_asyncns_new(). This will spawn a number of worker threads (or processes, depending on what is available) which
|
||||
* are subsequently used to process the queries the controlling
|
||||
* program issues via _g_asyncns_getaddrinfo() and
|
||||
* _g_asyncns_getnameinfo(). Use _g_asyncns_free() to shut down the worker
|
||||
* threads/processes.
|
||||
*
|
||||
* Since libasyncns may fork off new processes you have to make sure that
|
||||
* your program is not irritated by spurious SIGCHLD signals.
|
||||
*/
|
||||
|
||||
/** \example asyncns-test.c
|
||||
* An example program */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** An opaque libasyncns session structure */
|
||||
typedef struct asyncns _g_asyncns_t;
|
||||
|
||||
/** An opaque libasyncns query structure */
|
||||
typedef struct _g_asyncns_query _g_asyncns_query_t;
|
||||
|
||||
/** Allocate a new libasyncns session with n_proc worker processes/threads */
|
||||
_g_asyncns_t* _g_asyncns_new(unsigned n_proc);
|
||||
|
||||
/** Free a libasyncns session. This destroys all attached
|
||||
* _g_asyncns_query_t objects automatically */
|
||||
void _g_asyncns_free(_g_asyncns_t *asyncns);
|
||||
|
||||
/** Return the UNIX file descriptor to select() for readability
|
||||
* on. Use this function to integrate libasyncns with your custom main
|
||||
* loop. */
|
||||
int _g_asyncns_fd(_g_asyncns_t *asyncns);
|
||||
|
||||
/** Process pending responses. After this function is called you can
|
||||
* get the next completed query object(s) using _g_asyncns_getnext(). If
|
||||
* block is non-zero wait until at least one response has been
|
||||
* processed. If block is zero, process all pending responses and
|
||||
* return. */
|
||||
int _g_asyncns_wait(_g_asyncns_t *asyncns, int block);
|
||||
|
||||
/** Issue a name to address query on the specified session. The
|
||||
* arguments are compatible with the ones of libc's
|
||||
* getaddrinfo(3). The function returns a new query object. When the
|
||||
* query is completed you may retrieve the results using
|
||||
* _g_asyncns_getaddrinfo_done().*/
|
||||
_g_asyncns_query_t* _g_asyncns_getaddrinfo(_g_asyncns_t *asyncns, const char *node, const char *service, const struct addrinfo *hints);
|
||||
|
||||
/** Retrieve the results of a preceding _g_asyncns_getaddrinfo()
|
||||
* call. Returns a addrinfo structure and a return value compatible
|
||||
* with libc's getaddrinfo(3). The query object q is destroyed by this
|
||||
* call and may not be used any further. Make sure to free the
|
||||
* returned addrinfo structure with _g_asyncns_freeaddrinfo() and not
|
||||
* libc's freeaddrinfo(3)! If the query is not completed yet EAI_AGAIN
|
||||
* is returned.*/
|
||||
int _g_asyncns_getaddrinfo_done(_g_asyncns_t *asyncns, _g_asyncns_query_t* q, struct addrinfo **ret_res);
|
||||
|
||||
/** Issue an address to name query on the specified session. The
|
||||
* arguments are compatible with the ones of libc's
|
||||
* getnameinfo(3). The function returns a new query object. When the
|
||||
* query is completed you may retrieve the results using
|
||||
* _g_asyncns_getnameinfo_done(). Set gethost (resp. getserv) to non-zero
|
||||
* if you want to query the hostname (resp. the service name). */
|
||||
_g_asyncns_query_t* _g_asyncns_getnameinfo(_g_asyncns_t *asyncns, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv);
|
||||
|
||||
/** Retrieve the results of a preceding _g_asyncns_getnameinfo()
|
||||
* call. Returns the hostname and the service name in ret_host and
|
||||
* ret_serv. The query object q is destroyed by this call and may not
|
||||
* be used any further. If the query is not completed yet EAI_AGAIN is
|
||||
* returned. */
|
||||
int _g_asyncns_getnameinfo_done(_g_asyncns_t *asyncns, _g_asyncns_query_t* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen);
|
||||
|
||||
/** Issue a resolver query on the specified session. The arguments are
|
||||
* compatible with the ones of libc's res_query(3). The function returns a new
|
||||
* query object. When the query is completed you may retrieve the results using
|
||||
* _g_asyncns_res_done(). */
|
||||
_g_asyncns_query_t* _g_asyncns_res_query(_g_asyncns_t *asyncns, const char *dname, int class, int type);
|
||||
|
||||
/** Issue an resolver query on the specified session. The arguments are
|
||||
* compatible with the ones of libc's res_search(3). The function returns a new
|
||||
* query object. When the query is completed you may retrieve the results using
|
||||
* _g_asyncns_res_done(). */
|
||||
_g_asyncns_query_t* _g_asyncns_res_search(_g_asyncns_t *asyncns, const char *dname, int class, int type);
|
||||
|
||||
/** Retrieve the results of a preceding _g_asyncns_res_query() or
|
||||
* _g_asyncns_res_search call. The query object q is destroyed by this
|
||||
* call and may not be used any further. Returns a pointer to the
|
||||
* answer of the res_query call. If the query is not completed yet
|
||||
* -EAGAIN is returned, on failure -errno is returned, otherwise the
|
||||
* length of answer is returned. Make sure to free the answer is a
|
||||
* call to _g_asyncns_freeanswer(). */
|
||||
int _g_asyncns_res_done(_g_asyncns_t *asyncns, _g_asyncns_query_t* q, unsigned char **answer);
|
||||
|
||||
/** Return the next completed query object. If no query has been
|
||||
* completed yet, return NULL. Please note that you need to run
|
||||
* _g_asyncns_wait() before this function will return sensible data. */
|
||||
_g_asyncns_query_t* _g_asyncns_getnext(_g_asyncns_t *asyncns);
|
||||
|
||||
/** Return the number of query objects (completed or not) attached to
|
||||
* this session */
|
||||
int _g_asyncns_getnqueries(_g_asyncns_t *asyncns);
|
||||
|
||||
/** Cancel a currently running query. q is is destroyed by this call
|
||||
* and may not be used any futher. */
|
||||
void _g_asyncns_cancel(_g_asyncns_t *asyncns, _g_asyncns_query_t* q);
|
||||
|
||||
/** Free the addrinfo structure as returned by
|
||||
* _g_asyncns_getaddrinfo_done(). Make sure to use this functions instead
|
||||
* of the libc's freeaddrinfo()! */
|
||||
void _g_asyncns_freeaddrinfo(struct addrinfo *ai);
|
||||
|
||||
/** Free the answer data as returned by _g_asyncns_res_done().*/
|
||||
void _g_asyncns_freeanswer(unsigned char *answer);
|
||||
|
||||
/** Returns non-zero when the query operation specified by q has been completed */
|
||||
int _g_asyncns_isdone(_g_asyncns_t *asyncns, _g_asyncns_query_t*q);
|
||||
|
||||
/** Assign some opaque userdata with a query object */
|
||||
void _g_asyncns_setuserdata(_g_asyncns_t *asyncns, _g_asyncns_query_t *q, void *userdata);
|
||||
|
||||
/** Return userdata assigned to a query object. Use
|
||||
* _g_asyncns_setuserdata() to set this data. If no data has been set
|
||||
* prior to this call it returns NULL. */
|
||||
void* _g_asyncns_getuserdata(_g_asyncns_t *asyncns, _g_asyncns_query_t *q);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,33 +0,0 @@
|
||||
/* GLIB - Library of useful routines for C programming
|
||||
* Copyright (C) 2008 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __G_ASYNCNS_H__
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* We want to build the fork-based version, not the threaded version. */
|
||||
#undef HAVE_PTHREAD
|
||||
|
||||
/* asyncns.c removed this for #580301, but that appears to have been wrong */
|
||||
#if HAVE_ARPA_NAMESER_COMPAT_H
|
||||
#include <arpa/nameser_compat.h>
|
||||
#endif
|
||||
|
||||
#include "asyncns.h"
|
||||
|
||||
#endif
|
@ -1,20 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if test $# = 1 ; then
|
||||
ORIGINAL=$1
|
||||
else
|
||||
echo "Usage: update.sh /path/to/libasyncns" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -f $ORIGINAL/libasyncns/asyncns.c ; then : ; else
|
||||
echo "Usage: update.sh /path/to/libasyncns" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in asyncns.c asyncns.h ; do
|
||||
sed -e 's/\([^a-z]\)asyncns_/\1_g_asyncns_/g' \
|
||||
-e 's/^asyncns_/_g_asyncns_/' \
|
||||
-e 's/<config\.h>/"g-asyncns\.h"/' \
|
||||
$ORIGINAL/libasyncns/$i > $i
|
||||
done
|
@ -40,13 +40,11 @@ static int nlookups = 0;
|
||||
static void G_GNUC_NORETURN
|
||||
usage (void)
|
||||
{
|
||||
fprintf (stderr, "Usage: resolver [-t] [-s] [hostname | IP | service/protocol/domain ] ...\n");
|
||||
fprintf (stderr, " resolver [-t] [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
|
||||
fprintf (stderr, " Use -t to enable threading.\n");
|
||||
fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
|
||||
fprintf (stderr, " resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
|
||||
fprintf (stderr, " Use -s to do synchronous lookups.\n");
|
||||
fprintf (stderr, " Both together will result in simultaneous lookups in multiple threads\n");
|
||||
fprintf (stderr, " Use -c NUMBER(and only a single resolvable argument) to test GSocketConnectable.\n");
|
||||
fprintf (stderr, " The given NUMBER determines how often the connectable will be enumerated.\n");
|
||||
fprintf (stderr, " Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
|
||||
fprintf (stderr, " The given NUMBER determines how many times the connectable will be enumerated.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
@ -197,22 +195,13 @@ lookup_thread (gpointer arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
start_threaded_lookups (char **argv, int argc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
g_thread_create (lookup_thread, argv[i], FALSE, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
start_sync_lookups (char **argv, int argc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
lookup_one_sync (argv[i]);
|
||||
g_thread_create (lookup_thread, argv[i], FALSE, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -438,24 +427,19 @@ async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
gboolean threaded = FALSE, synchronous = FALSE;
|
||||
gboolean synchronous = FALSE;
|
||||
guint connectable_count = 0;
|
||||
#ifdef G_OS_UNIX
|
||||
GIOChannel *chan;
|
||||
guint watch;
|
||||
#endif
|
||||
|
||||
/* We can't use GOptionContext because we use the arguments to
|
||||
* decide whether or not to call g_thread_init().
|
||||
*/
|
||||
g_type_init ();
|
||||
|
||||
/* FIXME: use GOptionContext */
|
||||
while (argc >= 2 && argv[1][0] == '-')
|
||||
{
|
||||
if (!strcmp (argv[1], "-t"))
|
||||
{
|
||||
g_thread_init (NULL);
|
||||
threaded = TRUE;
|
||||
}
|
||||
else if (!strcmp (argv[1], "-s"))
|
||||
if (!strcmp (argv[1], "-s"))
|
||||
synchronous = TRUE;
|
||||
else if (!strcmp (argv[1], "-c"))
|
||||
{
|
||||
@ -469,7 +453,6 @@ main (int argc, char **argv)
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
g_type_init ();
|
||||
|
||||
if (argc < 2 || (argc > 2 && connectable_count))
|
||||
usage ();
|
||||
@ -504,9 +487,7 @@ main (int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (threaded && synchronous)
|
||||
start_threaded_lookups (argv + 1, argc - 1);
|
||||
else if (synchronous)
|
||||
if (synchronous)
|
||||
start_sync_lookups (argv + 1, argc - 1);
|
||||
else
|
||||
start_async_lookups (argv + 1, argc - 1);
|
||||
|
Loading…
Reference in New Issue
Block a user