Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

ntservice.c

00001 /*****************************************************************************
00002  * ntservice.c: Windows NT/2K/XP service interface
00003  *****************************************************************************
00004  * Copyright (C) 2004 the VideoLAN team
00005  * $Id: ntservice.c 11664 2005-07-09 06:17:09Z courmisch $
00006  *
00007  * Authors: Gildas Bazin <[email protected]>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*****************************************************************************
00025  * Preamble
00026  *****************************************************************************/
00027 #include <stdlib.h>
00028 #include <vlc/vlc.h>
00029 #include <vlc/intf.h>
00030 
00031 #define VLCSERVICENAME "VLC media player"
00032 
00033 /*****************************************************************************
00034  * Module descriptor
00035  *****************************************************************************/
00036 static int  Activate( vlc_object_t * );
00037 static void Close   ( vlc_object_t * );
00038 
00039 #define INSTALL_TEXT N_( "Install Windows Service" )
00040 #define INSTALL_LONGTEXT N_( \
00041     "If enabled the interface will install the Service and exit." )
00042 #define UNINSTALL_TEXT N_( "Uninstall Windows Service" )
00043 #define UNINSTALL_LONGTEXT N_( \
00044     "If enabled the interface will uninstall the Service and exit." )
00045 #define NAME_TEXT N_( "Display name of the Service" )
00046 #define NAME_LONGTEXT N_( \
00047     "This allows you to change the display name of the Service." )
00048 #define OPTIONS_TEXT N_("Configuration options")
00049 #define OPTIONS_LONGTEXT N_( \
00050     "This option allows you to specify configuration options that will be " \
00051     "used by the Service (eg. --foo=bar --no-foobar). It should be specified "\
00052     "at install time so the Service is properly configured.")
00053 #define EXTRAINTF_TEXT N_("Extra interface modules")
00054 #define EXTRAINTF_LONGTEXT N_( \
00055     "This option allows you to select additional interfaces spawned by the " \
00056     "Service. It should be specified at install time so the Service is " \
00057     "properly configured. Use a comma separated list of interface modules. " \
00058     "(common values are: logger, sap, rc, http)")
00059 
00060 vlc_module_begin();
00061     set_shortname( _("NT Service"));
00062     set_description( _("Windows Service interface") );
00063     set_category( CAT_INTERFACE );
00064     set_subcategory( SUBCAT_INTERFACE_CONTROL );
00065     add_bool( "ntservice-install", 0, NULL,
00066               INSTALL_TEXT, INSTALL_LONGTEXT, VLC_TRUE );
00067     add_bool( "ntservice-uninstall", 0, NULL,
00068               UNINSTALL_TEXT, UNINSTALL_LONGTEXT, VLC_TRUE );
00069     add_string ( "ntservice-name", VLCSERVICENAME, NULL,
00070                  NAME_TEXT, NAME_LONGTEXT, VLC_TRUE );
00071     add_string ( "ntservice-options", NULL, NULL,
00072                  OPTIONS_TEXT, OPTIONS_LONGTEXT, VLC_TRUE );
00073     add_string ( "ntservice-extraintf", NULL, NULL,
00074                  EXTRAINTF_TEXT, EXTRAINTF_LONGTEXT, VLC_TRUE );
00075 
00076     set_capability( "interface", 0 );
00077     set_callbacks( Activate, Close );
00078 vlc_module_end();
00079 
00080 struct intf_sys_t
00081 {
00082     SERVICE_STATUS_HANDLE hStatus;
00083     SERVICE_STATUS status;
00084     char *psz_service;
00085 };
00086 
00087 /*****************************************************************************
00088  * Local prototypes
00089  *****************************************************************************/
00090 static void Run( intf_thread_t *p_intf );
00091 static int NTServiceInstall( intf_thread_t *p_intf );
00092 static int NTServiceUninstall( intf_thread_t *p_intf );
00093 static void WINAPI ServiceDispatch( DWORD numArgs, char **args );
00094 static void WINAPI ServiceCtrlHandler( DWORD control );
00095 
00096 /* We need this global */
00097 static intf_thread_t *p_global_intf;
00098 
00099 /*****************************************************************************
00100  * Activate: initialize and create stuff
00101  *****************************************************************************/
00102 static int Activate( vlc_object_t *p_this )
00103 {
00104     intf_thread_t *p_intf = (intf_thread_t*)p_this;
00105 
00106     /* Only works on NT/2K/XP */
00107     if( !IS_WINNT ) return VLC_EGENERIC;
00108 
00109     p_intf->pf_run = Run;
00110     return VLC_SUCCESS;
00111 }
00112 
00113 /*****************************************************************************
00114  * Close: destroy interface
00115  *****************************************************************************/
00116 void Close( vlc_object_t *p_this )
00117 {
00118 }
00119 
00120 /*****************************************************************************
00121  * Run: interface thread
00122  *****************************************************************************/
00123 static void Run( intf_thread_t *p_intf )
00124 {
00125     intf_thread_t *p_extraintf;
00126     SERVICE_TABLE_ENTRY dispatchTable[] =
00127     {
00128         { VLCSERVICENAME, &ServiceDispatch },
00129         { NULL, NULL }
00130     };
00131 
00132     p_global_intf = p_intf;
00133     p_intf->p_sys = alloca( sizeof( intf_sys_t ) );
00134     p_intf->p_sys->psz_service = config_GetPsz( p_intf, "ntservice-name" );
00135     p_intf->p_sys->psz_service = p_intf->p_sys->psz_service ?
00136         p_intf->p_sys->psz_service : strdup(VLCSERVICENAME);
00137 
00138     if( config_GetInt( p_intf, "ntservice-install" ) )
00139     {
00140         NTServiceInstall( p_intf );
00141         return;
00142     }
00143 
00144     if( config_GetInt( p_intf, "ntservice-uninstall" ) )
00145     {
00146         NTServiceUninstall( p_intf );
00147         return;
00148     }
00149 
00150     if( StartServiceCtrlDispatcher( dispatchTable ) == 0 )
00151     {
00152         msg_Err( p_intf, "StartServiceCtrlDispatcher failed" );
00153     }
00154 
00155     free( p_intf->p_sys->psz_service );
00156 
00157     /* Stop and destroy the interfaces we spawned */
00158     while( (p_extraintf = vlc_object_find(p_intf, VLC_OBJECT_INTF, FIND_CHILD)))
00159     {
00160         intf_StopThread( p_extraintf );
00161         vlc_object_detach( p_extraintf );
00162         vlc_object_release( p_extraintf );
00163         intf_Destroy( p_extraintf );
00164     }
00165 
00166     /* Make sure we exit (In case other interfaces have been spawned) */
00167     p_intf->p_vlc->b_die = VLC_TRUE;
00168 }
00169 
00170 /*****************************************************************************
00171  * NT Service utility functions
00172  *****************************************************************************/
00173 static int NTServiceInstall( intf_thread_t *p_intf )
00174 {
00175     intf_sys_t *p_sys  = p_intf->p_sys;
00176     char psz_path[10*MAX_PATH], psz_pathtmp[MAX_PATH], *psz_extra;
00177     SC_HANDLE handle = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
00178     if( handle == NULL )
00179     {
00180         msg_Err( p_intf, "could not connect to SCM database" );
00181         return VLC_EGENERIC;
00182     }
00183 
00184     /* Find out the filename of ourselves so we can install it to the
00185      * service control manager */
00186     GetModuleFileName( NULL, psz_pathtmp, MAX_PATH );
00187     sprintf( psz_path, "\"%s\" -I "MODULE_STRING, psz_pathtmp );
00188 
00189     psz_extra = config_GetPsz( p_intf, "ntservice-extraintf" );
00190     if( psz_extra && *psz_extra )
00191     {
00192         strcat( psz_path, " --ntservice-extraintf " );
00193         strcat( psz_path, psz_extra );
00194     }
00195     if( psz_extra ) free( psz_extra );
00196 
00197     psz_extra = config_GetPsz( p_intf, "ntservice-options" );
00198     if( psz_extra && *psz_extra )
00199     {
00200         strcat( psz_path, " " );
00201         strcat( psz_path, psz_extra );
00202     }
00203     if( psz_extra ) free( psz_extra );
00204 
00205     SC_HANDLE service =
00206         CreateService( handle, p_sys->psz_service, p_sys->psz_service,
00207                        GENERIC_READ | GENERIC_EXECUTE,
00208                        SERVICE_WIN32_OWN_PROCESS,
00209                        SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
00210                        psz_path, NULL, NULL, NULL, NULL, NULL );
00211     if( service == NULL )
00212     {
00213         if( GetLastError() != ERROR_SERVICE_EXISTS )
00214         {
00215             msg_Err( p_intf, "could not create new service: \"%s\" (%s)",
00216                      p_sys->psz_service ,psz_path );
00217             CloseServiceHandle( handle );
00218             return VLC_EGENERIC;
00219         }
00220         else
00221         {
00222             msg_Warn( p_intf, "service \"%s\" already exists",
00223                       p_sys->psz_service );
00224         }
00225     }
00226     else
00227     {
00228         msg_Warn( p_intf, "service successfuly created" );
00229     }
00230 
00231     if( service ) CloseServiceHandle( service );
00232     CloseServiceHandle( handle );
00233 
00234     return VLC_SUCCESS;
00235 }
00236 
00237 static int NTServiceUninstall( intf_thread_t *p_intf )
00238 {
00239     intf_sys_t *p_sys  = p_intf->p_sys;
00240 
00241     SC_HANDLE handle = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
00242     if( handle == NULL )
00243     {
00244         msg_Err( p_intf, "could not connect to SCM database" );
00245         return VLC_EGENERIC;
00246     }
00247 
00248     /* First, open a handle to the service */
00249     SC_HANDLE service = OpenService( handle, p_sys->psz_service, DELETE );
00250     if( service == NULL )
00251     {
00252         msg_Err( p_intf, "could not open service" );
00253         CloseServiceHandle( handle );
00254         return VLC_EGENERIC;
00255     }
00256 
00257     /* Remove the service */
00258     if( !DeleteService( service ) )
00259     {
00260         msg_Err( p_intf, "could not delete service \"%s\"",
00261                  p_sys->psz_service );
00262     }
00263     else
00264     {
00265         msg_Dbg( p_intf, "service deleted successfuly" );
00266     }
00267 
00268     CloseServiceHandle( service );
00269     CloseServiceHandle( handle );
00270 
00271     return VLC_SUCCESS;
00272 }
00273 
00274 static void WINAPI ServiceDispatch( DWORD numArgs, char **args )
00275 {
00276     intf_thread_t *p_intf = (intf_thread_t *)p_global_intf;
00277     intf_sys_t    *p_sys  = p_intf->p_sys;
00278     char *psz_modules, *psz_parser;
00279 
00280     /* We have to initialize the service-specific stuff */
00281     memset( &p_sys->status, 0, sizeof(SERVICE_STATUS) );
00282     p_sys->status.dwServiceType = SERVICE_WIN32;
00283     p_sys->status.dwCurrentState = SERVICE_START_PENDING;
00284     p_sys->status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
00285 
00286     p_sys->hStatus =
00287         RegisterServiceCtrlHandler( p_sys->psz_service, &ServiceCtrlHandler );
00288     if( p_sys->hStatus == (SERVICE_STATUS_HANDLE)0 )
00289     {
00290         msg_Err( p_intf, "failed to register service control handler" );
00291         return;
00292     }
00293 
00294     /*
00295      * Load background interfaces
00296      */
00297     psz_modules = config_GetPsz( p_intf, "ntservice-extraintf" );
00298     psz_parser = psz_modules;
00299     while( psz_parser && *psz_parser )
00300     {
00301         char *psz_module, *psz_temp;
00302         psz_module = psz_parser;
00303         psz_parser = strchr( psz_module, ',' );
00304         if( psz_parser )
00305         {
00306             *psz_parser = '\0';
00307             psz_parser++;
00308         }
00309         psz_temp = (char *)malloc( strlen(psz_module) + sizeof(",none") );
00310         if( psz_temp )
00311         {
00312             intf_thread_t *p_new_intf;
00313             sprintf( psz_temp, "%s,none", psz_module );
00314 
00315             /* Try to create the interface */
00316             p_new_intf = intf_Create( p_intf, psz_temp );
00317             if( p_new_intf == NULL )
00318             {
00319                 msg_Err( p_intf, "interface \"%s\" initialization failed",
00320                          psz_temp );
00321                 free( psz_temp );
00322                 continue;
00323             }
00324 
00325             /* Try to run the interface */
00326             p_new_intf->b_block = VLC_FALSE;
00327             if( intf_RunThread( p_new_intf ) )
00328             {
00329                 vlc_object_detach( p_new_intf );
00330                 intf_Destroy( p_new_intf );
00331                 msg_Err( p_intf, "interface \"%s\" cannot run", psz_temp );
00332             }
00333 
00334             free( psz_temp );
00335         }
00336     }
00337     if( psz_modules )
00338     {
00339         free( psz_modules );
00340     }
00341 
00342     /* Initialization complete - report running status */
00343     p_sys->status.dwCurrentState = SERVICE_RUNNING;
00344     p_sys->status.dwCheckPoint   = 0;
00345     p_sys->status.dwWaitHint     = 0;
00346 
00347     SetServiceStatus( p_sys->hStatus, &p_sys->status );
00348 }
00349 
00350 static void WINAPI ServiceCtrlHandler( DWORD control )
00351 {
00352     intf_thread_t *p_intf = (intf_thread_t *)p_global_intf;
00353     intf_sys_t    *p_sys  = p_intf->p_sys;
00354 
00355     switch( control )
00356     {
00357     case SERVICE_CONTROL_SHUTDOWN:
00358     case SERVICE_CONTROL_STOP:
00359         p_sys->status.dwCurrentState = SERVICE_STOPPED;
00360         p_sys->status.dwWin32ExitCode = 0;
00361         p_sys->status.dwCheckPoint = 0;
00362         p_sys->status.dwWaitHint = 0;
00363         break;
00364     case SERVICE_CONTROL_INTERROGATE:
00365         /* just set the current state to whatever it is... */
00366         break;
00367     }
00368 
00369     SetServiceStatus( p_sys->hStatus, &p_sys->status );
00370 }

Generated on Tue Dec 20 10:14:31 2005 for vlc-0.8.4a by  doxygen 1.4.2