| /* |
| * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF |
| * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED |
| * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A |
| * PARTICULAR PURPOSE. |
| * |
| * Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved. |
| * 2013 Heiko Hund <heiko.hund@sophos.com> |
| */ |
| |
| #include "service.h" |
| |
| #include <windows.h> |
| #include <stdio.h> |
| #include <process.h> |
| |
| |
| openvpn_service_t openvpn_service[_service_max]; |
| |
| |
| BOOL |
| ReportStatusToSCMgr(SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status) |
| { |
| static DWORD dwCheckPoint = 1; |
| BOOL res = TRUE; |
| |
| if (status->dwCurrentState == SERVICE_START_PENDING) |
| { |
| status->dwControlsAccepted = 0; |
| } |
| else |
| { |
| status->dwControlsAccepted = SERVICE_ACCEPT_STOP; |
| } |
| |
| if (status->dwCurrentState == SERVICE_RUNNING |
| || status->dwCurrentState == SERVICE_STOPPED) |
| { |
| status->dwCheckPoint = 0; |
| } |
| else |
| { |
| status->dwCheckPoint = dwCheckPoint++; |
| } |
| |
| /* Report the status of the service to the service control manager. */ |
| res = SetServiceStatus(service, status); |
| if (!res) |
| { |
| MsgToEventLog(MSG_FLAGS_ERROR, TEXT("SetServiceStatus")); |
| } |
| |
| return res; |
| } |
| |
| static int |
| CmdInstallServices() |
| { |
| SC_HANDLE service; |
| SC_HANDLE svc_ctl_mgr; |
| TCHAR path[512]; |
| int i, ret = _service_max; |
| |
| if (GetModuleFileName(NULL, path + 1, 510) == 0) |
| { |
| _tprintf(TEXT("Unable to install service - %s\n"), GetLastErrorText()); |
| return 1; |
| } |
| |
| path[0] = TEXT('\"'); |
| _tcscat(path, TEXT("\"")); |
| |
| svc_ctl_mgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); |
| if (svc_ctl_mgr == NULL) |
| { |
| _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText()); |
| return 1; |
| } |
| |
| for (i = 0; i < _service_max; i++) |
| { |
| service = CreateService(svc_ctl_mgr, |
| openvpn_service[i].name, |
| openvpn_service[i].display_name, |
| SERVICE_QUERY_STATUS, |
| SERVICE_WIN32_SHARE_PROCESS, |
| openvpn_service[i].start_type, |
| SERVICE_ERROR_NORMAL, |
| path, NULL, NULL, |
| openvpn_service[i].dependencies, |
| NULL, NULL); |
| if (service) |
| { |
| _tprintf(TEXT("%s installed.\n"), openvpn_service[i].display_name); |
| CloseServiceHandle(service); |
| --ret; |
| } |
| else |
| { |
| _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText()); |
| } |
| } |
| |
| CloseServiceHandle(svc_ctl_mgr); |
| return ret; |
| } |
| |
| |
| static int |
| CmdStartService(openvpn_service_type type) |
| { |
| int ret = 1; |
| SC_HANDLE svc_ctl_mgr; |
| SC_HANDLE service; |
| |
| svc_ctl_mgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); |
| if (svc_ctl_mgr == NULL) |
| { |
| _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText()); |
| return 1; |
| } |
| |
| service = OpenService(svc_ctl_mgr, openvpn_service[type].name, SERVICE_ALL_ACCESS); |
| if (service) |
| { |
| if (StartService(service, 0, NULL)) |
| { |
| _tprintf(TEXT("Service Started\n")); |
| ret = 0; |
| } |
| else |
| { |
| _tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText()); |
| } |
| |
| CloseServiceHandle(service); |
| } |
| else |
| { |
| _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText()); |
| } |
| |
| CloseServiceHandle(svc_ctl_mgr); |
| return ret; |
| } |
| |
| |
| static int |
| CmdRemoveServices() |
| { |
| SC_HANDLE service; |
| SC_HANDLE svc_ctl_mgr; |
| SERVICE_STATUS status; |
| int i, ret = _service_max; |
| |
| svc_ctl_mgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); |
| if (svc_ctl_mgr == NULL) |
| { |
| _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText()); |
| return 1; |
| } |
| |
| for (i = 0; i < _service_max; i++) |
| { |
| openvpn_service_t *ovpn_svc = &openvpn_service[i]; |
| service = OpenService(svc_ctl_mgr, ovpn_svc->name, |
| DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS); |
| if (service == NULL) |
| { |
| _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText()); |
| goto out; |
| } |
| |
| /* try to stop the service */ |
| if (ControlService(service, SERVICE_CONTROL_STOP, &status)) |
| { |
| _tprintf(TEXT("Stopping %s."), ovpn_svc->display_name); |
| Sleep(1000); |
| |
| while (QueryServiceStatus(service, &status)) |
| { |
| if (status.dwCurrentState == SERVICE_STOP_PENDING) |
| { |
| _tprintf(TEXT(".")); |
| Sleep(1000); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if (status.dwCurrentState == SERVICE_STOPPED) |
| { |
| _tprintf(TEXT("\n%s stopped.\n"), ovpn_svc->display_name); |
| } |
| else |
| { |
| _tprintf(TEXT("\n%s failed to stop.\n"), ovpn_svc->display_name); |
| } |
| } |
| |
| /* now remove the service */ |
| if (DeleteService(service)) |
| { |
| _tprintf(TEXT("%s removed.\n"), ovpn_svc->display_name); |
| --ret; |
| } |
| else |
| { |
| _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText()); |
| } |
| |
| CloseServiceHandle(service); |
| } |
| |
| out: |
| CloseServiceHandle(svc_ctl_mgr); |
| return ret; |
| } |
| |
| |
| int |
| _tmain(int argc, TCHAR *argv[]) |
| { |
| /* |
| * Automatic + Interactive service (as a SERVICE_WIN32_SHARE_PROCESS) |
| * This is the default. |
| */ |
| const SERVICE_TABLE_ENTRY dispatchTable_shared[] = { |
| { automatic_service.name, ServiceStartAutomatic }, |
| { interactive_service.name, ServiceStartInteractive }, |
| { NULL, NULL } |
| }; |
| |
| /* Automatic service only (as a SERVICE_WIN32_OWN_PROCESS) */ |
| const SERVICE_TABLE_ENTRY dispatchTable_automatic[] = { |
| { TEXT(""), ServiceStartAutomaticOwn }, |
| { NULL, NULL } |
| }; |
| |
| /* Interactive service only (as a SERVICE_WIN32_OWN_PROCESS) */ |
| const SERVICE_TABLE_ENTRY dispatchTable_interactive[] = { |
| { TEXT(""), ServiceStartInteractiveOwn }, |
| { NULL, NULL } |
| }; |
| |
| const SERVICE_TABLE_ENTRY *dispatchTable = dispatchTable_shared; |
| |
| openvpn_service[0] = automatic_service; |
| openvpn_service[1] = interactive_service; |
| |
| for (int i = 1; i < argc; i++) |
| { |
| if (*argv[i] == TEXT('-') || *argv[i] == TEXT('/')) |
| { |
| if (_tcsicmp(TEXT("install"), argv[i] + 1) == 0) |
| { |
| return CmdInstallServices(); |
| } |
| else if (_tcsicmp(TEXT("remove"), argv[i] + 1) == 0) |
| { |
| return CmdRemoveServices(); |
| } |
| else if (_tcsicmp(TEXT("start"), argv[i] + 1) == 0) |
| { |
| BOOL is_auto = argc < i + 2 || _tcsicmp(TEXT("interactive"), argv[i + 1]) != 0; |
| return CmdStartService(is_auto ? automatic : interactive); |
| } |
| else if (argc > i + 2 && _tcsicmp(TEXT("instance"), argv[i] + 1) == 0) |
| { |
| dispatchTable = _tcsicmp(TEXT("interactive"), argv[i + 1]) != 0 ? |
| dispatchTable_automatic : |
| dispatchTable_interactive; |
| |
| service_instance = argv[i + 2]; |
| i += 2; |
| } |
| else |
| { |
| _tprintf(TEXT("%s -install to install the services\n"), APPNAME); |
| _tprintf(TEXT("%s -start <name> to start a service (\"automatic\" or \"interactive\")\n"), APPNAME); |
| _tprintf(TEXT("%s -remove to remove the services\n"), APPNAME); |
| |
| _tprintf(TEXT("\nService run-time parameters:\n")); |
| _tprintf(TEXT("-instance <name> <id>\n") |
| TEXT(" Runs the service as an alternate instance. <name> can be \"automatic\" or\n") |
| TEXT(" \"interactive\". The service settings will be loaded from\n") |
| TEXT(" HKLM\\Software\\" PACKAGE_NAME "<id> registry key, and the interactive service will accept\n") |
| TEXT(" requests on \\\\.\\pipe\\" PACKAGE "<id>\\service named pipe.\n")); |
| |
| return 0; |
| } |
| } |
| } |
| |
| /* If it doesn't match any of the above parameters |
| * the service control manager may be starting the service |
| * so we must call StartServiceCtrlDispatcher |
| */ |
| _tprintf(TEXT("\nStartServiceCtrlDispatcher being called.\n")); |
| _tprintf(TEXT("This may take several seconds. Please wait.\n")); |
| |
| if (!StartServiceCtrlDispatcher(dispatchTable)) |
| { |
| MsgToEventLog(MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed.")); |
| } |
| |
| return 0; |
| } |