From 0f09ec557ff01f6b8af6428b65962f1c9718676f Mon Sep 17 00:00:00 2001 From: Rom Walton Date: Thu, 13 Nov 2008 18:30:08 +0000 Subject: [PATCH] - SVCCTRL: Introduce a new binary that is used to start and stop the BOINC service, if it is installed as a service. NOTE: If UAC is turned on in Windows Vista then even an administrator cannot start/stop a service without elevating their priviliages. The new application includes the requiresAdmin tag in its manifest file to cause the UAC dialog to be displayed when it is launched. clientctrl/ boincsvcctrl.cpp, .h boincsvcctrl.rc boincsvcctrl.xml win_build/ boinc.sln boincsvcctrl.vcproj svn path=/trunk/boinc/; revision=16488 --- checkin_notes | 18 + clientctrl/boincsvcctrl.cpp | 70 ++ clientctrl/boincsvcctrl.h | 16 + clientctrl/boincsvcctrl.rc | 199 ++++++ clientctrl/boincsvcctrl.xml | 11 + win_build/boinc.sln | 41 +- win_build/boincsvcctrl.vcproj | 1259 +++++++++++++++++++++++++++++++++ 7 files changed, 1602 insertions(+), 12 deletions(-) create mode 100644 clientctrl/boincsvcctrl.cpp create mode 100644 clientctrl/boincsvcctrl.h create mode 100644 clientctrl/boincsvcctrl.rc create mode 100644 clientctrl/boincsvcctrl.xml create mode 100644 win_build/boincsvcctrl.vcproj diff --git a/checkin_notes b/checkin_notes index 05248e2375..f1d73e6ddd 100644 --- a/checkin_notes +++ b/checkin_notes @@ -9412,3 +9412,21 @@ David 12 Nov 2008 html/user/ team_join_action.php + +Rom 13 Nov 2008 + - SVCCTRL: Introduce a new binary that is used to start and stop + the BOINC service, if it is installed as a service. + + NOTE: If UAC is turned on in Windows Vista then even an + administrator cannot start/stop a service without elevating + their priviliages. The new application includes the + requiresAdmin tag in its manifest file to cause the UAC + dialog to be displayed when it is launched. + + clientctrl/ + boincsvcctrl.cpp, .h + boincsvcctrl.rc + boincsvcctrl.xml + win_build/ + boinc.sln + boincsvcctrl.vcproj diff --git a/clientctrl/boincsvcctrl.cpp b/clientctrl/boincsvcctrl.cpp new file mode 100644 index 0000000000..79870fc977 --- /dev/null +++ b/clientctrl/boincsvcctrl.cpp @@ -0,0 +1,70 @@ +// This file is part of BOINC. +// http://boinc.berkeley.edu +// Copyright (C) 2008 University of California +// +// BOINC 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 3 of the License, or (at your option) any later version. +// +// BOINC 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 BOINC. If not, see . + +#if defined(_WIN32) && !defined(__STDWX_H__) && !defined(_BOINC_WIN_) && !defined(_AFX_STDAFX_H_) +#include "boinc_win.h" +#endif + +#include "version.h" + +EXTERN_C BOOL StartBOINCService(); +EXTERN_C BOOL StopBOINCService(); + + +void version(){ + printf("boincsvcctrl, built from %s \n", PACKAGE_STRING ); + exit(0); +} + +void usage() { + fprintf(stderr, "\n\ +usage: boincsvcctrl command\n\n\ +Commands:\n\ + --start start the BOINC service\n\ + --stop stop the BOINC service\n\ + --version, -V show core client version\n\ +" +); + exit(1); +} + +int main(int argc, char** argv) { + int i = 1; + int retval = 0; + + if (argc < 1) { + usage(); + } + + if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) { + usage(); + } + if (!strcmp(argv[i], "--version") || !strcmp(argv[i], "-V")) { + version(); + } + + if (!strcmp(argv[i], "--start")) { + retval = !StartBOINCService(); + } + + if (!strcmp(argv[i], "--stop")) { + retval = !StopBOINCService(); + } + + exit(retval); +} + diff --git a/clientctrl/boincsvcctrl.h b/clientctrl/boincsvcctrl.h new file mode 100644 index 0000000000..d4aafdb3b8 --- /dev/null +++ b/clientctrl/boincsvcctrl.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by boincsvcctrl.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40000 +#define _APS_NEXT_CONTROL_VALUE 1007 +#define _APS_NEXT_SYMED_VALUE 102 +#endif +#endif diff --git a/clientctrl/boincsvcctrl.rc b/clientctrl/boincsvcctrl.rc new file mode 100644 index 0000000000..8c80bbe9f1 --- /dev/null +++ b/clientctrl/boincsvcctrl.rc @@ -0,0 +1,199 @@ +// Microsoft Visual C++ generated resource script. +// +#include "boincsvcctrl.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winresrc.h" +#include "version.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// + +#endif // Neutral resources + + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "boincsvcctrl.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winresrc.h""\r\n" + "#include ""version.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// Visual Studio 2005 Compatibility +// +#ifndef IDC_STATIC +#define IDC_STATIC (-1) // all static controls +#endif + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined (_GRIDREPUBLIC) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,BOINC_RELEASE,0 + PRODUCTVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,BOINC_RELEASE,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "GridRepublic" + VALUE "FileDescription", "BOINC service controller" + VALUE "FileVersion", BOINC_VERSION_STRING "\0" + VALUE "InternalName", "boincsvcctrl" + VALUE "LegalCopyright", "© 2003-2008 University of California" + VALUE "OriginalFilename", "boincsvcctrl.exe" + VALUE "ProductName", "BOINC core client" + VALUE "ProductVersion", BOINC_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#elif defined(_WCG) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,BOINC_RELEASE,0 + PRODUCTVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,BOINC_RELEASE,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "World Community Grid" + VALUE "FileDescription", "BOINC service controller" + VALUE "FileVersion", BOINC_VERSION_STRING "\0" + VALUE "InternalName", "boincsvcctrl" + VALUE "LegalCopyright", "© 2003-2008 University of California" + VALUE "OriginalFilename", "boincsvcctrl.exe" + VALUE "ProductName", "BOINC core client" + VALUE "ProductVersion", BOINC_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#else + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,BOINC_RELEASE,0 + PRODUCTVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,BOINC_RELEASE,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Space Sciences Laboratory" + VALUE "FileDescription", "BOINC service controller" + VALUE "FileVersion", BOINC_VERSION_STRING "\0" + VALUE "InternalName", "boincsvcctrl" + VALUE "LegalCopyright", "© 2003-2008 University of California" + VALUE "OriginalFilename", "boincsvcctrl.exe" + VALUE "ProductName", "BOINC core client" + VALUE "ProductVersion", BOINC_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/clientctrl/boincsvcctrl.xml b/clientctrl/boincsvcctrl.xml new file mode 100644 index 0000000000..eb477a0d3e --- /dev/null +++ b/clientctrl/boincsvcctrl.xml @@ -0,0 +1,11 @@ + + + BOINC Service Controller + + + + + + + + diff --git a/win_build/boinc.sln b/win_build/boinc.sln index a470b8a032..09d0a2fc14 100644 --- a/win_build/boinc.sln +++ b/win_build/boinc.sln @@ -6,8 +6,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "boinc", "boinc_cli_curl.vcp Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {B06280CB-82A4-46DE-8956-602643078BDF} = {B06280CB-82A4-46DE-8956-602643078BDF} {E8F6BD7E-461A-4733-B7D8-37B09A099ED8} = {E8F6BD7E-461A-4733-B7D8-37B09A099ED8} + {B06280CB-82A4-46DE-8956-602643078BDF} = {B06280CB-82A4-46DE-8956-602643078BDF} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "boinc_dll", "boinc_dll.vcproj", "{B06280CB-82A4-46DE-8956-602643078BDF}" @@ -28,8 +28,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "boinccmd", "boinccmd.vcproj Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {C04F0FCC-BB5D-4627-8656-6173B28BD69E} = {C04F0FCC-BB5D-4627-8656-6173B28BD69E} {E8F6BD7E-461A-4733-B7D8-37B09A099ED8} = {E8F6BD7E-461A-4733-B7D8-37B09A099ED8} + {C04F0FCC-BB5D-4627-8656-6173B28BD69E} = {C04F0FCC-BB5D-4627-8656-6173B28BD69E} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "boincmgr", "boincmgr_curl.vcproj", "{06113715-AC51-4E91-8B9D-C987CABE0920}" @@ -38,8 +38,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "boincmgr", "boincmgr_curl.v Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {C04F0FCC-BB5D-4627-8656-6173B28BD69E} = {C04F0FCC-BB5D-4627-8656-6173B28BD69E} {B06280CB-82A4-46DE-8956-602643078BDF} = {B06280CB-82A4-46DE-8956-602643078BDF} + {C04F0FCC-BB5D-4627-8656-6173B28BD69E} = {C04F0FCC-BB5D-4627-8656-6173B28BD69E} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libboinc", "libboinc.vcproj", "{E8F6BD7E-461A-4733-B7D8-37B09A099ED8}" @@ -96,8 +96,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wrapper", "wrapper.vcproj", Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} {B00664BD-71EB-46C1-957E-CD851418D395} = {B00664BD-71EB-46C1-957E-CD851418D395} + {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sleeper", "sleeper.vcproj", "{A9647CEA-644D-4C0A-8733-D916CD344859}" @@ -106,8 +106,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sleeper", "sleeper.vcproj", Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} {B00664BD-71EB-46C1-957E-CD851418D395} = {B00664BD-71EB-46C1-957E-CD851418D395} + {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "worker", "worker.vcproj", "{F1BE6109-586D-448E-8C5B-D5C2CB874EA2}" @@ -116,8 +116,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "worker", "worker.vcproj", " Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {B00664BD-71EB-46C1-957E-CD851418D395} = {B00664BD-71EB-46C1-957E-CD851418D395} {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} + {B00664BD-71EB-46C1-957E-CD851418D395} = {B00664BD-71EB-46C1-957E-CD851418D395} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_app", "uc2.vcproj", "{CCB9A37C-7AD8-4FC1-ABEC-1A6ED2268F83}" @@ -126,8 +126,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_app", "uc2.vcproj", Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} {B00664BD-71EB-46C1-957E-CD851418D395} = {B00664BD-71EB-46C1-957E-CD851418D395} + {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_app_graphics", "uc2_graphics.vcproj", "{3CF31288-A44D-4C78-A3AA-B05B6E32DF11}" @@ -136,11 +136,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_app_graphics", "uc2 Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {D3D21F11-A7E7-4EA2-8518-E24695133BFF} = {D3D21F11-A7E7-4EA2-8518-E24695133BFF} - {C4165626-F68F-4F66-A126-3B82DDBB7480} = {C4165626-F68F-4F66-A126-3B82DDBB7480} - {5F065EAC-B881-4E9A-9E34-7A21D7A01D98} = {5F065EAC-B881-4E9A-9E34-7A21D7A01D98} - {814EBFD3-3CE6-4933-A580-C1FE3147ACB4} = {814EBFD3-3CE6-4933-A580-C1FE3147ACB4} {B00664BD-71EB-46C1-957E-CD851418D395} = {B00664BD-71EB-46C1-957E-CD851418D395} + {814EBFD3-3CE6-4933-A580-C1FE3147ACB4} = {814EBFD3-3CE6-4933-A580-C1FE3147ACB4} + {5F065EAC-B881-4E9A-9E34-7A21D7A01D98} = {5F065EAC-B881-4E9A-9E34-7A21D7A01D98} + {C4165626-F68F-4F66-A126-3B82DDBB7480} = {C4165626-F68F-4F66-A126-3B82DDBB7480} + {D3D21F11-A7E7-4EA2-8518-E24695133BFF} = {D3D21F11-A7E7-4EA2-8518-E24695133BFF} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgraphics2", "libgraphics2.vcproj", "{814EBFD3-3CE6-4933-A580-C1FE3147ACB4}" @@ -155,8 +155,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "multi_thread", "multi_threa Release.AspNetCompiler.Debug = "False" EndProjectSection ProjectSection(ProjectDependencies) = postProject - {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} {B00664BD-71EB-46C1-957E-CD851418D395} = {B00664BD-71EB-46C1-957E-CD851418D395} + {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} = {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libboinc_staticcrt", "libboinc_staticcrt.vcproj", "{B00664BD-71EB-46C1-957E-CD851418D395}" @@ -171,6 +171,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libboincapi_staticcrt", "li Release.AspNetCompiler.Debug = "False" EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "boincsvcctrl", "boincsvcctrl.vcproj", "{9FC47E90-4E0D-4383-B446-A84314B00764}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {B06280CB-82A4-46DE-8956-602643078BDF} = {B06280CB-82A4-46DE-8956-602643078BDF} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -347,6 +356,14 @@ Global {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE}.Release|Win32.Build.0 = Release|Win32 {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE}.Release|x64.ActiveCfg = Release|x64 {07BDA8F7-4AAF-4A3B-B96E-EA72A143C5AE}.Release|x64.Build.0 = Release|x64 + {9FC47E90-4E0D-4383-B446-A84314B00764}.Debug|Win32.ActiveCfg = Debug|Win32 + {9FC47E90-4E0D-4383-B446-A84314B00764}.Debug|Win32.Build.0 = Debug|Win32 + {9FC47E90-4E0D-4383-B446-A84314B00764}.Debug|x64.ActiveCfg = Debug|x64 + {9FC47E90-4E0D-4383-B446-A84314B00764}.Debug|x64.Build.0 = Debug|x64 + {9FC47E90-4E0D-4383-B446-A84314B00764}.Release|Win32.ActiveCfg = Release|Win32 + {9FC47E90-4E0D-4383-B446-A84314B00764}.Release|Win32.Build.0 = Release|Win32 + {9FC47E90-4E0D-4383-B446-A84314B00764}.Release|x64.ActiveCfg = Release|x64 + {9FC47E90-4E0D-4383-B446-A84314B00764}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/win_build/boincsvcctrl.vcproj b/win_build/boincsvcctrl.vcproj new file mode 100644 index 0000000000..ca2c9c4421 --- /dev/null +++ b/win_build/boincsvcctrl.vcproj @@ -0,0 +1,1259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +