From d559edcce23ada96c4a788c3dbeb66c2890db054 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Tue, 14 Jun 2005 03:17:31 +0000 Subject: r7563: svcctl patches from Marcin; have cleaned up formating and am checking the code in to snapshot it before I start changing more things (This used to be commit 560ce111ce8de37d02bce64d2ca60a5f471d5477) --- source3/rpc_server/srv_svcctl.c | 51 +- source3/rpc_server/srv_svcctl_nt.c | 1381 +++++++++++++++++++++++++++++++++--- 2 files changed, 1350 insertions(+), 82 deletions(-) (limited to 'source3/rpc_server') diff --git a/source3/rpc_server/srv_svcctl.c b/source3/rpc_server/srv_svcctl.c index 85fb9f9ce3..6ba26414d3 100644 --- a/source3/rpc_server/srv_svcctl.c +++ b/source3/rpc_server/srv_svcctl.c @@ -166,7 +166,29 @@ static BOOL api_svcctl_enum_services_status(pipes_struct *p) return True; } +/******************************************************************* + ********************************************************************/ + +static BOOL api_svcctl_query_service_status_ex(pipes_struct *p) +{ + SVCCTL_Q_QUERY_SERVICE_STATUSEX q_u; + SVCCTL_R_QUERY_SERVICE_STATUSEX r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + if(!svcctl_io_q_query_service_status_ex("", &q_u, data, 0)) + return False; + + r_u.status = _svcctl_query_service_status_ex(p, &q_u, &r_u); + + if(!svcctl_io_r_query_service_status_ex("", &r_u, rdata, 0)) + return False; + + return True; +} /******************************************************************* ********************************************************************/ @@ -263,6 +285,30 @@ static BOOL api_svcctl_query_service_config(pipes_struct *p) return True; } +/******************************************************************* + ********************************************************************/ + +static BOOL api_svcctl_query_service_config2(pipes_struct *p) +{ + SVCCTL_Q_QUERY_SERVICE_CONFIG2 q_u; + SVCCTL_R_QUERY_SERVICE_CONFIG2 r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + if(!svcctl_io_q_query_service_config2("", &q_u, data, 0)) + return False; + + r_u.status = _svcctl_query_service_config2(p, &q_u, &r_u); + + if(!svcctl_io_r_query_service_config2("", &r_u, rdata, 0)) + return False; + + return True; +} + /******************************************************************* \PIPE\svcctl commands ********************************************************************/ @@ -275,12 +321,15 @@ static struct api_struct api_svcctl_cmds[] = { "SVCCTL_GET_DISPLAY_NAME" , SVCCTL_GET_DISPLAY_NAME , api_svcctl_get_display_name }, { "SVCCTL_QUERY_STATUS" , SVCCTL_QUERY_STATUS , api_svcctl_query_status }, { "SVCCTL_QUERY_SERVICE_CONFIG_W", SVCCTL_QUERY_SERVICE_CONFIG_W, api_svcctl_query_service_config }, + { "SVCCTL_QUERY_SERVICE_CONFIG2_W", SVCCTL_QUERY_SERVICE_CONFIG2_W, api_svcctl_query_service_config2 }, { "SVCCTL_ENUM_SERVICES_STATUS_W", SVCCTL_ENUM_SERVICES_STATUS_W, api_svcctl_enum_services_status }, { "SVCCTL_ENUM_DEPENDENT_SERVICES_W", SVCCTL_ENUM_DEPENDENT_SERVICES_W, api_svcctl_enum_dependent_services }, { "SVCCTL_START_SERVICE_W" , SVCCTL_START_SERVICE_W , api_svcctl_start_service }, - { "SVCCTL_CONTROL_SERVICE" , SVCCTL_CONTROL_SERVICE , api_svcctl_control_service } + { "SVCCTL_CONTROL_SERVICE" , SVCCTL_CONTROL_SERVICE , api_svcctl_control_service }, + { "SVCCTL_QUERY_SERVICE_STATUSEX_W", SVCCTL_QUERY_SERVICE_STATUSEX_W, api_svcctl_query_service_status_ex } }; + void svcctl_get_pipe_fns( struct api_struct **fns, int *n_fns ) { *fns = api_svcctl_cmds; diff --git a/source3/rpc_server/srv_svcctl_nt.c b/source3/rpc_server/srv_svcctl_nt.c index a76e68a312..1feb0f66e4 100644 --- a/source3/rpc_server/srv_svcctl_nt.c +++ b/source3/rpc_server/srv_svcctl_nt.c @@ -18,81 +18,595 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* TODO - Do the OpenService service name matching case-independently, or at least make it an option. */ + + #include "includes.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_SRV +#define SERVICEDB_VERSION_V1 1 /* Will there be more? */ +#define INTERNAL_SERVICES_LIST "NETLOGON Spooler" + +/* */ +/* scripts will execute from the following libdir, if they are in the enable svcctl= */ +/* these should likely be symbolic links. Note that information about them will be extracted from the files themselves */ +/* using the LSB standard keynames for various information */ + +#define SVCCTL_SCRIPT_DIR "/svcctl/" + /* * sertup the \PIPE\svcctl db API */ #define SCVCTL_DATABASE_VERSION_V1 1 +TALLOC_CTX *svcdb=NULL; +static TDB_CONTEXT *service_tdb; /* used for services tdb file */ + +/* there are two types of services -- internal, and external. + Internal services are "built-in" to samba -- there may be + functions that exist to provide the control and enumeration + functions. There certainly is information returned to be + displayed in the typical management console. + + External services are those that can be specified in the smb.conf + file -- and they conform to the LSB specification as to having + particular keywords in the scripts. Note that these "scripts" are + located in the lib directory, and are likely links to LSB-compliant + init.d scripts, such as those that might come with Suse. Note + that the spec is located http://www.linuxbase.org/spec/ */ + + + +/* Expand this to include what can and can't be done + with a particular internal service. Expand as necessary + to add other infromation like what can be controlled, + etc. */ + +typedef struct Internal_service_struct +{ + const char *filename; /* internal name "index" */ + const char *displayname; + const char *description; + const uint32 statustype; + void *status_fn; + void *control_fn; +} Internal_service_description; + + +static const Internal_service_description ISD[] = { + { "NETLOGON", "Net Logon", "Provides logon and authentication service to the network", 0x110, NULL, NULL}, + { "Spooler", "Spooler", "Printing Services", 0x0020, NULL, NULL}, + { NULL, NULL, NULL, 0, NULL, NULL} +}; + /******************************************************************** + TODOs + (a) get and set security descriptors on services + (b) read and write QUERY_SERVICE_CONFIG structures (both kinds, country and western) + (c) create default secdesc objects for services and SCM + (d) check access control masks with se_access_check() ********************************************************************/ -#if 0 /* unused static function and static variable*/ +/* parse a LSB init.d type file for things it provides, dependencies, descriptions, etc. */ + -static TDB_CONTEXT *svcctl_tdb; /* used for share security descriptors */ +/******************************************************************************* + Get the INTERNAL services information for the given service name. +*******************************************************************************/ -static BOOL init_svcctl_db( void ) +static BOOL _svcctl_get_internal_service_data(const Internal_service_description *isd, Service_info *si) { - static pid_t local_pid; - const char *vstring = "INFO/version"; - - /* see if we've already opened the tdb */ + ZERO_STRUCTP( si ); - if (svcctl_tdb && local_pid == sys_getpid()) - return True; + pstrcpy( si->servicename, isd->displayname); + pstrcpy( si->servicetype, "INTERNAL"); + pstrcpy( si->filename, isd->filename); + pstrcpy( si->provides, isd->displayname); + pstrcpy( si->description, isd->description); + pstrcpy( si->shortdescription, isd->description); - /* so open it */ - if ( !(svcctl_tdb = tdb_open_log(lock_path("svcctl.tdb"), 0, TDB_DEFAULT, - O_RDWR|O_CREAT, 0600))) - { - DEBUG(0,("Failed to open svcctl database %s (%s)\n", - lock_path("svcctl.tdb"), strerror(errno) )); + return True; +} + + +/******************************************************************************* + Get the services information by reading and parsing the shell scripts. These + are symbolically linked into the SVCCTL_SCRIPT_DIR directory. + + Get the names of the services/scripts to read from the smb.conf file. +*******************************************************************************/ + +static BOOL _svcctl_get_LSB_data(char *fname,Service_info *si ) +{ + pstring initdfile; + char mybuffer[256]; + const char *tokenptr; + char **qlines; + int fd = -1; + int nlines, *numlines,i,in_section,in_description; + + pstrcpy(si->servicename,""); + pstrcpy(si->servicetype,"EXTERNAL"); + pstrcpy(si->filename,fname); + pstrcpy(si->provides,""); + pstrcpy(si->dependencies,""); + pstrcpy(si->shouldstart,""); + pstrcpy(si->shouldstop,""); + pstrcpy(si->requiredstart,""); + pstrcpy(si->requiredstop,""); + pstrcpy(si->description,""); + pstrcpy(si->shortdescription,""); + + numlines = &nlines; + in_section = 0; + in_description = 0; + + + if( !fname || !*fname ) { + DEBUG(0, ("Must define an \"LSB-style init file\" to read.\n")); return False; } - - local_pid = sys_getpid(); - - /***** BEGIN Check the tdb version ******/ + pstrcpy(initdfile,dyn_LIBDIR); + pstrcat(initdfile,SVCCTL_SCRIPT_DIR); + pstrcat(initdfile,fname); + + /* TODO - should check to see if the file that we're trying to open is + actually a script. If it's NOT, we should do something like warn, + and not continue to try to find info we're looking for */ + + DEBUG(10, ("Opening [%s]\n", initdfile)); + fd = -1; + fd = open(initdfile,O_RDONLY); + *numlines = 0; + + if (fd == -1) { + DEBUG(10, ("Couldn't open [%s]\n", initdfile)); + return False; + } + + qlines = fd_lines_load(fd, numlines); + DEBUGADD(10, ("Lines returned = [%d]\n", *numlines)); + close(fd); + + + if (*numlines) { - tdb_lock_bystring(svcctl_tdb, vstring, 0); + for(i = 0; i < *numlines; i++) { + + DEBUGADD(10, ("Line[%d] = %s\n", i, qlines[i])); + if (!in_section && (0==strwicmp("### BEGIN INIT INFO", qlines[i]))) { + /* we now can look for params */ + DEBUGADD(10, ("Configuration information starts on line = [%d]\n", i)); + in_section = 1; + + } else if (in_section && (0==strwicmp("### END INIT INFO", qlines[i]))) { + DEBUGADD(10, ("Configuration information ends on line = [%d]\n", i)); + DEBUGADD(10, ("Description is [%s]\n", si->description)); + in_description = 0; + in_section = 0; + break; + } else if (in_section) { + tokenptr = qlines[i]; + if (in_description) { + DEBUGADD(10, ("Processing DESCRIPTION [%d]\n", *tokenptr)); + if (tokenptr && (*tokenptr=='#') && (*(tokenptr+1)=='\t')) { + DEBUGADD(10, ("Adding to DESCRIPTION [%d]\n", *tokenptr)); + pstrcat(si->description," "); + pstrcat(si->description,tokenptr+2); + continue; + } + in_description = 0; + DEBUGADD(10, ("Not a description!\n")); + } + if (!next_token(&tokenptr,mybuffer," \t",sizeof(mybuffer))) { + DEBUGADD(10, ("Invalid line [%d]\n", i)); + break; /* bad line? */ + } + if (0 != strncmp(mybuffer,"#",1)) { + DEBUGADD(10, ("Invalid line [%d], is %s\n", i,mybuffer)); + break; + } + if (!next_token(&tokenptr,mybuffer," \t",sizeof(mybuffer))) { + DEBUGADD(10, ("Invalid token on line [%d]\n", i)); + break; /* bad line? */ + } + DEBUGADD(10, ("Keyword is [%s]\n", mybuffer)); + if (0==strwicmp(mybuffer,"Description:")) { + while (tokenptr && *tokenptr && (strchr(" \t",*tokenptr))) { + tokenptr++; + } + pstrcpy(si->description,tokenptr); + DEBUGADD(10, ("FOUND DESCRIPTION! Data is [%s]\n", tokenptr)); + in_description = 1; + } else { + while (tokenptr && *tokenptr && (strchr(" \t",*tokenptr))) { + tokenptr++; + } + DEBUGADD(10, ("Data is [%s]\n", tokenptr)); + in_description = 0; + + /* save certain keywords, don't save others */ + if (0==strwicmp(mybuffer, "Provides:")) { + pstrcpy(si->provides,tokenptr); + pstrcpy(si->servicename,tokenptr); + } + + if (0==strwicmp(mybuffer, "Short-Description:")) { + pstrcpy(si->shortdescription,tokenptr); + } + + if (0==strwicmp(mybuffer, "Required-start:")) { + pstrcpy(si->requiredstart,tokenptr); + pstrcpy(si->dependencies,tokenptr); + } + + if (0==strwicmp(mybuffer, "Should-start:")) { + pstrcpy(si->shouldstart,tokenptr); + } + } + } + } + + file_lines_free(qlines); + return True; + } + + return False; +} + + +BOOL _svcctl_read_service_tdb_to_si(TDB_CONTEXT *stdb,char *service_name, Service_info *si) +{ + + pstring keystring; + TDB_DATA key_data; + + if ((stdb == NULL) || (si == NULL) || (service_name==NULL) || (*service_name == 0)) + return False; + + /* TODO - error handling -- what if the service isn't in the DB? */ + + pstr_sprintf(keystring,"SERVICE/%s/TYPE", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->servicetype,key_data.dptr,key_data.dsize); + si->servicetype[key_data.dsize] = 0; + + /* crude check to see if the service exists... */ + DEBUG(3,("Size of the TYPE field is %d\n",key_data.dsize)); + if (key_data.dsize == 0) + return False; + + pstr_sprintf(keystring,"SERVICE/%s/FILENAME", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->filename,key_data.dptr,key_data.dsize); + si->filename[key_data.dsize] = 0; + + pstr_sprintf(keystring,"SERVICE/%s/PROVIDES", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->provides,key_data.dptr,key_data.dsize); + si->provides[key_data.dsize] = 0; + strncpy(si->servicename,key_data.dptr,key_data.dsize); + si->servicename[key_data.dsize] = 0; + + + pstr_sprintf(keystring,"SERVICE/%s/DEPENDENCIES", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->dependencies,key_data.dptr,key_data.dsize); + si->dependencies[key_data.dsize] = 0; + + pstr_sprintf(keystring,"SERVICE/%s/SHOULDSTART", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->shouldstart,key_data.dptr,key_data.dsize); + si->shouldstart[key_data.dsize] = 0; + + pstr_sprintf(keystring,"SERVICE/%s/SHOULD_STOP", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->shouldstop,key_data.dptr,key_data.dsize); + si->shouldstop[key_data.dsize] = 0; + + pstr_sprintf(keystring,"SERVICE/%s/REQUIREDSTART", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->requiredstart,key_data.dptr,key_data.dsize); + si->requiredstart[key_data.dsize] = 0; + + pstr_sprintf(keystring,"SERVICE/%s/REQUIREDSTOP", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->requiredstop,key_data.dptr,key_data.dsize); + si->requiredstop[key_data.dsize] = 0; + + pstr_sprintf(keystring,"SERVICE/%s/DESCRIPTION", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->description,key_data.dptr,key_data.dsize); + si->description[key_data.dsize] = 0; + + pstr_sprintf(keystring,"SERVICE/%s/SHORTDESC", service_name); + key_data = tdb_fetch_bystring(stdb,keystring); + strncpy(si->shortdescription,key_data.dptr,key_data.dsize); + si->shortdescription[key_data.dsize] = 0; + + return True; +} + +/********************************************************************* + given a service nice name, find the underlying service name +*********************************************************************/ + +BOOL _svcctl_service_nicename_to_servicename(TDB_CONTEXT *stdb,pstring service_nicename, pstring servicename,int szsvcname) +{ + pstring keystring; + TDB_DATA key_data; + + if ((stdb == NULL) || (service_nicename==NULL) || (servicename == NULL)) + return False; + + pstr_sprintf(keystring,"SERVICE_NICENAME/%s", servicename); + + DEBUG(5, ("_svcctl_service_nicename_to_servicename: Looking for service name [%s], key [%s]\n", + service_nicename, keystring)); + + key_data = tdb_fetch_bystring(stdb,keystring); + + if (key_data.dsize == 0) { + DEBUG(5, ("_svcctl_service_nicename_to_servicename: [%s] Not found, tried key [%s]\n",service_nicename,keystring)); + return False; + } + + strncpy(servicename,key_data.dptr,szsvcname); + servicename[(key_data.dsize > szsvcname ? szsvcname : key_data.dsize)] = 0; + DEBUG(5, ("_svcctl_service_nicename_to_servicename: Found service name [%s], name is [%s]\n", + service_nicename,servicename)); + + return True; +} + +/********************************************************************* +*********************************************************************/ + +BOOL _svcctl_write_si_to_service_tdb(TDB_CONTEXT *stdb,char *service_name, Service_info *si) +{ + pstring keystring; + + /* Note -- when we write to the tdb, we "index" on the filename + field, not the nice name. when a service is "opened", it is + opened by the nice (SERVICENAME) name, not the file name. + So there needs to be a mapping from nice name back to the file name. */ + + if ((stdb == NULL) || (si == NULL) || (service_name==NULL) || (*service_name == 0)) + return False; + + + /* Store the nicename */ + + pstr_sprintf(keystring,"SERVICE_NICENAME/%s", si->servicename); + tdb_store_bystring(stdb,keystring,string_tdb_data(service_name),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/TYPE", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->servicetype),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/FILENAME", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->filename),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/PROVIDES", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->provides),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/SERVICENAME", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->servicename),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/DEPENDENCIES", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->dependencies),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/SHOULDSTART", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->shouldstart),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/SHOULDSTOP", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->shouldstop),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/REQUIREDSTART", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->requiredstart),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/REQUIREDSTOP", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->requiredstop),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/DESCRIPTION", service_name); + tdb_store_bystring(stdb,keystring,string_tdb_data(si->description),TDB_REPLACE); + + pstr_sprintf(keystring,"SERVICE/%s/SHORTDESC", service_name); + tdb_lock_bystring(stdb, keystring, 0); + if (si->shortdescription && *si->shortdescription) + tdb_store_bystring(stdb,keystring,string_tdb_data(si->shortdescription),TDB_REPLACE); + else + tdb_store_bystring(stdb,keystring,string_tdb_data(si->description),TDB_REPLACE); + + return True; +} + +/**************************************************************************** + Create/Open the service control manager tdb. This code a clone of init_group_mapping. +****************************************************************************/ + +BOOL init_svcctl_db(void) +{ + const char *vstring = "INFO/version"; + uint32 vers_id; + char **svc_list; + char **svcname; + pstring keystring; + pstring external_service_list; + pstring internal_service_list; + Service_info si; + const Internal_service_description *isd_ptr; + /* svc_list = str_list_make( "etc/init.d/skeleton etc/init.d/syslog", NULL ); */ + svc_list=(char **)lp_enable_svcctl(); + + if (service_tdb) + return True; + + pstrcpy(external_service_list,""); + + service_tdb = tdb_open_log(lock_path("services.tdb"), 0, TDB_DEFAULT, O_RDWR, 0600); + if (!service_tdb) { + DEBUG(0,("Failed to open service db\n")); + service_tdb = tdb_open_log(lock_path("services.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (!service_tdb) return False; + DEBUG(0,("Created new services db\n")); + } + + if ((-1 == tdb_fetch_uint32(service_tdb, vstring,&vers_id)) || (vers_id != SERVICEDB_VERSION_V1)) { + /* wrong version of DB, or db was just created */ + tdb_traverse(service_tdb, tdb_traverse_delete_fn, NULL); + tdb_store_uint32(service_tdb, vstring, SERVICEDB_VERSION_V1); + } + tdb_unlock_bystring(service_tdb, vstring); + + DEBUG(0,("Initializing services db\n")); - if ( tdb_fetch_int32(svcctl_tdb, vstring) != SCVCTL_DATABASE_VERSION_V1 ) - tdb_store_int32(svcctl_tdb, vstring, SCVCTL_DATABASE_VERSION_V1); + svcname = svc_list; + + /* Get the EXTERNAL services as mentioned by line in smb.conf */ + + while (*svcname) { + DEBUG(10,("Reading information on service %s\n",*svcname)); + if (_svcctl_get_LSB_data(*svcname,&si));{ + /* write the information to the TDB */ + _svcctl_write_si_to_service_tdb(service_tdb,*svcname,&si); + /* definitely not efficient to do it this way. */ + pstrcat(external_service_list,"\""); + pstrcat(external_service_list,*svcname); + pstrcat(external_service_list,"\" "); + } + svcname++; + } + pstrcpy(keystring,"EXTERNAL_SERVICES"); + tdb_lock_bystring(service_tdb, keystring, 0); + DEBUG(8,("Storing external service list [%s]\n",external_service_list)); + tdb_store_bystring(service_tdb,keystring,string_tdb_data(external_service_list),TDB_REPLACE); + tdb_unlock_bystring(service_tdb,keystring); - tdb_unlock_bystring(svcctl_tdb, vstring); + /* Get the INTERNAL services */ - /***** END Check the tdb version ******/ + pstrcpy(internal_service_list,""); + isd_ptr = ISD; + + while (isd_ptr && (isd_ptr->filename)) { + DEBUG(10,("Reading information on service %s\n",isd_ptr->filename)); + if (_svcctl_get_internal_service_data(isd_ptr,&si)){ + /* write the information to the TDB */ + _svcctl_write_si_to_service_tdb(service_tdb,(char *)isd_ptr->filename,&si); + /* definitely not efficient to do it this way. */ + pstrcat(internal_service_list,"\""); + pstrcat(internal_service_list,isd_ptr->filename); + pstrcat(internal_service_list,"\" "); + + } + isd_ptr++; + } + pstrcpy(keystring,"INTERNAL_SERVICES"); + tdb_lock_bystring(service_tdb, keystring, 0); + DEBUG(8,("Storing internal service list [%s]\n",internal_service_list)); + tdb_store_bystring(service_tdb,keystring,string_tdb_data(internal_service_list),TDB_REPLACE); + tdb_unlock_bystring(service_tdb,keystring); return True; } -#endif +/* Service_info related functions */ +static Service_info *find_service_info_by_hnd(pipes_struct *p, POLICY_HND *handle) +{ + Service_info *info = NULL; -/******************************************************************** - TODO - (a) get and set security descriptors on services - (b) read and write QUERY_SERVICE_CONFIG structures - (c) create default secdesc objects for services and SCM - (d) check access control masks with se_access_check() - (e) implement SERVICE * for associating with open handles -********************************************************************/ + if(!(find_policy_by_hnd(p,handle,(void **)&info))) + DEBUG(2,("find_service_info_by_hnd: service not found.\n")); + + return info; +} + +static void free_service_info(void *ptr) +{ + Service_info *info = (Service_info *)ptr; + memset(info,'0',sizeof(Service_info)); + SAFE_FREE(info); +} + +/* SCM_info related functions */ +static void free_SCM_info(void *ptr) +{ + SCM_info *info = (SCM_info *)ptr; + memset(info->target_server_name, '0', sizeof(*(info->target_server_name))); + memset(info->target_db_name, '0', sizeof(*(info->target_db_name))); + memset(info, 0, sizeof(*(info))); + SAFE_FREE(info); +} + +static SCM_info *find_SCManager_info_by_hnd(pipes_struct *p, POLICY_HND *handle) +{ + SCM_info *info = NULL; + + if ( !(find_policy_by_hnd(p,handle,(void **)&info)) ) + DEBUG(2,("svcctl_find_SCManager_info_by_hnd: service not found.\n")); + + return info; +} + +static BOOL _svcctl_open_SCManager_hook(SCM_info *info) +{ + return True; +} + +static BOOL _svcctl_close_SCManager_hook(SCM_info *info) +{ + return True; +} /******************************************************************** ********************************************************************/ WERROR _svcctl_open_scmanager(pipes_struct *p, SVCCTL_Q_OPEN_SCMANAGER *q_u, SVCCTL_R_OPEN_SCMANAGER *r_u) { - /* just fake it for now */ - - if ( !create_policy_hnd( p, &r_u->handle, NULL, NULL ) ) - return WERR_ACCESS_DENIED; + /* create the DB of the services that we have */ + /* associate the information from the service opened in the create_policy_hnd string */ + + SCM_info *info = NULL; + fstring fhandle_string; + + if(!q_u || !r_u) + return WERR_NOMEM; + + if((info = SMB_MALLOC_P(SCM_info)) == NULL) + return WERR_NOMEM; + + ZERO_STRUCTP(info); + + info->type = SVC_HANDLE_IS_SCM; + + if(q_u->servername != 0) + unistr2_to_ascii(info->target_server_name, q_u->servername, sizeof(info->target_server_name)); + else { + /* if servername == NULL, use the local computer */ + pstrcpy(info->target_server_name, global_myname()); + } + DEBUG(10, ("_svcctl_open_scmanager: Using [%s] as the server name.\n", info->target_server_name)); + + if(q_u->database != 0) + unistr2_to_ascii(info->target_db_name, q_u->database, sizeof(info->target_db_name)); + else + pstrcpy(info->target_db_name, "ServicesActive"); + + if(!create_policy_hnd(p, &(r_u->handle), free_SCM_info, (void *)info)) + return WERR_NOMEM; + + policy_handle_to_string(&r_u->handle, &fhandle_string); + DEBUG(10, ("_svcctl_open_scmanager: Opening [%s] as the target services db, handle [%s]\n", info->target_db_name,fhandle_string)); + + if(!(_svcctl_open_SCManager_hook(info))) { + /* TODO - should we free the memory that may have been allocated with the policy handle? */ + return WERR_BADFILE; + } return WERR_OK; + } /******************************************************************** @@ -100,29 +614,116 @@ WERROR _svcctl_open_scmanager(pipes_struct *p, SVCCTL_Q_OPEN_SCMANAGER *q_u, SVC WERROR _svcctl_open_service(pipes_struct *p, SVCCTL_Q_OPEN_SERVICE *q_u, SVCCTL_R_OPEN_SERVICE *r_u) { - fstring service; + pstring service; + pstring service_filename; + fstring fhandle_string; + Service_info *info; + + if(!q_u || !r_u) + return WERR_NOMEM; + + if((info = SMB_MALLOC_P(Service_info)) == NULL) + return WERR_NOMEM; + + ZERO_STRUCTP(info); + + info->type = SVC_HANDLE_IS_SERVICE; rpcstr_pull(service, q_u->servicename.buffer, sizeof(service), q_u->servicename.uni_str_len*2, 0); - /* can only be called on service name (not displayname) */ + if (service_tdb == NULL) { + DEBUG(1, ("_svcctl_open_service: Cannot open Service [%s], the service database is not open; handle [%s]\n", service,fhandle_string)); + return WERR_ACCESS_DENIED; + } + DEBUG(1, ("_svcctl_open_service: Attempting to open Service [%s], \n", service)); + +#if 0 + if ( !_svcctl_service_nicename_to_servicename(service_tdb, service, service_filename, sizeof(pstring)) ) { + DEBUG(1, ("_svcctl_open_service: Cannot open Service [%s], the service can't be found\n", service)); + return WERR_NO_SUCH_SERVICE; + } +#else + pstrcpy(service_filename,service); +#endif + if (_svcctl_read_service_tdb_to_si(service_tdb,service, info)) + DEBUG(1, ("_svcctl_open_service: Found service [%s], servicename [%s], \n", service, info->servicename)); + else + return WERR_NO_SUCH_SERVICE; + +#if 0 if ( !(strequal( service, "NETLOGON") || strequal(service, "Spooler")) ) return WERR_NO_SUCH_SERVICE; - - if ( !create_policy_hnd( p, &r_u->handle, NULL, NULL ) ) +#endif + if ( !create_policy_hnd( p, &(r_u->handle), free_service_info, (void *)info ) ) return WERR_ACCESS_DENIED; + policy_handle_to_string(&r_u->handle, &fhandle_string); + DEBUG(10, ("_svcctl_open_service: Opening Service [%s], handle [%s]\n", service,fhandle_string)); + return WERR_OK; } /******************************************************************** ********************************************************************/ +/* Note that this can be called to close an individual service, ** OR ** the Service Control Manager */ + WERROR _svcctl_close_service(pipes_struct *p, SVCCTL_Q_CLOSE_SERVICE *q_u, SVCCTL_R_CLOSE_SERVICE *r_u) { - if ( !close_policy_hnd( p, &q_u->handle ) ) - return WERR_BADFID; - + SCM_info *scminfo; + Service_info *svcinfo; + POLICY_HND *handle; + fstring fhandle_string; + POLICY_HND null_policy_handle; + + + handle = &(q_u->handle); + + /* a handle is returned in the close when it's for a service */ + + policy_handle_to_string(handle, &fhandle_string); + DEBUG(10, ("_svcctl_close_service: Closing handle [%s]\n",fhandle_string)); + + ZERO_STRUCT(null_policy_handle); + + policy_handle_to_string(handle, &fhandle_string); + DEBUG(10, ("_svcctl_close_service: Closing handle [%s]\n",fhandle_string)); + + scminfo = find_SCManager_info_by_hnd(p, handle); + + if ((NULL != scminfo) && (scminfo->type == SVC_HANDLE_IS_SCM)) { + DEBUG(3,("_svcctl_close_service: Closing SERVICE DATABASE [%s]\n", scminfo->target_db_name)); + + if(!(_svcctl_close_SCManager_hook(scminfo))) + return WERR_BADFILE; + + if(!(close_policy_hnd(p, handle))) + { + /* WERR_NOMEM is probably not the correct error, but until I figure out a better + one it will have to do */ + DEBUG(3,("_svcctl_close_service: Can't close SCM \n")); + return WERR_NOMEM; + } + memcpy(&(r_u->handle),&null_policy_handle, sizeof(POLICY_HND)); + return WERR_OK; + } + + if ((NULL != scminfo) && (scminfo->type == SVC_HANDLE_IS_SERVICE)) { + svcinfo = (Service_info *)scminfo; + DEBUG(3,("_svcctl_close_service: Handle is a SERVICE not SCM \n")); + DEBUG(3,("_svcctl_close_service: Closing SERVICE [%s]\n", svcinfo->servicename)); + if(!(close_policy_hnd(p, handle))) + { + /* WERR_NOMEM is probably not the correct error, but until I figure out a better + one it will have to do */ + DEBUG(3,("_svcctl_close_service: Can't close SERVICE [%s]\n", svcinfo->servicename)); + return WERR_NOMEM; + } + } + + memcpy(&(r_u->handle),&null_policy_handle, sizeof(POLICY_HND)); + return WERR_OK; } @@ -133,15 +734,34 @@ WERROR _svcctl_get_display_name(pipes_struct *p, SVCCTL_Q_GET_DISPLAY_NAME *q_u, { fstring service; fstring displayname; + fstring fhandle_string; + + Service_info *service_info; + POLICY_HND *handle; rpcstr_pull(service, q_u->servicename.buffer, sizeof(service), q_u->servicename.uni_str_len*2, 0); - DEBUG(10,("_svcctl_get_display_name: service name [%s]\n", service)); + handle = &(q_u->handle); + policy_handle_to_string(&q_u->handle, &fhandle_string); + + DEBUG(10, ("_svcctl_get_display_name: Looking for handle [%s]\n",(char *)&fhandle_string)); + service_info = find_service_info_by_hnd(p, handle); + + if (!service_info) { + DEBUG(10, ("_svcctl_get_display_name : Can't find the service for the handle\n")); + return WERR_ACCESS_DENIED; + } + + DEBUG(10, ("_svcctl_get_display_name: Found service [%s], [%s]\n",service_info->servicename,service_info->filename)); + /* no dependent services...basically a stub function */ + +#if 0 if ( !strequal( service, "NETLOGON" ) ) return WERR_ACCESS_DENIED; +#endif + fstrcpy( displayname, service_info->servicename) ; - fstrcpy( displayname, "Net Logon"); init_svcctl_r_get_display_name( r_u, displayname ); return WERR_OK; @@ -153,34 +773,266 @@ WERROR _svcctl_get_display_name(pipes_struct *p, SVCCTL_Q_GET_DISPLAY_NAME *q_u, WERROR _svcctl_query_status(pipes_struct *p, SVCCTL_Q_QUERY_STATUS *q_u, SVCCTL_R_QUERY_STATUS *r_u) { - r_u->svc_status.type = 0x0110; + r_u->svc_status.type = 0x0020; r_u->svc_status.state = 0x0004; r_u->svc_status.controls_accepted = 0x0005; return WERR_OK; } -/******************************************************************** -********************************************************************/ +/* allocate an array of external services and return them. Null return is okay, make sure &added is also zero! */ -WERROR _svcctl_enum_services_status(pipes_struct *p, SVCCTL_Q_ENUM_SERVICES_STATUS *q_u, SVCCTL_R_ENUM_SERVICES_STATUS *r_u) +int _svcctl_num_external_services(void) { - ENUM_SERVICES_STATUS *services = NULL; - uint32 num_services = 0; + int num_services; + char **svc_list; + pstring keystring, external_services_string; + TDB_DATA key_data; + + + if (!service_tdb) { + DEBUG(8,("_svcctl_enum_external_services: service database is not open!!!\n")); + num_services = 0; + } else { + pstrcpy(keystring,"EXTERNAL_SERVICES"); + tdb_lock_bystring(service_tdb, keystring, 0); + key_data = tdb_fetch_bystring(service_tdb, keystring); + + if ((key_data.dptr != NULL) && (key_data.dsize != 0)) { + strncpy(external_services_string,key_data.dptr,key_data.dsize); + external_services_string[key_data.dsize] = 0; + DEBUG(8,("_svcctl_enum_external_services: services list is %s, size is %d\n",external_services_string,key_data.dsize)); + } + tdb_unlock_bystring(service_tdb, keystring); + } + svc_list = str_list_make(external_services_string,NULL); + + num_services = str_list_count( (const char **)svc_list); + + return num_services; +} + + + +/* + + Gather information on the "external services". These are services listed in the smb.conf file, and found to exist through + checks in this code. Note that added will be incremented on the basis of the number of services added. svc_ptr should have enough + memory allocated to accommodate all of the services that exist. + + Typically _svcctl_num_external_services is used to "size" the amount of memory allocated, but does little/no work. + + _svcctl_enum_external_services actually examines each of the specified external services, populates the memory structures, and returns. + + ** note that 'added' may end up with less than the number of services found in _num_external_services, such as the case when a service is + called out, but the actual service doesn't exist or the file can't be read for the service information. + + + */ + +WERROR _svcctl_enum_external_services(TALLOC_CTX *tcx,ENUM_SERVICES_STATUS **svc_ptr, int existing_services,int *added) +{ + /* *svc_ptr must have pre-allocated memory */ + int num_services = 0; int i = 0; - size_t buffer_size; - WERROR result = WERR_OK; - - /* num_services = str_list_count( lp_enable_svcctl() ); */ - num_services = 2; - - if ( !(services = TALLOC_ARRAY( p->mem_ctx, ENUM_SERVICES_STATUS, num_services )) ) + ENUM_SERVICES_STATUS *services=NULL; + char **svc_list,**svcname; + pstring command, keystring, external_services_string; + int ret; + int fd = -1; + Service_info *si; + TDB_DATA key_data; + + *added = num_services; + + if (!service_tdb) { + DEBUG(8,("_svcctl_enum_external_services: service database is not open!!!\n")); + } else { + pstrcpy(keystring,"EXTERNAL_SERVICES"); + tdb_lock_bystring(service_tdb, keystring, 0); + key_data = tdb_fetch_bystring(service_tdb, keystring); + if ((key_data.dptr != NULL) && (key_data.dsize != 0)) { + strncpy(external_services_string,key_data.dptr,key_data.dsize); + external_services_string[key_data.dsize] = 0; + DEBUG(8,("_svcctl_enum_external_services: services list is %s, size is %d\n",external_services_string,key_data.dsize)); + } + tdb_unlock_bystring(service_tdb, keystring); + } + svc_list = str_list_make(external_services_string,NULL); + + num_services = str_list_count( (const char **)svc_list); + + if (0 == num_services) { + DEBUG(8,("_svcctl_enum_external_services: there are no external services\n")); + *added = num_services; + return WERR_OK; + } + DEBUG(8,("_svcctl_enum_external_services: there are [%d] external services\n",num_services)); + si=TALLOC_ARRAY( tcx, Service_info, 1 ); + if (si == NULL) { + DEBUG(8,("_svcctl_enum_external_services: Failed to alloc si\n")); + return WERR_NOMEM; + } + +#if 0 +/* *svc_ptr has the pointer to the array if there is one already. NULL if not. */ + if ((existing_services>0) && svc_ptr && *svc_ptr) { /* reallocate vs. allocate */ + DEBUG(8,("_svcctl_enum_external_services: REALLOCing %x to %d services\n", *svc_ptr, existing_services+num_services)); + + services=TALLOC_REALLOC_ARRAY(tcx,*svc_ptr,ENUM_SERVICES_STATUS,existing_services+num_services); + DEBUG(8,("_svcctl_enum_external_services: REALLOCed to %x services\n", services)); + + if (!services) return WERR_NOMEM; + *svc_ptr = services; + } else { + if ( !(services = TALLOC_ARRAY( tcx, ENUM_SERVICES_STATUS, num_services )) ) + return WERR_NOMEM; + } +#endif + + if (!svc_ptr || !(*svc_ptr)) + return WERR_NOMEM; + services = *svc_ptr; + if (existing_services > 0) { + i+=existing_services; + } + + svcname = svc_list; + DEBUG(8,("_svcctl_enum_external_services: enumerating %d external services starting at index %d\n", num_services,existing_services)); + + while (*svcname) { + DEBUG(10,("_svcctl_enum_external_services: Reading information on service %s, index %d\n",*svcname,i)); + /* _svcctl_get_LSB_data(*svcname,si); */ + if (!_svcctl_read_service_tdb_to_si(service_tdb,*svcname, si)) { + DEBUG(1,("_svcctl_enum_external_services: CAN'T FIND INFO FOR SERVICE %s in the services DB\n",*svcname)); + } + + if ((si->filename == NULL) || (*si->filename == 0)) { + init_unistr(&services[i].servicename, *svcname ); + } else { + init_unistr( &services[i].servicename, si->filename ); + /* init_unistr( &services[i].servicename, si->servicename ); */ + } + + if ((si->provides == NULL) || (*si->provides == 0)) { + init_unistr(&services[i].displayname, *svcname ); + } else { + init_unistr( &services[i].displayname, si->provides ); + } + + /* TODO - we could keep the following info in the DB, too... */ + + DEBUG(8,("_svcctl_enum_external_services: Service name [%s] displayname [%s]\n", + si->filename, si->provides)); + services[i].status.type = SVCCTL_WIN32_OWN_PROC; + services[i].status.win32_exit_code = 0x0; + services[i].status.service_exit_code = 0x0; + services[i].status.check_point = 0x0; + services[i].status.wait_hint = 0x0; + + /* TODO - do callout here to get the status */ + + memset(command, 0, sizeof(command)); + slprintf(command, sizeof(command)-1, "%s%s%s %s", dyn_LIBDIR, SVCCTL_SCRIPT_DIR, *svcname, "status"); + + DEBUG(10, ("_svcctl_enum_external_services: status command is [%s]\n", command)); + + /* TODO - wrap in privilege check */ + + ret = smbrun(command, &fd); + DEBUGADD(10, ("returned [%d]\n", ret)); + close(fd); + if(ret != 0) + DEBUG(10, ("_svcctl_enum_external_services: Command returned [%d]\n", ret)); + services[i].status.state = SVCCTL_STOPPED; + if (ret == 0) { + services[i].status.state = SVCCTL_RUNNING; + services[i].status.controls_accepted = SVCCTL_CONTROL_SHUTDOWN | SVCCTL_CONTROL_STOP; + } else { + services[i].status.state = SVCCTL_STOPPED; + services[i].status.controls_accepted = 0; + } + svcname++; + i++; + } + + DEBUG(10,("_svcctl_enum_external_services: Read services %d\n",num_services)); + *added = num_services; + + return WERR_OK; +} + +int _svcctl_num_internal_services(void) +{ + int num_services; + char **svc_list; + pstring keystring, internal_services_string; + TDB_DATA key_data; + + if (!service_tdb) { + DEBUG(8,("_svcctl_enum_internal_services: service database is not open!!!\n")); + num_services = 0; + } else { + pstrcpy(keystring,"INTERNAL_SERVICES"); + tdb_lock_bystring(service_tdb, keystring, 0); + key_data = tdb_fetch_bystring(service_tdb, keystring); + + if ((key_data.dptr != NULL) && (key_data.dsize != 0)) { + strncpy(internal_services_string,key_data.dptr,key_data.dsize); + internal_services_string[key_data.dsize] = 0; + DEBUG(8,("_svcctl_enum_internal_services: services list is %s, size is %d\n",internal_services_string,key_data.dsize)); + } + tdb_unlock_bystring(service_tdb, keystring); + } + svc_list = str_list_make(internal_services_string,NULL); + + num_services = str_list_count( (const char **)svc_list); + + return num_services; +} + +#if 0 + +int _svcctl_num_internal_services(void) +{ + return 2; +} +#endif + +/* TODO - for internal services, do similar to external services, except we have to call the right status routine... */ + +WERROR _svcctl_enum_internal_services(TALLOC_CTX *tcx,ENUM_SERVICES_STATUS **svc_ptr, int existing_services, int *added) +{ + int num_services = 2; + int i = 0; + ENUM_SERVICES_STATUS *services=NULL; + + if (!svc_ptr || !(*svc_ptr)) return WERR_NOMEM; - - DEBUG(8,("_svcctl_enum_services_status: Enumerating %d services\n", num_services)); + + services = *svc_ptr; + +#if 0 + /* *svc_ptr has the pointer to the array if there is one already. NULL if not. */ + if ((existing_services>0) && svc_ptr && *svc_ptr) { /* reallocate vs. allocate */ + DEBUG(8,("_svcctl_enum_internal_services: REALLOCing %d services\n", num_services)); + services = TALLOC_REALLOC_ARRAY(tcx,*svc_ptr,ENUM_SERVICES_STATUS,existing_services+num_services); + if (!rsvcs) + return WERR_NOMEM; + *svc_ptr = services; + } else { + if ( !(services = TALLOC_ARRAY( tcx, ENUM_SERVICES_STATUS, num_services )) ) + return WERR_NOMEM; + } +#endif + + if (existing_services > 0) { + i += existing_services; + } + DEBUG(8,("_svcctl_enum_internal_services: Creating %d services, starting index %d\n", num_services,existing_services)); init_unistr( &services[i].servicename, "Spooler" ); - init_unistr( &services[i].displayname, "Spooler" ); + init_unistr( &services[i].displayname, "Print Spooler" ); services[i].status.type = 0x110; services[i].status.controls_accepted = 0x0; @@ -195,7 +1047,7 @@ WERROR _svcctl_enum_services_status(pipes_struct *p, SVCCTL_Q_ENUM_SERVICES_STAT i++; - init_unistr( &services[i].servicename, "Netlogon" ); + init_unistr( &services[i].servicename, "NETLOGON" ); init_unistr( &services[i].displayname, "Net Logon" ); services[i].status.type = 0x20; @@ -208,30 +1060,90 @@ WERROR _svcctl_enum_services_status(pipes_struct *p, SVCCTL_Q_ENUM_SERVICES_STAT services[i].status.state = SVCCTL_RUNNING; else services[i].status.state = SVCCTL_STOPPED; + + *added = num_services; + + return WERR_OK; +} + +WERROR _init_svcdb(void) +{ + if (svcdb) { + talloc_destroy(svcdb); + } + svcdb = talloc_init("services DB"); + + return WERR_OK; +} + +/******************************************************************** +********************************************************************/ + +WERROR _svcctl_enum_services_status(pipes_struct *p, SVCCTL_Q_ENUM_SERVICES_STATUS *q_u, SVCCTL_R_ENUM_SERVICES_STATUS *r_u) +{ + ENUM_SERVICES_STATUS *services = NULL; + + uint32 num_int_services = 0; + uint32 num_ext_services = 0; + int i = 0; + size_t buffer_size; + WERROR result = WERR_OK; + WERROR ext_result = WERR_OK; + + /* num_services = str_list_count( lp_enable_svcctl() ); */ + + /* here's where we'll read the db of external services */ + /* _svcctl_read_LSB_data(NULL,NULL); */ + /* init_svcctl_db(); */ + num_int_services = 0; + + num_int_services = _svcctl_num_internal_services(); + + num_ext_services = _svcctl_num_external_services(); + + if ( !(services = TALLOC_ARRAY(p->mem_ctx, ENUM_SERVICES_STATUS, num_int_services+num_ext_services )) ) + return WERR_NOMEM; + + result = _svcctl_enum_internal_services(p->mem_ctx, &services, 0, &num_int_services); + + if (W_ERROR_IS_OK(result)) { + DEBUG(8,("_svcctl_enum_services_status: Got %d internal services\n", num_int_services)); + } + + ext_result=_svcctl_enum_external_services(p->mem_ctx, &services, num_int_services, &num_ext_services); + + if (W_ERROR_IS_OK(ext_result)) { + DEBUG(8,("_svcctl_enum_services_status: Got %d external services\n", num_ext_services)); + } + + DEBUG(8,("_svcctl_enum_services_status: total of %d services\n", num_int_services+num_ext_services)); + buffer_size = 0; - for (i=0; i q_u->buffer_size ) { - num_services = 0; + DEBUG(8,("_svcctl_enum_services_status: buffer size passed %d, we need %d\n", + q_u->buffer_size, buffer_size)); + + if (buffer_size > q_u->buffer_size ) { + num_int_services = 0; + num_ext_services = 0; result = WERR_MORE_DATA; } - - /* we have to set the outgoing buffer size to the same as the - incoming buffer size (even in the case of failure */ - rpcbuf_init( &r_u->buffer, q_u->buffer_size, p->mem_ctx ); - + rpcbuf_init(&r_u->buffer, q_u->buffer_size, p->mem_ctx); + if ( W_ERROR_IS_OK(result) ) { - for ( i=0; ibuffer, 0 ); } - + r_u->needed = (buffer_size > q_u->buffer_size) ? buffer_size : q_u->buffer_size; - r_u->returned = num_services; + r_u->returned = num_int_services+num_ext_services; if ( !(r_u->resume = TALLOC_P( p->mem_ctx, uint32 )) ) return WERR_NOMEM; @@ -254,7 +1166,75 @@ WERROR _svcctl_start_service(pipes_struct *p, SVCCTL_Q_START_SERVICE *q_u, SVCCT WERROR _svcctl_control_service(pipes_struct *p, SVCCTL_Q_CONTROL_SERVICE *q_u, SVCCTL_R_CONTROL_SERVICE *r_u) { - return WERR_ACCESS_DENIED; + Service_info *service_info; + POLICY_HND *handle; + pstring command; + fstring fhandle_string; + SERVICE_STATUS *service_status; + int ret,fd; + + /* need to find the service name by the handle that is open */ + handle = &(q_u->handle); + policy_handle_to_string(&q_u->handle, &fhandle_string); + + DEBUG(10, ("_svcctl_control_service: Looking for handle [%s]\n",fhandle_string)); + + service_info = find_service_info_by_hnd(p, handle); + + if (!service_info) { + DEBUG(10, ("_svcctl_control_service : Can't find the service for the handle\n")); + return WERR_BADFID; + } + + /* we return a SERVICE_STATUS structure if there's an error. */ + if ( !(service_status = TALLOC_ARRAY(p->mem_ctx, SERVICE_STATUS, 1 )) ) + return WERR_NOMEM; + + DEBUG(10, ("_svcctl_control_service: Found service [%s], [%s]\n", + service_info->servicename, service_info->filename)); + + /* TODO - call the service config function here... */ + memset(command, 0, sizeof(command)); + if (q_u->control == SVCCTL_CONTROL_STOP) { + slprintf(command, sizeof(command)-1, "%s%s%s %s", dyn_LIBDIR, SVCCTL_SCRIPT_DIR, + service_info->filename, "stop"); + } + + if (q_u->control == SVCCTL_CONTROL_PAUSE) { + slprintf(command, sizeof(command)-1, "%s%s%s %s", dyn_LIBDIR, SVCCTL_SCRIPT_DIR, + service_info->filename, "stop"); + } + + if (q_u->control == SVCCTL_CONTROL_CONTINUE) { + slprintf(command, sizeof(command)-1, "%s%s%s %s", dyn_LIBDIR, SVCCTL_SCRIPT_DIR, + service_info->filename, "restart"); + } + + DEBUG(10, ("_svcctl_control_service: status command is [%s]\n", command)); + + /* TODO - wrap in privilege check */ + + ret = smbrun(command, &fd); + DEBUGADD(10, ("returned [%d]\n", ret)); + close(fd); + + if(ret != 0) + DEBUG(10, ("_svcctl_enum_external_services: Command returned [%d]\n", ret)); + + /* SET all service_stats bits here...*/ + if (ret == 0) { + service_status->state = SVCCTL_RUNNING; + service_status->controls_accepted = SVCCTL_CONTROL_SHUTDOWN | SVCCTL_CONTROL_STOP; + } else { + service_status->state = SVCCTL_STOPPED; + service_status->controls_accepted = 0; + } + + DEBUG(10, ("_svcctl_query_service_config: Should call the commFound service [%s], [%s]\n",service_info->servicename,service_info->filename)); + + /* no dependent services...basically a stub function */ + + return WERR_OK; } /******************************************************************** @@ -279,17 +1259,256 @@ WERROR _svcctl_enum_dependent_services( pipes_struct *p, SVCCTL_Q_ENUM_DEPENDENT /******************************************************************** ********************************************************************/ -WERROR _svcctl_query_service_config( pipes_struct *p, SVCCTL_Q_QUERY_SERVICE_CONFIG *q_u, SVCCTL_R_QUERY_SERVICE_CONFIG *r_u ) +WERROR _svcctl_query_service_status_ex( pipes_struct *p, SVCCTL_Q_QUERY_SERVICE_STATUSEX *q_u, SVCCTL_R_QUERY_SERVICE_STATUSEX *r_u ) { + SERVICE_STATUS_PROCESS ssp; + fstring fhandle_string; + POLICY_HND *handle; + Service_info *service_info; + pstring command; + int ret,fd; + + /* we have to set the outgoing buffer size to the same as the + incoming buffer size (even in the case of failure */ + + r_u->needed = q_u->buffer_size; + + /* need to find the service name by the handle that is open */ + handle = &(q_u->handle); + policy_handle_to_string(&q_u->handle, &fhandle_string); + + DEBUG(10, ("_svcctl_query_service_status_ex Looking for handle [%s]\n",fhandle_string)); + + /* get rid of the easy errors */ + + if (q_u->info_level != SVC_STATUS_PROCESS_INFO) { + DEBUG(10, ("_svcctl_query_service_status_ex : Invalid information level specified\n")); + return WERR_UNKNOWN_LEVEL; + } + + service_info = find_service_info_by_hnd(p, handle); + + if (!service_info) { + DEBUG(10, ("_svcctl_query_service_status_ex : Can't find the service for the handle\n")); + return WERR_BADFID; + } + if (r_u->needed < (sizeof(SERVICE_STATUS_PROCESS)+sizeof(uint32)+sizeof(uint32))) { + DEBUG(10, ("_svcctl_query_service_status_ex : buffer size of [%d] is too small.\n",r_u->needed)); + return WERR_INSUFFICIENT_BUFFER; + } + + ZERO_STRUCT(ssp); + + if (!strwicmp(service_info->servicetype,"EXTERNAL")) + ssp.type = SVCCTL_WIN32_OWN_PROC; + else + ssp.type = SVCCTL_WIN32_SHARED_PROC; + + /* Get the status of the service.. */ + + DEBUG(10, ("_svcctl_query_service_status_ex: Found service [%s], [%s]\n",service_info->servicename,service_info->filename)); + + memset(command, 0, sizeof(command)); + + slprintf(command, sizeof(command)-1, "%s%s%s %s", + dyn_LIBDIR, SVCCTL_SCRIPT_DIR, service_info->filename, "status"); + + DEBUG(10, ("_svcctl_query_service_status_ex: status command is [%s]\n", command)); + + /* TODO - wrap in privilege check */ + + ret = smbrun(command, &fd); + DEBUGADD(10, ("returned [%d]\n", ret)); + close(fd); + if(ret != 0) + DEBUG(10, ("_svcctl_query_service_status_ex: Command returned [%d]\n", ret)); + + /* SET all service_stats bits here... */ + if (ret == 0) { + ssp.state = SVCCTL_RUNNING; + ssp.controls_accepted = SVCCTL_CONTROL_SHUTDOWN | SVCCTL_CONTROL_STOP; + } else { + ssp.state = SVCCTL_STOPPED; + ssp.controls_accepted = 0; + } + + return WERR_OK; +} + +/******************************************************************** +********************************************************************/ + +WERROR _svcctl_query_service_config( pipes_struct *p, SVCCTL_Q_QUERY_SERVICE_CONFIG *q_u, SVCCTL_R_QUERY_SERVICE_CONFIG *r_u ) +{ + /* SERVICE_CONFIG *service_config = NULL; */ + fstring fhandle_string; + POLICY_HND *handle; + Service_info *service_info; + pstring fullpathinfo; + uint32 needed_size; + /* we have to set the outgoing buffer size to the same as the incoming buffer size (even in the case of failure */ r_u->needed = q_u->buffer_size; + + /* need to find the service name by the handle that is open */ + handle = &(q_u->handle); + policy_handle_to_string(&q_u->handle, &fhandle_string); + + DEBUG(10, ("_svcctl_query_service_config: Looking for handle [%s]\n",fhandle_string)); + + service_info = find_service_info_by_hnd(p, handle); + +#if 0 + if (q_u->buffer_size < sizeof(Service_info)) { + /* have to report need more... */ + /* TODO worst case -- should actualy calc what we need here. */ + r_u->needed = sizeof(Service_info)+sizeof(pstring)*5; + DEBUG(10, ("_svcctl_query_service_config: NOT ENOUGH BUFFER ALLOCATED FOR RETURN DATA -- provided %d wanted %d\n", + q_u->buffer_size,r_u->needed)); + + return WERR_INSUFFICIENT_BUFFER; + } +#endif + if (!service_info) { + DEBUG(10, ("_svcctl_query_service_config : Can't find the service for the handle\n")); + return WERR_BADFID; + } + +#if 0 + if ( !(service_config = (SERVICE_CONFIG *)TALLOC_ZERO_P(p->mem_ctx, SERVICE_CONFIG)) ) + return WERR_NOMEM; +#endif + + r_u->config.service_type = SVCCTL_WIN32_OWN_PROC; + r_u->config.start_type = SVCCTL_DEMAND_START; + r_u->config.error_control = SVCCTL_SVC_ERROR_IGNORE; + r_u->config.tag_id = 0x00000000; + + /* Init the strings */ + + r_u->config.executablepath = TALLOC_ZERO_P(p->mem_ctx, UNISTR2); + r_u->config.loadordergroup = TALLOC_ZERO_P(p->mem_ctx, UNISTR2); + r_u->config.dependencies = TALLOC_ZERO_P(p->mem_ctx, UNISTR2); + r_u->config.startname = TALLOC_ZERO_P(p->mem_ctx, UNISTR2); + r_u->config.displayname = TALLOC_ZERO_P(p->mem_ctx, UNISTR2); + + DEBUG(10, ("_svcctl_query_service_config: Found service [%s], [%s]\n",service_info->servicename,service_info->filename)); + + pstrcpy(fullpathinfo,dyn_LIBDIR); + pstrcat(fullpathinfo,SVCCTL_SCRIPT_DIR); + pstrcat(fullpathinfo,service_info->filename); + + /* Get and calculate the size of the fields. Note that we're still building the fields in the "too-small buffer case" + even though we throw it away. */ - /* no dependent services...basically a stub function */ + DEBUG(10, ("_svcctl_query_service_config: fullpath info [%s]\n",fullpathinfo)); + init_unistr2(r_u->config.executablepath,fullpathinfo,UNI_STR_TERMINATE); + init_unistr2(r_u->config.loadordergroup,"",UNI_STR_TERMINATE); + init_unistr2(r_u->config.dependencies,service_info->dependencies,UNI_STR_TERMINATE); - return WERR_ACCESS_DENIED; + /* TODO - if someone really cares, perhaps "LocalSystem" should be changed to something else here... */ + + init_unistr2(r_u->config.startname,"LocalSystem",UNI_STR_TERMINATE); + init_unistr2(r_u->config.displayname,service_info->servicename,UNI_STR_TERMINATE); + + needed_size = 0x04 + sizeof(SERVICE_CONFIG)+ 2*( + r_u->config.executablepath->uni_str_len + + r_u->config.loadordergroup->uni_str_len + + r_u->config.dependencies->uni_str_len + + r_u->config.startname->uni_str_len + + r_u->config.displayname->uni_str_len); + + DEBUG(10, ("_svcctl_query_service_config: ****** need to have a buffer of [%d], [%d] for struct \n",needed_size, + sizeof(SERVICE_CONFIG))); + DEBUG(10, ("\tsize of executable path : %d\n",r_u->config.executablepath->uni_str_len)); + DEBUG(10, ("\tsize of loadordergroup : %d\n", r_u->config.loadordergroup->uni_str_len)); + DEBUG(10, ("\tsize of dependencies : %d\n", r_u->config.dependencies->uni_str_len)); + DEBUG(10, ("\tsize of startname : %d\n", r_u->config.startname->uni_str_len)); + DEBUG(10, ("\tsize of displayname : %d\n", r_u->config.displayname->uni_str_len)); + + if (q_u->buffer_size < needed_size) { + /* have to report need more...*/ + r_u->needed = needed_size; + DEBUG(10, ("_svcctl_query_service_config: ****** zeroing strings for return\n")); + memset(&r_u->config,0,sizeof(SERVICE_CONFIG)); + DEBUG(10, ("_svcctl_query_service_config: Not enouh buffer provided for return -- provided %d wanted %d\n", + q_u->buffer_size,needed_size)); + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; } +/******************************************************************** +********************************************************************/ + +WERROR _svcctl_query_service_config2( pipes_struct *p, SVCCTL_Q_QUERY_SERVICE_CONFIG2 *q_u, SVCCTL_R_QUERY_SERVICE_CONFIG2 *r_u ) +{ + fstring fhandle_string; + POLICY_HND *handle; + Service_info *service_info; + uint32 level, string_buffer_size; + + /* we have to set the outgoing buffer size to the same as the + incoming buffer size (even in the case of failure */ + r_u->needed = q_u->buffer_size; + r_u->description = NULL; + r_u->returned = q_u->buffer_size; + r_u->offset = 4; + + handle = &(q_u->handle); + policy_handle_to_string(&(q_u->handle), &fhandle_string); + + DEBUG(10, ("_svcctl_query_service_config2: Looking for handle [%s]\n",fhandle_string)); + + service_info = find_service_info_by_hnd(p, handle); + + if (!service_info) { + DEBUG(10, ("_svcctl_query_service_config2 : Can't find the service for the handle\n")); + return WERR_BADFID; + } + + /* + TODO - perhaps move the RPC_DATA_BLOB into the R_QUERY_SERVICE_CONFIG structure, and to the processing in here, vs + in the *r_query_config2 marshalling routine... + */ + + level = q_u->info_level; + DEBUG(10, ("_svcctl_query_service_config2: Found service [%s], [%s]\n",service_info->servicename,service_info->filename)); + DEBUG(10, ("_svcctl_query_service_config2: Looking for level [%x], buffer size is [%x]\n",level,q_u->buffer_size)); + + if (SERVICE_CONFIG_DESCRIPTION == level) { + if (service_info && service_info->shortdescription) { + /* length of the string, plus the terminator... */ + string_buffer_size = strlen(service_info->shortdescription)+1; + DEBUG(10, ("_svcctl_query_service_config: copying the description [%s] length [%d]\n", + service_info->shortdescription,string_buffer_size)); + + if (q_u->buffer_size >= ((string_buffer_size)*2+4)) { + r_u->description = TALLOC_ZERO_P(p->mem_ctx, UNISTR2); + if (!r_u->description) return WERR_NOMEM; + init_unistr2(r_u->description,service_info->shortdescription,UNI_STR_TERMINATE); + } + } + else { + string_buffer_size = 0; + } + DEBUG(10, ("_svcctl_query_service_config2: buffer needed is [%x], return buffer size is [%x]\n", + string_buffer_size,q_u->buffer_size)); + if (((string_buffer_size)*2+4) > q_u->buffer_size) { + r_u->needed = (string_buffer_size+1)*2+4; + DEBUG(10, ("_svcctl_query_service_config2: INSUFFICIENT BUFFER\n")); + return WERR_INSUFFICIENT_BUFFER; + } + DEBUG(10, ("_svcctl_query_service_config2: returning ok, needed is [%x], buffer size is [%x]\n", + r_u->needed,q_u->buffer_size)); + + return WERR_OK; + } + + return WERR_ACCESS_DENIED; +} -- cgit