wine/programs/sc/sc.c

394 lines
13 KiB
C
Raw Normal View History

/*
* Copyright 2010 Hans Leidekker
*
* 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <winsvc.h>
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(sc);
static BOOL parse_string_param( int argc, const WCHAR *argv[], unsigned int *index,
const WCHAR *param_name, size_t name_len, const WCHAR **out )
{
if (!wcsnicmp( argv[*index], param_name, name_len ))
{
if (argv[*index][name_len])
{
*out = &argv[*index][name_len];
return TRUE;
}
else if (*index < argc - 1)
{
*index += 1;
*out = argv[*index];
return TRUE;
}
}
return FALSE;
}
struct create_params
{
const WCHAR *displayname;
const WCHAR *binpath;
const WCHAR *group;
const WCHAR *depend;
const WCHAR *obj;
const WCHAR *password;
DWORD type;
DWORD start;
DWORD error;
BOOL tag;
};
static BOOL parse_create_params( int argc, const WCHAR *argv[], struct create_params *cp )
{
unsigned int i;
cp->displayname = NULL;
cp->type = SERVICE_WIN32_OWN_PROCESS;
cp->start = SERVICE_DEMAND_START;
cp->error = SERVICE_ERROR_NORMAL;
cp->binpath = NULL;
cp->group = NULL;
cp->tag = FALSE;
cp->depend = NULL;
cp->obj = NULL;
cp->password = NULL;
#define PARSE(x, y) parse_string_param( (argc), (argv), &(i), (x), ARRAY_SIZE(x) - 1, (y) )
for (i = 0; i < argc; i++)
{
const WCHAR *tag, *type, *start, *error;
if (PARSE( L"displayname=", &cp->displayname )) continue;
if (PARSE( L"binpath=", &cp->binpath )) continue;
if (PARSE( L"group=", &cp->group )) continue;
if (PARSE( L"depend=", &cp->depend )) continue;
if (PARSE( L"obj=", &cp->obj )) continue;
if (PARSE( L"password=", &cp->password )) continue;
if (PARSE( L"tag=", &tag ))
{
if (!wcsicmp( tag, L"yes" ))
{
WINE_FIXME("tag argument not supported\n");
cp->tag = TRUE;
}
continue;
}
if (PARSE( L"type=", &type ))
{
if (!wcsicmp( type, L"own" )) cp->type = SERVICE_WIN32_OWN_PROCESS;
else if (!wcsicmp( type, L"share" )) cp->type = SERVICE_WIN32_SHARE_PROCESS;
else if (!wcsicmp( type, L"kernel" )) cp->type = SERVICE_KERNEL_DRIVER;
else if (!wcsicmp( type, L"filesys" )) cp->type = SERVICE_FILE_SYSTEM_DRIVER;
else if (!wcsicmp( type, L"rec" )) cp->type = SERVICE_RECOGNIZER_DRIVER;
else if (!wcsicmp( type, L"interact" )) cp->type |= SERVICE_INTERACTIVE_PROCESS;
continue;
}
if (PARSE( L"start=", &start ))
{
if (!wcsicmp( start, L"boot" )) cp->start = SERVICE_BOOT_START;
else if (!wcsicmp( start, L"system" )) cp->start = SERVICE_SYSTEM_START;
else if (!wcsicmp( start, L"auto" )) cp->start = SERVICE_AUTO_START;
else if (!wcsicmp( start, L"demand" )) cp->start = SERVICE_DEMAND_START;
else if (!wcsicmp( start, L"disabled" )) cp->start = SERVICE_DISABLED;
continue;
}
if (PARSE( L"error=", &error ))
{
if (!wcsicmp( error, L"normal" )) cp->error = SERVICE_ERROR_NORMAL;
else if (!wcsicmp( error, L"severe" )) cp->error = SERVICE_ERROR_SEVERE;
else if (!wcsicmp( error, L"critical" )) cp->error = SERVICE_ERROR_CRITICAL;
else if (!wcsicmp( error, L"ignore" )) cp->error = SERVICE_ERROR_IGNORE;
continue;
}
}
#undef PARSE
if (!cp->binpath) return FALSE;
return TRUE;
}
static BOOL parse_failure_actions( const WCHAR *arg, SERVICE_FAILURE_ACTIONSW *fa )
{
unsigned int i, count;
WCHAR *actions, *p;
actions = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( arg ) + 1) * sizeof(WCHAR) );
if (!actions) return FALSE;
lstrcpyW( actions, arg );
for (p = actions, count = 0; *p; p++)
{
if (*p == '/')
{
count++;
*p = 0;
}
}
count = count / 2 + 1;
fa->cActions = count;
fa->lpsaActions = HeapAlloc( GetProcessHeap(), 0, fa->cActions * sizeof(SC_ACTION) );
if (!fa->lpsaActions)
{
HeapFree( GetProcessHeap(), 0, actions );
return FALSE;
}
p = actions;
for (i = 0; i < count; i++)
{
if (!wcsicmp( p, L"run" )) fa->lpsaActions[i].Type = SC_ACTION_RUN_COMMAND;
else if (!wcsicmp( p, L"restart" )) fa->lpsaActions[i].Type = SC_ACTION_RESTART;
else if (!wcsicmp( p, L"reboot" )) fa->lpsaActions[i].Type = SC_ACTION_REBOOT;
else fa->lpsaActions[i].Type = SC_ACTION_NONE;
p += lstrlenW( p ) + 1;
fa->lpsaActions[i].Delay = wcstol( p, NULL, 10 );
p += lstrlenW( p ) + 1;
}
HeapFree( GetProcessHeap(), 0, actions );
return TRUE;
}
static BOOL parse_failure_params( int argc, const WCHAR *argv[], SERVICE_FAILURE_ACTIONSW *fa )
{
unsigned int i;
fa->dwResetPeriod = 0;
fa->lpRebootMsg = NULL;
fa->lpCommand = NULL;
fa->cActions = 0;
fa->lpsaActions = NULL;
#define PARSE(x, y) parse_string_param( (argc), (argv), &(i), (x), ARRAY_SIZE(x) - 1, (y) )
for (i = 0; i < argc; i++)
{
const WCHAR *reset, *actions;
if (PARSE( L"reset=", &reset ))
{
fa->dwResetPeriod = wcstol( reset, NULL, 10 );
continue;
}
if (PARSE( L"reboot=", (const WCHAR **)&fa->lpRebootMsg )) continue;
if (PARSE( L"command=", (const WCHAR **)&fa->lpCommand )) continue;
if (PARSE( L"actions=", &actions ))
{
if (!parse_failure_actions( actions, fa )) return FALSE;
continue;
}
}
#undef PARSE
return TRUE;
}
static void usage( void )
{
WINE_MESSAGE( "Usage: sc command servicename [parameter= value ...]\n" );
exit( 1 );
}
static const WCHAR *service_type_string( DWORD type )
{
switch (type)
{
case SERVICE_WIN32_OWN_PROCESS: return L"WIN32_OWN_PROCESS";
case SERVICE_WIN32_SHARE_PROCESS: return L"WIN32_SHARE_PROCESS";
case SERVICE_WIN32: return L"WIN32";
default: return L"";
}
}
static const WCHAR *service_state_string( DWORD state )
{
static const WCHAR * const state_str[] = { L"", L"STOPPED", L"START_PENDING",
L"STOP_PENDING", L"RUNNING", L"CONTINUE_PENDING", L"PAUSE_PENDING", L"PAUSED" };
if (state < ARRAY_SIZE( state_str )) return state_str[ state ];
return L"";
}
static BOOL query_service( SC_HANDLE manager, const WCHAR *name )
{
SC_HANDLE service;
SERVICE_STATUS status;
BOOL ret = FALSE;
service = OpenServiceW( manager, name, SERVICE_QUERY_STATUS );
if (service)
{
ret = QueryServiceStatus( service, &status );
if (!ret)
WINE_ERR("failed to query service status %lu\n", GetLastError());
else
printf( "SERVICE_NAME: %ls\n"
" TYPE : %lx %ls\n"
" STATE : %lx %ls\n"
" WIN32_EXIT_CODE : %lu (0x%lx)\n"
" SERVICE_EXIT_CODE : %lu (0x%lx)\n"
" CHECKPOINT : 0x%lx\n"
" WAIT_HINT : 0x%lx\n",
name, status.dwServiceType, service_type_string( status.dwServiceType ),
status.dwCurrentState, service_state_string( status.dwCurrentState ),
status.dwWin32ExitCode, status.dwWin32ExitCode,
status.dwServiceSpecificExitCode, status.dwServiceSpecificExitCode,
status.dwCheckPoint, status.dwWaitHint );
CloseServiceHandle( service );
}
else WINE_ERR("failed to open service %lu\n", GetLastError());
return ret;
}
int __cdecl wmain( int argc, const WCHAR *argv[] )
{
SC_HANDLE manager, service;
SERVICE_STATUS status;
BOOL ret = FALSE;
if (argc < 3) usage();
if (argv[2][0] == '\\' && argv[2][1] == '\\')
{
WINE_FIXME("server argument not supported\n");
return 1;
}
manager = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
if (!manager)
{
WINE_ERR("failed to open service manager\n");
return 1;
}
if (!wcsicmp( argv[1], L"create" ))
{
struct create_params cp;
if (argc < 4)
{
CloseServiceHandle( manager );
usage();
}
if (!parse_create_params( argc - 3, argv + 3, &cp ))
{
WINE_ERR("failed to parse create parameters\n");
CloseServiceHandle( manager );
return 1;
}
service = CreateServiceW( manager, argv[2], cp.displayname, SERVICE_ALL_ACCESS,
cp.type, cp.start, cp.error, cp.binpath, cp.group, NULL,
cp.depend, cp.obj, cp.password );
if (service)
{
CloseServiceHandle( service );
ret = TRUE;
}
else WINE_ERR("failed to create service %lu\n", GetLastError());
}
else if (!wcsicmp( argv[1], L"description" ))
{
service = OpenServiceW( manager, argv[2], SERVICE_CHANGE_CONFIG );
if (service)
{
SERVICE_DESCRIPTIONW sd;
sd.lpDescription = argc > 3 ? (WCHAR *)argv[3] : NULL;
ret = ChangeServiceConfig2W( service, SERVICE_CONFIG_DESCRIPTION, &sd );
if (!ret) WINE_ERR("failed to set service description %lu\n", GetLastError());
CloseServiceHandle( service );
}
else WINE_ERR("failed to open service %lu\n", GetLastError());
}
else if (!wcsicmp( argv[1], L"failure" ))
{
service = OpenServiceW( manager, argv[2], SERVICE_CHANGE_CONFIG );
if (service)
{
SERVICE_FAILURE_ACTIONSW sfa;
if (parse_failure_params( argc - 3, argv + 3, &sfa ))
{
ret = ChangeServiceConfig2W( service, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa );
if (!ret) WINE_ERR("failed to set service failure actions %lu\n", GetLastError());
HeapFree( GetProcessHeap(), 0, sfa.lpsaActions );
}
else
WINE_ERR("failed to parse failure parameters\n");
CloseServiceHandle( service );
}
else WINE_ERR("failed to open service %lu\n", GetLastError());
}
else if (!wcsicmp( argv[1], L"delete" ))
{
service = OpenServiceW( manager, argv[2], DELETE );
if (service)
{
ret = DeleteService( service );
if (!ret) WINE_ERR("failed to delete service %lu\n", GetLastError());
CloseServiceHandle( service );
}
else WINE_ERR("failed to open service %lu\n", GetLastError());
}
else if (!wcsicmp( argv[1], L"start" ))
{
service = OpenServiceW( manager, argv[2], SERVICE_START );
if (service)
{
ret = StartServiceW( service, argc - 3, argv + 3 );
if (!ret) WINE_ERR("failed to start service %lu\n", GetLastError());
else query_service( manager, argv[2] );
CloseServiceHandle( service );
}
else WINE_ERR("failed to open service %lu\n", GetLastError());
}
else if (!wcsicmp( argv[1], L"stop" ))
{
service = OpenServiceW( manager, argv[2], SERVICE_STOP );
if (service)
{
ret = ControlService( service, SERVICE_CONTROL_STOP, &status );
if (!ret) WINE_ERR("failed to stop service %lu\n", GetLastError());
else query_service( manager, argv[2] );
CloseServiceHandle( service );
}
else WINE_ERR("failed to open service %lu\n", GetLastError());
}
else if (!wcsicmp( argv[1], L"query" ))
{
ret = query_service( manager, argv[2] );
}
else if (!wcsicmp( argv[1], L"sdset" ))
{
WINE_FIXME("SdSet command not supported, faking success\n");
ret = TRUE;
}
else
WINE_FIXME("command not supported\n");
CloseServiceHandle( manager );
return !ret;
}