wine/programs/plugplay/main.c

261 lines
7.1 KiB
C
Raw Normal View History

/*
* Copyright 2011 Hans Leidekker for CodeWeavers
*
* 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "winsvc.h"
#include "wine/debug.h"
#include "wine/list.h"
#include "plugplay.h"
WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
static WCHAR plugplayW[] = {'P','l','u','g','P','l','a','y',0};
static SERVICE_STATUS_HANDLE service_handle;
static HANDLE stop_event;
void __RPC_FAR * __RPC_USER MIDL_user_allocate( SIZE_T len )
{
return malloc( len );
}
void __RPC_USER MIDL_user_free( void __RPC_FAR *ptr )
{
free( ptr );
}
static CRITICAL_SECTION plugplay_cs;
static CRITICAL_SECTION_DEBUG plugplay_cs_debug =
{
0, 0, &plugplay_cs,
{ &plugplay_cs_debug.ProcessLocksList, &plugplay_cs_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": plugplay_cs") }
};
static CRITICAL_SECTION plugplay_cs = { &plugplay_cs_debug, -1, 0, 0, 0, 0 };
static struct list listener_list = LIST_INIT(listener_list);
struct listener
{
struct list entry;
struct list events;
CONDITION_VARIABLE cv;
};
struct event
{
struct list entry;
DWORD code;
BYTE *data;
unsigned int size;
};
static void destroy_listener( struct listener *listener )
{
struct event *event, *next;
EnterCriticalSection( &plugplay_cs );
list_remove( &listener->entry );
LeaveCriticalSection( &plugplay_cs );
LIST_FOR_EACH_ENTRY_SAFE(event, next, &listener->events, struct event, entry)
{
MIDL_user_free( event->data );
list_remove( &event->entry );
free( event );
}
free( listener );
}
void __RPC_USER plugplay_rpc_handle_rundown( plugplay_rpc_handle handle )
{
destroy_listener( handle );
}
plugplay_rpc_handle __cdecl plugplay_register_listener(void)
{
struct listener *listener;
if (!(listener = calloc( 1, sizeof(*listener) )))
return NULL;
list_init( &listener->events );
InitializeConditionVariable( &listener->cv );
EnterCriticalSection( &plugplay_cs );
list_add_tail( &listener_list, &listener->entry );
LeaveCriticalSection( &plugplay_cs );
return listener;
}
DWORD __cdecl plugplay_get_event( plugplay_rpc_handle handle, BYTE **data, unsigned int *size )
{
struct listener *listener = handle;
struct event *event;
struct list *entry;
DWORD ret;
EnterCriticalSection( &plugplay_cs );
while (!(entry = list_head( &listener->events )))
SleepConditionVariableCS( &listener->cv, &plugplay_cs, INFINITE );
event = LIST_ENTRY(entry, struct event, entry);
list_remove( &event->entry );
LeaveCriticalSection( &plugplay_cs );
ret = event->code;
*data = event->data;
*size = event->size;
free( event );
return ret;
}
void __cdecl plugplay_unregister_listener( plugplay_rpc_handle handle )
{
destroy_listener( handle );
}
void __cdecl plugplay_send_event( DWORD code, const BYTE *data, unsigned int size )
{
struct listener *listener;
struct event *event;
BroadcastSystemMessageW( BSF_FORCEIFHUNG | BSF_QUERY, NULL, WM_DEVICECHANGE, code, (LPARAM)data );
EnterCriticalSection( &plugplay_cs );
LIST_FOR_EACH_ENTRY(listener, &listener_list, struct listener, entry)
{
if (!(event = malloc( sizeof(*event) )))
break;
if (!(event->data = malloc( size )))
{
free( event );
break;
}
event->code = code;
memcpy( event->data, data, size );
event->size = size;
list_add_tail( &listener->events, &event->entry );
WakeConditionVariable( &listener->cv );
}
LeaveCriticalSection( &plugplay_cs );
}
static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
{
SERVICE_STATUS status;
status.dwServiceType = SERVICE_WIN32;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
switch(ctrl)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
WINE_TRACE( "shutting down\n" );
status.dwCurrentState = SERVICE_STOP_PENDING;
status.dwControlsAccepted = 0;
SetServiceStatus( service_handle, &status );
SetEvent( stop_event );
return NO_ERROR;
default:
WINE_FIXME( "got service ctrl %x\n", ctrl );
status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( service_handle, &status );
return NO_ERROR;
}
}
static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
{
unsigned char endpoint[] = "\\pipe\\wine_plugplay";
unsigned char protseq[] = "ncalrpc";
SERVICE_STATUS status;
RPC_STATUS err;
WINE_TRACE( "starting service\n" );
if ((err = RpcServerUseProtseqEpA( protseq, 0, endpoint, NULL )))
{
ERR("RpcServerUseProtseqEp() failed, error %u\n", err);
return;
}
if ((err = RpcServerRegisterIf( plugplay_v0_0_s_ifspec, NULL, NULL )))
{
ERR("RpcServerRegisterIf() failed, error %u\n", err);
return;
}
if ((err = RpcServerListen( 1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE )))
{
ERR("RpcServerListen() failed, error %u\n", err);
return;
}
stop_event = CreateEventW( NULL, TRUE, FALSE, NULL );
service_handle = RegisterServiceCtrlHandlerExW( plugplayW, service_handler, NULL );
if (!service_handle)
return;
status.dwServiceType = SERVICE_WIN32;
status.dwCurrentState = SERVICE_RUNNING;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = 10000;
SetServiceStatus( service_handle, &status );
WaitForSingleObject( stop_event, INFINITE );
RpcMgmtStopServerListening( NULL );
RpcServerUnregisterIf( plugplay_v0_0_s_ifspec, NULL, TRUE );
RpcMgmtWaitServerListen();
status.dwCurrentState = SERVICE_STOPPED;
status.dwControlsAccepted = 0;
SetServiceStatus( service_handle, &status );
WINE_TRACE( "service stopped\n" );
}
int __cdecl wmain( int argc, WCHAR *argv[] )
{
static const SERVICE_TABLE_ENTRYW service_table[] =
{
{ plugplayW, ServiceMain },
{ NULL, NULL }
};
StartServiceCtrlDispatcherW( service_table );
return 0;
}