sc: Print service status after 'start' and 'stop' commands.
[wine.git] / programs / sc / sc.c
blob3b029f33c78cff3875e7820339007780de3a7ddd
1 /*
2 * Copyright 2010 Hans Leidekker
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define WIN32_LEAN_AND_MEAN
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <windows.h>
25 #include <winsvc.h>
26 #include "wine/debug.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(sc);
30 struct create_params
32 const WCHAR *displayname;
33 const WCHAR *binpath;
34 const WCHAR *group;
35 const WCHAR *depend;
36 const WCHAR *obj;
37 const WCHAR *password;
38 DWORD type;
39 DWORD start;
40 DWORD error;
41 BOOL tag;
44 static BOOL parse_create_params( int argc, const WCHAR *argv[], struct create_params *cp )
46 unsigned int i;
48 cp->displayname = NULL;
49 cp->type = SERVICE_WIN32_OWN_PROCESS;
50 cp->start = SERVICE_DEMAND_START;
51 cp->error = SERVICE_ERROR_NORMAL;
52 cp->binpath = NULL;
53 cp->group = NULL;
54 cp->tag = FALSE;
55 cp->depend = NULL;
56 cp->obj = NULL;
57 cp->password = NULL;
59 for (i = 0; i < argc; i++)
61 if (!wcsnicmp( argv[i], L"displayname=", 12 )) cp->displayname = argv[i] + 12;
62 if (!wcsnicmp( argv[i], L"binpath=", 8 )) cp->binpath = argv[i] + 8;
63 if (!wcsnicmp( argv[i], L"group=", 6 )) cp->group = argv[i] + 6;
64 if (!wcsnicmp( argv[i], L"depend=", 7 )) cp->depend = argv[i] + 7;
65 if (!wcsnicmp( argv[i], L"obj=", 4 )) cp->obj = argv[i] + 4;
66 if (!wcsnicmp( argv[i], L"password=", 9 )) cp->password = argv[i] + 9;
68 if (!wcsnicmp( argv[i], L"tag=", 4 ))
70 if (!wcsicmp( argv[i] + 4, L"yes" ))
72 WINE_FIXME("tag argument not supported\n");
73 cp->tag = TRUE;
76 if (!wcsnicmp( argv[i], L"type=", 5 ))
78 if (!wcsicmp( argv[i] + 5, L"own" )) cp->type = SERVICE_WIN32_OWN_PROCESS;
79 if (!wcsicmp( argv[i] + 5, L"share" )) cp->type = SERVICE_WIN32_SHARE_PROCESS;
80 if (!wcsicmp( argv[i] + 5, L"kernel" )) cp->type = SERVICE_KERNEL_DRIVER;
81 if (!wcsicmp( argv[i] + 5, L"filesys" )) cp->type = SERVICE_FILE_SYSTEM_DRIVER;
82 if (!wcsicmp( argv[i] + 5, L"rec" )) cp->type = SERVICE_RECOGNIZER_DRIVER;
83 if (!wcsicmp( argv[i] + 5, L"interact" )) cp->type |= SERVICE_INTERACTIVE_PROCESS;
85 if (!wcsnicmp( argv[i], L"start=", 6 ))
87 if (!wcsicmp( argv[i] + 6, L"boot" )) cp->start = SERVICE_BOOT_START;
88 if (!wcsicmp( argv[i] + 6, L"system" )) cp->start = SERVICE_SYSTEM_START;
89 if (!wcsicmp( argv[i] + 6, L"auto" )) cp->start = SERVICE_AUTO_START;
90 if (!wcsicmp( argv[i] + 6, L"demand" )) cp->start = SERVICE_DEMAND_START;
91 if (!wcsicmp( argv[i] + 6, L"disabled" )) cp->start = SERVICE_DISABLED;
93 if (!wcsnicmp( argv[i], L"error=", 6 ))
95 if (!wcsicmp( argv[i] + 6, L"normal" )) cp->error = SERVICE_ERROR_NORMAL;
96 if (!wcsicmp( argv[i] + 6, L"severe" )) cp->error = SERVICE_ERROR_SEVERE;
97 if (!wcsicmp( argv[i] + 6, L"critical" )) cp->error = SERVICE_ERROR_CRITICAL;
98 if (!wcsicmp( argv[i] + 6, L"ignore" )) cp->error = SERVICE_ERROR_IGNORE;
101 if (!cp->binpath) return FALSE;
102 return TRUE;
105 static BOOL parse_failure_actions( const WCHAR *arg, SERVICE_FAILURE_ACTIONSW *fa )
107 unsigned int i, count;
108 WCHAR *actions, *p;
110 actions = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( arg ) + 1) * sizeof(WCHAR) );
111 if (!actions) return FALSE;
113 lstrcpyW( actions, arg );
114 for (p = actions, count = 0; *p; p++)
116 if (*p == '/')
118 count++;
119 *p = 0;
122 count = count / 2 + 1;
124 fa->cActions = count;
125 fa->lpsaActions = HeapAlloc( GetProcessHeap(), 0, fa->cActions * sizeof(SC_ACTION) );
126 if (!fa->lpsaActions)
128 HeapFree( GetProcessHeap(), 0, actions );
129 return FALSE;
132 p = actions;
133 for (i = 0; i < count; i++)
135 if (!wcsicmp( p, L"run" )) fa->lpsaActions[i].Type = SC_ACTION_RUN_COMMAND;
136 else if (!wcsicmp( p, L"restart" )) fa->lpsaActions[i].Type = SC_ACTION_RESTART;
137 else if (!wcsicmp( p, L"reboot" )) fa->lpsaActions[i].Type = SC_ACTION_REBOOT;
138 else fa->lpsaActions[i].Type = SC_ACTION_NONE;
140 p += lstrlenW( p ) + 1;
141 fa->lpsaActions[i].Delay = wcstol( p, NULL, 10 );
142 p += lstrlenW( p ) + 1;
145 HeapFree( GetProcessHeap(), 0, actions );
146 return TRUE;
149 static BOOL parse_failure_params( int argc, const WCHAR *argv[], SERVICE_FAILURE_ACTIONSW *fa )
151 unsigned int i;
153 fa->dwResetPeriod = 0;
154 fa->lpRebootMsg = NULL;
155 fa->lpCommand = NULL;
156 fa->cActions = 0;
157 fa->lpsaActions = NULL;
159 for (i = 0; i < argc; i++)
161 if (!wcsnicmp( argv[i], L"reset=", 6 )) fa->dwResetPeriod = wcstol( argv[i] + 6, NULL, 10 );
162 if (!wcsnicmp( argv[i], L"reboot=", 7 )) fa->lpRebootMsg = (WCHAR *)argv[i] + 7;
163 if (!wcsnicmp( argv[i], L"command=", 8 )) fa->lpCommand = (WCHAR *)argv[i] + 8;
164 if (!wcsnicmp( argv[i], L"actions=", 8 ))
166 if (!parse_failure_actions( argv[i] + 8, fa )) return FALSE;
169 return TRUE;
172 static void usage( void )
174 WINE_MESSAGE( "Usage: sc command servicename [parameter= value ...]\n" );
175 exit( 1 );
178 static const WCHAR *service_type_string( DWORD type )
180 switch (type)
182 case SERVICE_WIN32_OWN_PROCESS: return L"WIN32_OWN_PROCESS";
183 case SERVICE_WIN32_SHARE_PROCESS: return L"WIN32_SHARE_PROCESS";
184 case SERVICE_WIN32: return L"WIN32";
185 default: return L"";
189 static const WCHAR *service_state_string( DWORD state )
191 static const WCHAR * const state_str[] = { L"", L"STOPPED", L"START_PENDING",
192 L"STOP_PENDING", L"RUNNING", L"CONTINUE_PENDING", L"PAUSE_PENDING", L"PAUSED" };
194 if (state < ARRAY_SIZE( state_str )) return state_str[ state ];
195 return L"";
198 static BOOL query_service( SC_HANDLE manager, const WCHAR *name )
200 SC_HANDLE service;
201 SERVICE_STATUS status;
202 BOOL ret = FALSE;
204 service = OpenServiceW( manager, name, SERVICE_QUERY_STATUS );
205 if (service)
207 ret = QueryServiceStatus( service, &status );
208 if (!ret)
209 WINE_ERR("failed to query service status %lu\n", GetLastError());
210 else
211 printf( "SERVICE_NAME: %ls\n"
212 " TYPE : %lx %ls\n"
213 " STATE : %lx %ls\n"
214 " WIN32_EXIT_CODE : %lu (0x%lx)\n"
215 " SERVICE_EXIT_CODE : %lu (0x%lx)\n"
216 " CHECKPOINT : 0x%lx\n"
217 " WAIT_HINT : 0x%lx\n",
218 name, status.dwServiceType, service_type_string( status.dwServiceType ),
219 status.dwCurrentState, service_state_string( status.dwCurrentState ),
220 status.dwWin32ExitCode, status.dwWin32ExitCode,
221 status.dwServiceSpecificExitCode, status.dwServiceSpecificExitCode,
222 status.dwCheckPoint, status.dwWaitHint );
223 CloseServiceHandle( service );
225 else WINE_ERR("failed to open service %lu\n", GetLastError());
227 return ret;
230 int __cdecl wmain( int argc, const WCHAR *argv[] )
232 SC_HANDLE manager, service;
233 SERVICE_STATUS status;
234 BOOL ret = FALSE;
236 if (argc < 3) usage();
238 if (argv[2][0] == '\\' && argv[2][1] == '\\')
240 WINE_FIXME("server argument not supported\n");
241 return 1;
244 manager = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
245 if (!manager)
247 WINE_ERR("failed to open service manager\n");
248 return 1;
251 if (!wcsicmp( argv[1], L"create" ))
253 struct create_params cp;
255 if (argc < 4)
257 CloseServiceHandle( manager );
258 usage();
260 if (!parse_create_params( argc - 3, argv + 3, &cp ))
262 WINE_ERR("failed to parse create parameters\n");
263 CloseServiceHandle( manager );
264 return 1;
266 service = CreateServiceW( manager, argv[2], cp.displayname, SERVICE_ALL_ACCESS,
267 cp.type, cp.start, cp.error, cp.binpath, cp.group, NULL,
268 cp.depend, cp.obj, cp.password );
269 if (service)
271 CloseServiceHandle( service );
272 ret = TRUE;
274 else WINE_ERR("failed to create service %lu\n", GetLastError());
276 else if (!wcsicmp( argv[1], L"description" ))
278 service = OpenServiceW( manager, argv[2], SERVICE_CHANGE_CONFIG );
279 if (service)
281 SERVICE_DESCRIPTIONW sd;
282 sd.lpDescription = argc > 3 ? (WCHAR *)argv[3] : NULL;
283 ret = ChangeServiceConfig2W( service, SERVICE_CONFIG_DESCRIPTION, &sd );
284 if (!ret) WINE_ERR("failed to set service description %lu\n", GetLastError());
285 CloseServiceHandle( service );
287 else WINE_ERR("failed to open service %lu\n", GetLastError());
289 else if (!wcsicmp( argv[1], L"failure" ))
291 service = OpenServiceW( manager, argv[2], SERVICE_CHANGE_CONFIG );
292 if (service)
294 SERVICE_FAILURE_ACTIONSW sfa;
295 if (parse_failure_params( argc - 3, argv + 3, &sfa ))
297 ret = ChangeServiceConfig2W( service, SERVICE_CONFIG_FAILURE_ACTIONS, &sfa );
298 if (!ret) WINE_TRACE("failed to set service failure actions %lu\n", GetLastError());
299 HeapFree( GetProcessHeap(), 0, sfa.lpsaActions );
301 else
302 WINE_ERR("failed to parse failure parameters\n");
303 CloseServiceHandle( service );
305 else WINE_ERR("failed to open service %lu\n", GetLastError());
307 else if (!wcsicmp( argv[1], L"delete" ))
309 service = OpenServiceW( manager, argv[2], DELETE );
310 if (service)
312 ret = DeleteService( service );
313 if (!ret) WINE_TRACE("failed to delete service %lu\n", GetLastError());
314 CloseServiceHandle( service );
316 else WINE_ERR("failed to open service %lu\n", GetLastError());
318 else if (!wcsicmp( argv[1], L"start" ))
320 service = OpenServiceW( manager, argv[2], SERVICE_START );
321 if (service)
323 ret = StartServiceW( service, argc - 3, argv + 3 );
324 if (!ret) WINE_TRACE("failed to start service %lu\n", GetLastError());
325 else query_service( manager, argv[2] );
326 CloseServiceHandle( service );
328 else WINE_ERR("failed to open service %lu\n", GetLastError());
330 else if (!wcsicmp( argv[1], L"stop" ))
332 service = OpenServiceW( manager, argv[2], SERVICE_STOP );
333 if (service)
335 ret = ControlService( service, SERVICE_CONTROL_STOP, &status );
336 if (!ret) WINE_TRACE("failed to stop service %lu\n", GetLastError());
337 else query_service( manager, argv[2] );
338 CloseServiceHandle( service );
340 else WINE_ERR("failed to open service %lu\n", GetLastError());
342 else if (!wcsicmp( argv[1], L"query" ))
344 ret = query_service( manager, argv[2] );
346 else if (!wcsicmp( argv[1], L"sdset" ))
348 WINE_FIXME("SdSet command not supported, faking success\n");
349 ret = TRUE;
351 else
352 WINE_FIXME("command not supported\n");
354 CloseServiceHandle( manager );
355 return !ret;