text: /*
   *  Unix SMB/CIFS implementation.
   *  RPC Pipe client / server routines
   *  Copyright (C) Gerald Carter                   2005.
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or
   *  (at your option) any later version.
   *
   *  This program 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 General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program; if not, write to the Free Software
   *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  
  #include "includes.h"
  #include "rpc_client.h"
  
  struct svc_state_msg {
  	uint32 flag;
  	const char *message;
  };
  
  static struct svc_state_msg state_msg_table[] = {
  	{ SVCCTL_STOPPED,            "stopped" },
  	{ SVCCTL_START_PENDING,      "start pending" },
  	{ SVCCTL_STOP_PENDING,       "stop pending" },
  	{ SVCCTL_RUNNING,            "running" },
  	{ SVCCTL_CONTINUE_PENDING,   "resume pending" },
  	{ SVCCTL_PAUSE_PENDING,      "pause pending" },
  	{ SVCCTL_PAUSED,             "paused" },
  	{ 0,                          NULL }
  };
  	
  
  /********************************************************************
  ********************************************************************/
  const char* svc_status_string( uint32 state )
  {
  	static fstring msg;
  	int i;
  	
  	fstr_sprintf( msg, "Unknown State [%d]", state );
  	
  	for ( i=0; state_msg_table[i].message; i++ ) {
  		if ( state_msg_table[i].flag == state ) {
  			fstrcpy( msg, state_msg_table[i].message );
  			break;	
  		}
  	}
  	
  	return msg;
  }
  
  /********************************************************************
  ********************************************************************/
  
  WERROR rpccli_svcctl_open_scm(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, 
                                POLICY_HND *hSCM, uint32 access_desired )
  {
  	SVCCTL_Q_OPEN_SCMANAGER in;
  	SVCCTL_R_OPEN_SCMANAGER out;
  	prs_struct qbuf, rbuf;
  	fstring server;
  	
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	/* leave the database name NULL to get the default service db */
  
  	in.database = NULL;
  
  	/* set the server name */
  
  	if ( !(in.servername = TALLOC_P( mem_ctx, UNISTR2 )) )
  		return WERR_NOMEM;
  	fstr_sprintf( server, "\\\\%s", cli->cli->desthost );
  	init_unistr2( in.servername, server, UNI_STR_TERMINATE );
  
  	in.access = access_desired;
  	
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_OPEN_SCMANAGER_W, 
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_open_scmanager,
  	            svcctl_io_r_open_scmanager, 
  	            WERR_GENERAL_FAILURE );
  	
  	if ( !W_ERROR_IS_OK( out.status ) )
  		return out.status;
  
  	memcpy( hSCM, &out.handle, sizeof(POLICY_HND) );
  	
  	return out.status;
  }
  
  /********************************************************************
  ********************************************************************/
  
  WERROR rpccli_svcctl_open_service( struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, 
                                  POLICY_HND *hSCM, POLICY_HND *hService, 
  				const char *servicename, uint32 access_desired )
  {
  	SVCCTL_Q_OPEN_SERVICE in;
  	SVCCTL_R_OPEN_SERVICE out;
  	prs_struct qbuf, rbuf;
  	
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	memcpy( &in.handle, hSCM, sizeof(POLICY_HND) );
  	init_unistr2( &in.servicename, servicename, UNI_STR_TERMINATE );
  	in.access = access_desired;
  	
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_OPEN_SERVICE_W, 
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_open_service,
  	            svcctl_io_r_open_service, 
  	            WERR_GENERAL_FAILURE );
  	
  	if ( !W_ERROR_IS_OK( out.status ) )
  		return out.status;
  
  	memcpy( hService, &out.handle, sizeof(POLICY_HND) );
  	
  	return out.status;
  }
  
  /********************************************************************
  ********************************************************************/
  
  WERROR rpccli_svcctl_close_service(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, POLICY_HND *hService )
  {
  	SVCCTL_Q_CLOSE_SERVICE in;
  	SVCCTL_R_CLOSE_SERVICE out;
  	prs_struct qbuf, rbuf;
  	
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	memcpy( &in.handle, hService, sizeof(POLICY_HND) );
  	
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_CLOSE_SERVICE, 
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_close_service,
  	            svcctl_io_r_close_service, 
  	            WERR_GENERAL_FAILURE );
  
  	return out.status;
  }
  
  /*******************************************************************
  *******************************************************************/
  
  WERROR rpccli_svcctl_enumerate_services( struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
                                        POLICY_HND *hSCM, uint32 type, uint32 state, 
  				      uint32 *returned, ENUM_SERVICES_STATUS **service_array  )
  {
  	SVCCTL_Q_ENUM_SERVICES_STATUS in;
  	SVCCTL_R_ENUM_SERVICES_STATUS out;
  	prs_struct qbuf, rbuf;
  	uint32 resume = 0;
  	ENUM_SERVICES_STATUS *services;
  	int i;
  
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	/* setup the request */
  	
  	memcpy( &in.handle, hSCM, sizeof(POLICY_HND) );
  	
  	in.type        = type;
  	in.state       = state;
  	in.resume      = &resume;
  	
  	/* first time is to get the buffer size */
  	in.buffer_size = 0;
  
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_ENUM_SERVICES_STATUS_W, 
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_enum_services_status,
  	            svcctl_io_r_enum_services_status, 
  	            WERR_GENERAL_FAILURE );
  
  	/* second time with correct buffer size...should be ok */
  	
  	if ( W_ERROR_EQUAL( out.status, WERR_MORE_DATA ) ) {
  		in.buffer_size = out.needed;
  
  		CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_ENUM_SERVICES_STATUS_W, 
  		            in, out, 
  		            qbuf, rbuf,
  		            svcctl_io_q_enum_services_status,
  		            svcctl_io_r_enum_services_status, 
  		            WERR_GENERAL_FAILURE );
  	}
  	
  	if ( !W_ERROR_IS_OK(out.status) ) 
  		return out.status;
  		
  	/* pull out the data */
  	if (out.returned) {
  		if ( !(services = TALLOC_ARRAY( mem_ctx, ENUM_SERVICES_STATUS, out.returned )) ) 
  			return WERR_NOMEM;
  	} else {
  		services = NULL;
  	}
  		
  	for ( i=0; i<out.returned; i++ ) {
  		svcctl_io_enum_services_status( "", &services[i], &out.buffer, 0 );
  	}
  	
  	*service_array = services;
  	*returned      = out.returned;
  	
  	return out.status;
  }
  
  /*******************************************************************
  *******************************************************************/
  
  WERROR rpccli_svcctl_query_status( struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
                                  POLICY_HND *hService, SERVICE_STATUS *status )
  {
  	SVCCTL_Q_QUERY_STATUS in;
  	SVCCTL_R_QUERY_STATUS out;
  	prs_struct qbuf, rbuf;
  	
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	memcpy( &in.handle, hService, sizeof(POLICY_HND) );
  	
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_QUERY_STATUS, 
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_query_status,
  	            svcctl_io_r_query_status, 
  	            WERR_GENERAL_FAILURE );
  	
  	if ( !W_ERROR_IS_OK( out.status ) )
  		return out.status;
  
  	memcpy( status, &out.svc_status, sizeof(SERVICE_STATUS) );
  	
  	return out.status;
  }
  
  /*******************************************************************
  *******************************************************************/
  
  WERROR rpccli_svcctl_query_config(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
                                  POLICY_HND *hService, SERVICE_CONFIG *config )
  {
  	SVCCTL_Q_QUERY_SERVICE_CONFIG in;
  	SVCCTL_R_QUERY_SERVICE_CONFIG out;
  	prs_struct qbuf, rbuf;
  	
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	memcpy( &in.handle, hService, sizeof(POLICY_HND) );
  	in.buffer_size = 0;
  	
  	
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_QUERY_SERVICE_CONFIG_W, 
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_query_service_config,
  	            svcctl_io_r_query_service_config, 
  	            WERR_GENERAL_FAILURE );
  	
  	if ( W_ERROR_EQUAL( out.status, WERR_INSUFFICIENT_BUFFER ) ) {
  		in.buffer_size = out.needed;
  
  		CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_QUERY_SERVICE_CONFIG_W,
  		            in, out, 
  		            qbuf, rbuf,
  		            svcctl_io_q_query_service_config,
  		            svcctl_io_r_query_service_config, 
  		            WERR_GENERAL_FAILURE );
  	}
  	
  	if ( !W_ERROR_IS_OK( out.status ) )
  		return out.status;
  
  	memcpy( config, &out.config, sizeof(SERVICE_CONFIG) );
  	
  	config->executablepath = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  	config->loadordergroup = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  	config->dependencies   = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  	config->startname      = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  	config->displayname    = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  	
  	if ( out.config.executablepath ) {
  		config->executablepath = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  		copy_unistr2( config->executablepath, out.config.executablepath );
  	}
  
  	if ( out.config.loadordergroup ) {
  		config->loadordergroup = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  		copy_unistr2( config->loadordergroup, out.config.loadordergroup );
  	}
  
  	if ( out.config.dependencies ) {
  		config->dependencies = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  		copy_unistr2( config->dependencies, out.config.dependencies );
  	}
  
  	if ( out.config.startname ) {
  		config->startname = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  		copy_unistr2( config->startname, out.config.startname );
  	}
  
  	if ( out.config.displayname ) {
  		config->displayname = TALLOC_ZERO_P( mem_ctx, UNISTR2 );
  		copy_unistr2( config->displayname, out.config.displayname );
  	}
  	
  	return out.status;
  }
  
  /*******************************************************************
  *******************************************************************/
  
  WERROR rpccli_svcctl_start_service( struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
                                   POLICY_HND *hService,
                                   const char **parm_array, uint32 parmcount )
  {
  	SVCCTL_Q_START_SERVICE in;
  	SVCCTL_R_START_SERVICE out;
  	prs_struct qbuf, rbuf;
  	
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	memcpy( &in.handle, hService, sizeof(POLICY_HND) );
  	
  	in.parmcount  = 0;
  	in.parameters = NULL;
  	
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_START_SERVICE_W,
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_start_service,
  	            svcctl_io_r_start_service,
  	            WERR_GENERAL_FAILURE );
  	
  	return out.status;
  }
  
  /*******************************************************************
  *******************************************************************/
  
  WERROR rpccli_svcctl_control_service( struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
                                     POLICY_HND *hService, uint32 control,
  				   SERVICE_STATUS *status )
  {
  	SVCCTL_Q_CONTROL_SERVICE in;
  	SVCCTL_R_CONTROL_SERVICE out;
  	prs_struct qbuf, rbuf;
  	
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	memcpy( &in.handle, hService, sizeof(POLICY_HND) );
  	in.control = control;
  	
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_CONTROL_SERVICE, 
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_control_service,
  	            svcctl_io_r_control_service,
  	            WERR_GENERAL_FAILURE );
  	
  	if ( !W_ERROR_IS_OK( out.status ) )
  		return out.status;
  
  	memcpy( status, &out.svc_status, sizeof(SERVICE_STATUS) );
  	
  	return out.status;
  }
  
  
  /*******************************************************************
  *******************************************************************/
  
  WERROR rpccli_svcctl_get_dispname( struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, 
                                  POLICY_HND *hService, fstring displayname )
  {
  	SVCCTL_Q_GET_DISPLAY_NAME in;
  	SVCCTL_R_GET_DISPLAY_NAME out;
  	prs_struct qbuf, rbuf;
  	
  	ZERO_STRUCT(in);
  	ZERO_STRUCT(out);
  	
  	memcpy( &in.handle, hService, sizeof(POLICY_HND) );
  	in.display_name_len = 0;
  	
  	CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_GET_DISPLAY_NAME, 
  	            in, out, 
  	            qbuf, rbuf,
  	            svcctl_io_q_get_display_name,
  	            svcctl_io_r_get_display_name, 
  	            WERR_GENERAL_FAILURE );
  	
  	/* second time with correct buffer size...should be ok */
  	
  	if ( W_ERROR_EQUAL( out.status, WERR_INSUFFICIENT_BUFFER ) ) {
  		in.display_name_len = out.display_name_len;
  
  		CLI_DO_RPC_WERR( cli, mem_ctx, PI_SVCCTL, SVCCTL_GET_DISPLAY_NAME, 
  		            in, out, 
  		            qbuf, rbuf,
  		            svcctl_io_q_get_display_name,
  		            svcctl_io_r_get_display_name, 
  		            WERR_GENERAL_FAILURE );
  	}
  
  	if ( !W_ERROR_IS_OK( out.status ) )
  		return out.status;
  
  	rpcstr_pull( displayname, out.displayname.buffer, sizeof(displayname), -1, STR_TERMINATE );
  	
  	return out.status;
  }
  