cardif_windows_wmi_async.c

来自「linux 下通过802.1认证的安装包」· C语言 代码 · 共 509 行

C
509
字号
/**
 * Interface to Windows WMI to check for WMI method calls completion status.
 *
 * Licensed under a dual GPL/BSD license.  (See LICENSE file for more info.)
 *
 * \file cardif_windows_wmi_async.c
 *
 * \author chris@open1x.org
 *
 * \todo Implement calls to get the list of DNS servers.
 *
 * $Id: cardif_windows_wmi_async.c,v 1.1.2.16 2008/01/21 22:51:48 chessing Exp $
 * $Date: 2008/01/21 22:51:48 $
 **/

#define _WIN32_DCOM

// We need to define COBJMACROS so that we can make the C calls
// to the IWbem* interfaces.
#ifndef COBJMACROS
#define COBJMACROS
#endif 

#include <wbemidl.h>

#include "../../xsup_common.h"
#include "../../../lib/libxsupconfig/xsupconfig.h"
#include "../../../lib/libxsupconfig/xsupconfig_structs.h"
#include "../../context.h"
#include "../../xsup_debug.h"
#include "../../xsup_err.h"
#include "cardif_windows.h"
#include "../../event_core_win.h"
#include "../../ipc_events.h"
#include "../../ipc_events_index.h"
#include "cardif_windows_wmi.h"
#include "cardif_windows_wmi_async.h"


#define STALE_REQUEST   0x01

typedef struct _execmethod_check {
	char *name;                ///< The name of the command we are waiting for completion on.
	context *ctx;              ///< The context that triggered this event.
	IWbemCallResult *rescheck; ///< The handle to check the result of the call.
	void (*callback)(char *name, context *ctx, int err);   ///< A call to make when the call completes.
	uint8_t flags;             ///< Flags for this instance of the call.

	struct _execmethod_check *next;
} execmethod_check;

execmethod_check *method_head = NULL;

/**
 * \brief Dump a list of all of the async callbacks that are currently enqueued.
 **/
void cardif_windows_wmi_async_dump()
{
	execmethod_check *cur = NULL;

	cur = method_head;

	debug_printf(DEBUG_INT, "------------  WMI Callbacks --------------\n");
	while (cur != NULL)
	{
		debug_printf(DEBUG_INT, "Name = %s (0x%x)\n", cur->name, &cur);
		cur = cur->next;
	}

	debug_printf(DEBUG_INT, "------------------------------------------\n");
}

/**
 * \brief Add an execmethod_check node to the list.
 *
 * @param[in] name   A text name for the method that we are checking, used only in 
 *                   debugging and logging output.
 * @param[in] intname   The OS specific interface name that this call is operating against.
 *                      If the call doesn't operate on an interface, this should be NULL.
 * @param[in] rescheck   A IWbemCallResult value that contains the information we need to use
 *                       in order to verify that we are attempting to get an IP address.
 * @param[in] callback   A callback that can be used to generate an event when the call completes.
 *                       An error code is passed in so that the callback can also generate an
 *                       error if needed.
 *
 * \retval 0 on success
 * \retval -1 on failure
 **/
int cardif_windows_wmi_async(char *name, context *ctx, IWbemCallResult *rescheck, 
							 void *callback)
{
	execmethod_check *cur = NULL, *temp = NULL;

	cur = Malloc(sizeof(execmethod_check));
	if (cur == NULL)
	{
		debug_printf(DEBUG_NORMAL, "Couldn't allocate memory to create WMI async check funcion!\n");
		return -1;
	}

	cur->name = strdup(name);
	cur->ctx = ctx;
	cur->rescheck = rescheck;
	cur->callback = callback;
	cur->flags = 0x00;
	cur->next = NULL;

	if (method_head == NULL)
	{
		method_head = cur;
	}
	else
	{
		temp = method_head;

		while (temp->next != NULL)
		{
			temp = temp->next;
		}

		temp->next = cur;
	}

	return 0;  // Success!
}

/**
 * \brief Remove a node from the check list.
 *
 * @param[in] rm   The node that we want to locate and remove from the list.
 **/
static void cardif_windows_wmi_async_remove_node(execmethod_check **rm)
{
	execmethod_check *cur = NULL;

	if (method_head == NULL)
	{
		debug_printf(DEBUG_INT, "Attempt to remove a node from the WMI check list when "
				"there are currently no nodes in the list!?\n");
		return;
	}

	// The line below can be useful in debugging this code.  But, leave it commented out if you don't need it.
//	cardif_windows_wmi_async_dump();

	if (method_head == (*rm))
	{
		method_head = method_head->next;
	}
	else
	{
		cur = method_head;

		while ((cur != NULL) && (cur->next != (*rm)))
			cur = cur->next;

		if (cur == NULL)
		{
			debug_printf(DEBUG_NORMAL, "Unable to locate the WMI check list node we were asked to delete!?\n");
			return;
		}

		// Otherwise, remove it.
		cur->next = cur->next->next;
	}

	FREE((*rm)->name);
	IWbemCallResult_Release((*rm)->rescheck);
	FREE((*rm));
	rm = NULL;
}

/**
 * \brief Check all of the existing commands in the list to see if any have completed.
 **/
void cardif_windows_wmi_async_check()
{
	LONG result;
	execmethod_check *cur = NULL, *temp = NULL;
	HRESULT hr;
	VARIANT varReturnValue;
	IWbemClassObject *pclsobj = NULL;

	cur = method_head;

	while (cur != NULL)
	{
		temp = cur->next;

		if ((cur != NULL) && (cur->rescheck != NULL))
		{
			hr = IWbemCallResult_GetCallStatus(cur->rescheck, 0, &result);
			if (FAILED(hr))
			{
				if (hr != WBEM_S_TIMEDOUT)
				{
					// ACK!  We failed in a bad way!
					debug_printf(DEBUG_NORMAL, "Execution of method '%s' failed! (Result : %x)\n", 
							cur->name, hr);	

					ipc_events_error(NULL, IPC_EVENT_ERROR_WMI_ASYNC_FAILED, cur->name);
					return;
				}

				// Otherwise, it just means we aren't ready yet.
			}
			else
			{
				if (hr == WBEM_S_NO_ERROR)
				{
					debug_printf(DEBUG_INT, "Call %s completed!  Return value : %d\n", 
						cur->name, result);

					hr = IWbemCallResult_GetResultObject(cur->rescheck, 0, &pclsobj);
					if (FAILED(hr))
					{
						printf("Couldn't get result object!\n");
						cardif_windows_wmi_async_remove_node(&cur);
						return;
					}

					if (pclsobj == NULL)
					{
						debug_printf(DEBUG_INT, "No return object.  Trying later!\n");
						cardif_windows_wmi_async_remove_node(&cur);
						return;
					}

					hr = IWbemClassObject_Get(pclsobj, L"ReturnValue", 0, 
						&varReturnValue, NULL, 0);  
					if (FAILED(hr))
					{
						debug_printf(DEBUG_NORMAL, "%s result object didn't have a valid value!\n", cur->name);
						cardif_windows_wmi_async_remove_node(&cur);
						return;
					}

					if (cur->callback != NULL)
					{
						if (cur->flags == 0x01) debug_printf(DEBUG_INT, "!!!!!! Stale Response !!!!!!\n");
						cur->callback(cur->name, cur->ctx, varReturnValue.intVal);
					}	

					VariantClear(&varReturnValue);

					cardif_windows_wmi_async_remove_node(&cur);
				}
			}
		}
		cur = temp;
	}
}

/**
 * \brief Clean up any calls that might be hanging around in memory when the supplicant 
 *        terminates.
 **/
void cardif_windows_wmi_async_cleanup()
{
	execmethod_check *cur = NULL, *next = NULL;

	if (method_head == NULL) return;  // Everything is clean.

	cur = method_head;

	while (cur != NULL)
	{
		next = cur->next;

		cardif_windows_wmi_async_remove_node(&cur);

		cur = next;
	}
}

/**
 * \brief The callback that will be called when a DHCP release call completes.
 *
 * @param[in] name   The description of the call that triggered this callback.
 * @param[in] ctx   The context that caused this event.
 * @param[in] err   The error code that was returned from the call.
 **/
void cardif_windows_wmi_async_dhcp_release_renew_callback(char *name, context *ctx, int err)
{
	if (err != 0)
	{
		debug_printf(DEBUG_NORMAL, "Error attempting to release the current DHCP address.\n");
	}

	cardif_windows_wmi_renew_dhcp(ctx);
}

/**
 * \brief The callback that will be called when a DHCP renew call completes.
 *
 * If we have static DNS values set, we need to attempt to set them here.  We 
 * should also trigger an event to let the UI know that DHCP has completed, and
 * an IP address should be available now.
 *
 * @param[in] name   The description of the call that triggered this callback.
 * @param[in] ctx   The context that caused this event.
 * @param[in] err   The error code that was returned from the call.
 **/
void cardif_windows_wmi_async_dhcp_renew_callback(char *name, context *ctx, int err)
{
	int retval = 0;
	char *ipaddr = NULL;

	if (err == 82)
	{
		// If we get an error 82, then it means that Windows "couldn't renew" the DHCP
		// address.  But, it isn't necessarily an indication that getting an IP address failed.
		//  It is possible that there was an existing lease already available, or that a new
		// lease was acquired.  So, we want to check the IP address that we have, and see if
		// if is 0.0.0.0, or one of the link local addresses 169.254/16.  If it is, then we will
		// throw an error.
		ipaddr = cardif_windows_wmi_get_ip_utf8(ctx);
		if (ipaddr == NULL)
		{
			debug_printf(DEBUG_NORMAL, "No IP address is set!  DHCP failed!\n");
			ipc_events_ui(ctx, IPC_EVENT_ERROR_CANT_RENEW_DHCP, NULL);
			return;
		}

		if ((strcmp(ipaddr, "0.0.0.0") == 0) || (strncmp(ipaddr, "169.254", 7) == 0))
		{
			debug_printf(DEBUG_NORMAL, "Unable to acquire an IP address via DHCP.  (Address is %s.)\n", ipaddr);
			ipc_events_ui(ctx, IPC_EVENT_ERROR_CANT_RENEW_DHCP, NULL);
			FREE(ipaddr);
			return;
		}

		FREE(ipaddr);

		// Otherwise, we have a valid address, but log a message so the user has an idea of what is going on.
		debug_printf(DEBUG_NORMAL, "The attempt to renew the DHCP lease for interface '%s' generated an error.  However, you have a valid IP address.  Windows is probably using an address that was cached and still indicated it had time available on its lease.\n", ctx->desc);
	}
	else if (err != 0)
	{
		debug_printf(DEBUG_NORMAL, "Failed to renew DHCP address. (Error : %d)\n", err);
		ipc_events_ui(ctx, IPC_EVENT_ERROR_CANT_RENEW_DHCP, NULL);
	}
	else
	{
		// Notify the UI that we changed IP addresses.
		ipc_events_ui(ctx, IPC_EVENT_UI_IP_ADDRESS_SET, NULL);

		debug_printf(DEBUG_NORMAL, "Interface '%s' had it's IP address set.\n", ctx->desc);
	}

	// We want to set this stuff no matter what.  It is possible the stuff above got foobared, and the address is still
	// valid even when it claims not to be.  So, make sure we do an update.

	// Check and see if we have static DNS values to set.
	if ((ctx->conn != NULL) && ((ctx->conn->ip.dns1 != NULL) || (ctx->conn->ip.dns2 != NULL) ||
		(ctx->conn->ip.dns3 != NULL)))
	{
		retval = cardif_windows_wmi_set_dns_servers(ctx, ctx->conn->ip.dns1, ctx->conn->ip.dns2,
													ctx->conn->ip.dns3);
		if (retval == 94)
		{
			debug_printf(DEBUG_INT, "Failed on first attempt to set static DNS servers, trying again.\n");

			retval = cardif_windows_wmi_set_dns_servers(ctx, ctx->conn->ip.dns1, ctx->conn->ip.dns2,
				ctx->conn->ip.dns3);
			if (retval != 0)
			{
				debug_printf(DEBUG_NORMAL, "Failed to set static DNS servers.  Error was %d.\n", retval);
				return;
			}
		}
	}

	if ((ctx->conn != NULL) && (ctx->conn->ip.search_domain != NULL))
	{
		debug_printf(DEBUG_INT, "Setting search domain.\n");
		retval = cardif_windows_wmi_set_dns_domain(ctx, ctx->conn->ip.search_domain);
		if (retval == 94)
		{
			debug_printf(DEBUG_INT, "First attempt failed, trying again.\n");
			retval = cardif_windows_wmi_set_dns_domain(ctx, ctx->conn->ip.search_domain);
			if (retval != 0)
			{
				debug_printf(DEBUG_NORMAL, "Couldn't set search domain.  Error was %d.\n", retval);
				return;
			}
		} else if (retval != 0)
		{
			debug_printf(DEBUG_NORMAL, "Unable to set DNS search domain.  Error was %d.\n", retval);
			return;
		}
	}
}

/**
 * \brief The callback that will be called when a set static IP call completes.
 *
 * @param[in] name   The description of the call that triggered this callback.
 * @param[in] ctx   The context that caused this event.
 * @param[in] err   The error code that was returned from the call.
 **/
void cardif_windows_wmi_async_static_ip_callback(char *name, context *ctx, int err)
{
	int retval = 0;

	if (err != 0)
	{
		debug_printf(DEBUG_NORMAL, "Failed to set static IP address.\n");
		return;
	}

	// Notify the UI that we changed IP addresses.
	ipc_events_ui(ctx, IPC_EVENT_UI_IP_ADDRESS_SET, NULL);

	debug_printf(DEBUG_INT, "Setting DNS server(s).\n");
	retval = cardif_windows_wmi_set_dns_servers(ctx, ctx->conn->ip.dns1, ctx->conn->ip.dns2,
				ctx->conn->ip.dns3);
	if (retval == 94)
	{
		debug_printf(DEBUG_INT, "First attempt failed, trying again.\n");
		retval = cardif_windows_wmi_set_dns_servers(ctx, ctx->conn->ip.dns1, ctx->conn->ip.dns2,
			ctx->conn->ip.dns3);
		if (retval != 0)
		{
			debug_printf(DEBUG_NORMAL, "Couldn't set DNS servers!  Error was %d.\n", retval);
			return;
		}
	}
	else if (retval != 0)
	{
		debug_printf(DEBUG_NORMAL, "Unable to set DNS servers!  Error was %d.\n", retval);
		return;
	}

	debug_printf(DEBUG_INT, "Setting search domain.\n");
	retval = cardif_windows_wmi_set_dns_domain(ctx, ctx->conn->ip.search_domain);
	if (retval == 94)
	{
		debug_printf(DEBUG_INT, "First attempt failed, trying again.\n");
		retval = cardif_windows_wmi_set_dns_domain(ctx, ctx->conn->ip.search_domain);
		if (retval != 0)
		{
			debug_printf(DEBUG_NORMAL, "Couldn't set search domain.  Error was %d.\n", retval);
			return;
		}
	} else if (retval != 0)
	{
		debug_printf(DEBUG_NORMAL, "Unable to set DNS search domain.  Error was %d.\n", retval);
		return;
	}

	debug_printf(DEBUG_INT, "Setting default GW.\n");
	retval = cardif_windows_wmi_set_static_gw(ctx, ctx->conn->ip.gateway);
	if (retval == 94)
	{
		debug_printf(DEBUG_INT, "First attempt failed, trying again.\n");
		retval = cardif_windows_wmi_set_static_gw(ctx, ctx->conn->ip.gateway);
		if (retval != 0)
		{
			debug_printf(DEBUG_NORMAL, "Couldn't set GW.  Error was %d.\n", retval);
			return;
		}
	} else if (retval != 0)
	{	
		debug_printf(DEBUG_NORMAL, "Unable to set default GW.  Error was %d.\n", retval);
		return;
	}
}

/**
 * \brief Clear out any waiting events in our list.  
 *
 * @param[in] ctx   The context that we want to clear events for.
 **/
void cardif_windows_wmi_async_clear_by_ctx(context *ctx)
{
	execmethod_check *cur = NULL, *prev = NULL;

	if (method_head == NULL) return;  // Everything is clean.

	cur = method_head;
	prev = NULL;

	while (cur != NULL)
	{
		if (cur->ctx == ctx)
		{
			if (cur == method_head) 
			{
				prev = method_head->next;
				cardif_windows_wmi_async_remove_node(&cur);
				method_head = prev;
				cur = prev;
			}
			else
			{
				prev->next = cur->next;
				cardif_windows_wmi_async_remove_node(&cur);
				cur = prev->next;
			}
		}
		else
		{
			prev = cur;
			cur = cur->next;
		}
	}
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?