summaryrefslogtreecommitdiff
path: root/source3/services
diff options
context:
space:
mode:
authorAndreas Schneider <asn@samba.org>2011-02-01 13:38:16 +0100
committerGünther Deschner <gd@samba.org>2011-02-07 12:32:01 +0100
commitc6255adc6f027cafeca7247ce58bbeae0733d45a (patch)
tree748115a3523909789d8e45266496aeee4e77ddef /source3/services
parentb2ddfc642d0d6a39e6962c61dd178c69f0cd801b (diff)
downloadsamba-c6255adc6f027cafeca7247ce58bbeae0733d45a.tar.gz
s3-services: Remove obsolete services_db.c.
Signed-off-by: Günther Deschner <gd@samba.org>
Diffstat (limited to 'source3/services')
-rw-r--r--source3/services/services_db.c726
1 files changed, 0 insertions, 726 deletions
diff --git a/source3/services/services_db.c b/source3/services/services_db.c
deleted file mode 100644
index 03f7951b7c2..00000000000
--- a/source3/services/services_db.c
+++ /dev/null
@@ -1,726 +0,0 @@
-/*
- * Unix SMB/CIFS implementation.
- * Service Control API Implementation
- *
- * Copyright (C) Marcin Krzysztof Porwit 2005.
- * Largely Rewritten by:
- * Copyright (C) Gerald (Jerry) 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include "includes.h"
-#include "services/services.h"
-#include "registry.h"
-#include "registry/reg_api.h"
-#include "registry/reg_api_util.h"
-#include "../libcli/security/security.h"
-
-struct rcinit_file_information {
- char *description;
-};
-
-struct service_display_info {
- const char *servicename;
- const char *daemon;
- const char *dispname;
- const char *description;
-};
-
-struct service_display_info builtin_svcs[] = {
- { "Spooler", "smbd", "Print Spooler", "Internal service for spooling files to print devices" },
- { "NETLOGON", "smbd", "Net Logon", "File service providing access to policy and profile data (not remotely manageable)" },
- { "RemoteRegistry", "smbd", "Remote Registry Service", "Internal service providing remote access to "
- "the Samba registry" },
- { "WINS", "nmbd", "Windows Internet Name Service (WINS)", "Internal service providing a "
- "NetBIOS point-to-point name server (not remotely manageable)" },
- { NULL, NULL, NULL, NULL }
-};
-
-struct service_display_info common_unix_svcs[] = {
- { "cups", NULL, "Common Unix Printing System","Provides unified printing support for all operating systems" },
- { "postfix", NULL, "Internet Mail Service", "Provides support for sending and receiving electonic mail" },
- { "sendmail", NULL, "Internet Mail Service", "Provides support for sending and receiving electonic mail" },
- { "portmap", NULL, "TCP Port to RPC PortMapper",NULL },
- { "xinetd", NULL, "Internet Meta-Daemon", NULL },
- { "inet", NULL, "Internet Meta-Daemon", NULL },
- { "xntpd", NULL, "Network Time Service", NULL },
- { "ntpd", NULL, "Network Time Service", NULL },
- { "lpd", NULL, "BSD Print Spooler", NULL },
- { "nfsserver", NULL, "Network File Service", NULL },
- { "cron", NULL, "Scheduling Service", NULL },
- { "at", NULL, "Scheduling Service", NULL },
- { "nscd", NULL, "Name Service Cache Daemon", NULL },
- { "slapd", NULL, "LDAP Directory Service", NULL },
- { "ldap", NULL, "LDAP DIrectory Service", NULL },
- { "ypbind", NULL, "NIS Directory Service", NULL },
- { "courier-imap", NULL, "IMAP4 Mail Service", NULL },
- { "courier-pop3", NULL, "POP3 Mail Service", NULL },
- { "named", NULL, "Domain Name Service", NULL },
- { "bind", NULL, "Domain Name Service", NULL },
- { "httpd", NULL, "HTTP Server", NULL },
- { "apache", NULL, "HTTP Server", "Provides s highly scalable and flexible web server "
- "capable of implementing various protocols incluing "
- "but not limited to HTTP" },
- { "autofs", NULL, "Automounter", NULL },
- { "squid", NULL, "Web Cache Proxy ", NULL },
- { "perfcountd", NULL, "Performance Monitoring Daemon", NULL },
- { "pgsql", NULL, "PgSQL Database Server", "Provides service for SQL database from Postgresql.org" },
- { "arpwatch", NULL, "ARP Tables watcher", "Provides service for monitoring ARP tables for changes" },
- { "dhcpd", NULL, "DHCP Server", "Provides service for dynamic host configuration and IP assignment" },
- { "nwserv", NULL, "NetWare Server Emulator", "Provides service for emulating Novell NetWare 3.12 server" },
- { "proftpd", NULL, "Professional FTP Server", "Provides high configurable service for FTP connection and "
- "file transferring" },
- { "ssh2", NULL, "SSH Secure Shell", "Provides service for secure connection for remote administration" },
- { "sshd", NULL, "SSH Secure Shell", "Provides service for secure connection for remote administration" },
- { NULL, NULL, NULL, NULL }
-};
-
-static WERROR svcctl_set_secdesc_internal(struct registry_key *key,
- struct security_descriptor *sec_desc);
-
-/********************************************************************
-********************************************************************/
-
-static struct security_descriptor* construct_service_sd( TALLOC_CTX *ctx )
-{
- struct security_ace ace[4];
- size_t i = 0;
- struct security_descriptor *sd = NULL;
- struct security_acl *theacl = NULL;
- size_t sd_size;
-
- /* basic access for Everyone */
-
- init_sec_ace(&ace[i++], &global_sid_World,
- SEC_ACE_TYPE_ACCESS_ALLOWED, SERVICE_READ_ACCESS, 0);
-
- init_sec_ace(&ace[i++], &global_sid_Builtin_Power_Users,
- SEC_ACE_TYPE_ACCESS_ALLOWED, SERVICE_EXECUTE_ACCESS, 0);
-
- init_sec_ace(&ace[i++], &global_sid_Builtin_Server_Operators,
- SEC_ACE_TYPE_ACCESS_ALLOWED, SERVICE_ALL_ACCESS, 0);
- init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
- SEC_ACE_TYPE_ACCESS_ALLOWED, SERVICE_ALL_ACCESS, 0);
-
- /* create the security descriptor */
-
- theacl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace);
- if (theacl == NULL) {
- return NULL;
- }
-
- sd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1,
- SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL,
- theacl, &sd_size);
- if (sd == NULL) {
- return NULL;
- }
-
- return sd;
-}
-
-/********************************************************************
- This is where we do the dirty work of filling in things like the
- Display name, Description, etc...
-********************************************************************/
-
-static char *get_common_service_dispname(TALLOC_CTX *mem_ctx,
- const char *servicename)
-{
- int i;
-
- for ( i=0; common_unix_svcs[i].servicename; i++ ) {
- if (strequal(servicename, common_unix_svcs[i].servicename)) {
- char *dispname;
- dispname = talloc_asprintf(mem_ctx, "%s (%s)",
- common_unix_svcs[i].dispname,
- common_unix_svcs[i].servicename);
- if (dispname == NULL) {
- return NULL;
- }
- return dispname;
- }
- }
-
- return talloc_strdup(mem_ctx, servicename);
-}
-
-/********************************************************************
-********************************************************************/
-
-static char *cleanup_string( const char *string )
-{
- char *clean = NULL;
- char *begin, *end;
- TALLOC_CTX *ctx = talloc_tos();
-
- clean = talloc_strdup(ctx, string);
- if (!clean) {
- return NULL;
- }
- begin = clean;
-
- /* trim any beginning whilespace */
-
- while (isspace(*begin)) {
- begin++;
- }
-
- if (*begin == '\0') {
- return NULL;
- }
-
- /* trim any trailing whitespace or carriage returns.
- Start at the end and move backwards */
-
- end = begin + strlen(begin) - 1;
-
- while ( isspace(*end) || *end=='\n' || *end=='\r' ) {
- *end = '\0';
- end--;
- }
-
- return begin;
-}
-
-/********************************************************************
-********************************************************************/
-
-static bool read_init_file( const char *servicename, struct rcinit_file_information **service_info )
-{
- struct rcinit_file_information *info = NULL;
- char *filepath = NULL;
- char str[1024];
- XFILE *f = NULL;
- char *p = NULL;
-
- info = TALLOC_ZERO_P( NULL, struct rcinit_file_information );
- if (info == NULL) {
- return False;
- }
-
- /* attempt the file open */
-
- filepath = talloc_asprintf(info, "%s/%s/%s", get_dyn_MODULESDIR(),
- SVCCTL_SCRIPT_DIR, servicename);
- if (!filepath) {
- TALLOC_FREE(info);
- return false;
- }
- f = x_fopen( filepath, O_RDONLY, 0 );
- if (f == NULL) {
- DEBUG(0,("read_init_file: failed to open [%s]\n", filepath));
- TALLOC_FREE(info);
- return false;
- }
-
- while ( (x_fgets( str, sizeof(str)-1, f )) != NULL ) {
- /* ignore everything that is not a full line
- comment starting with a '#' */
-
- if ( str[0] != '#' )
- continue;
-
- /* Look for a line like '^#.*Description:' */
-
- p = strstr( str, "Description:" );
- if (p != NULL) {
- char *desc;
-
- p += strlen( "Description:" ) + 1;
- if ( !p )
- break;
-
- desc = cleanup_string(p);
- if (desc != NULL)
- info->description = talloc_strdup( info, desc );
- }
- }
-
- x_fclose( f );
-
- if ( !info->description )
- info->description = talloc_strdup( info, "External Unix Service" );
-
- *service_info = info;
- TALLOC_FREE(filepath);
-
- return True;
-}
-
-/********************************************************************
- This is where we do the dirty work of filling in things like the
- Display name, Description, etc...
-********************************************************************/
-
-static WERROR svcctl_setvalue(struct registry_key *key,
- const char *name,
- struct registry_value *value)
-{
- WERROR wresult;
- struct registry_value *existing;
-
- wresult = reg_queryvalue(talloc_tos(), key, name, &existing);
- if (W_ERROR_IS_OK(wresult)) {
- bool exists;
-
- exists = ((value->type == existing->type) &&
- (data_blob_cmp(&value->data, &existing->data) == 0));
-
- TALLOC_FREE(existing);
-
- if (exists) {
- /*
- * Avoid an expensive write when not necessary
- */
- return WERR_OK;
- }
- }
-
- wresult = reg_setvalue(key, name, value);
- if (!W_ERROR_IS_OK(wresult)) {
- DEBUG(0, ("reg_setvalue failed for %s in key %s: %s\n",
- name, key->key->name, win_errstr(wresult)));
- }
-
- return wresult;
-}
-
-static WERROR svcctl_setvalue_dword(struct registry_key *key,
- const char *name,
- uint32_t dword)
-{
- struct registry_value value;
-
- value.type = REG_DWORD;
- value.data.length = sizeof(uint32_t);
- value.data.data = (uint8_t *)&dword;
-
- return svcctl_setvalue(key, name, &value);
-}
-
-static WERROR svcctl_setvalue_sz(struct registry_key *key,
- const char *name,
- const char *sz)
-{
- struct registry_value value;
- WERROR wresult;
- TALLOC_CTX *mem_ctx = talloc_stackframe();
-
- if (!push_reg_sz(mem_ctx, &value.data, sz)) {
- DEBUG(0, ("push_reg_sz failed\n"));
- wresult = WERR_NOMEM;
- goto done;
- }
- value.type = REG_SZ;
-
- wresult = svcctl_setvalue(key, name, &value);
-done:
- talloc_free(mem_ctx);
- return wresult;
-}
-
-static void fill_service_values(struct registry_key *key)
-{
- char *dname, *ipath, *description;
- int i;
- WERROR wresult;
- TALLOC_CTX *mem_ctx = talloc_stackframe();
- char *name = NULL;
-
- name = strrchr(key->key->name, '\\');
- if (name == NULL) {
- name = key->key->name;
- } else {
- name++;
- }
-
- /* These values are hardcoded in all QueryServiceConfig() replies.
- I'm just storing them here for cosmetic purposes */
-
- wresult = svcctl_setvalue_dword(key, "Start", SVCCTL_AUTO_START);
- if (!W_ERROR_IS_OK(wresult)) {
- goto done;
- }
-
- wresult = svcctl_setvalue_dword(key, "Type", SERVICE_TYPE_WIN32_OWN_PROCESS);
- if (!W_ERROR_IS_OK(wresult)) {
- goto done;
- }
-
- wresult = svcctl_setvalue_dword(key, "ErrorControl", SVCCTL_SVC_ERROR_NORMAL);
- if (!W_ERROR_IS_OK(wresult)) {
- goto done;
- }
-
- /* everything runs as LocalSystem */
-
- wresult = svcctl_setvalue_sz(key, "ObjectName", "LocalSystem");
- if (!W_ERROR_IS_OK(wresult)) {
- goto done;
- }
-
- /* special considerations for internal services and the DisplayName value */
-
- for ( i=0; builtin_svcs[i].servicename; i++ ) {
- if ( strequal( name, builtin_svcs[i].servicename ) ) {
- ipath = talloc_asprintf(mem_ctx, "%s/%s/%s",
- get_dyn_MODULESDIR(),
- SVCCTL_SCRIPT_DIR,
- builtin_svcs[i].daemon);
- description = talloc_strdup(mem_ctx, builtin_svcs[i].description);
- dname = talloc_strdup(mem_ctx, builtin_svcs[i].dispname);
- break;
- }
- }
-
- /* default to an external service if we haven't found a match */
-
- if ( builtin_svcs[i].servicename == NULL ) {
- char *dispname = NULL;
- struct rcinit_file_information *init_info = NULL;
-
- ipath = talloc_asprintf(mem_ctx, "%s/%s/%s",
- get_dyn_MODULESDIR(), SVCCTL_SCRIPT_DIR,
- name);
-
- /* lookup common unix display names */
- dispname = get_common_service_dispname(mem_ctx, name);
- dname = talloc_strdup(mem_ctx, dispname ? dispname : "");
-
- /* get info from init file itself */
- if ( read_init_file( name, &init_info ) ) {
- description = talloc_strdup(mem_ctx, init_info->description);
- TALLOC_FREE( init_info );
- }
- else {
- description = talloc_strdup(mem_ctx, "External Unix Service");
- }
- }
-
- /* add the new values */
-
- wresult = svcctl_setvalue_sz(key, "DisplayName", dname);
- if (!W_ERROR_IS_OK(wresult)) {
- goto done;
- }
-
- wresult = svcctl_setvalue_sz(key, "ImagePath", ipath);
- if (!W_ERROR_IS_OK(wresult)) {
- goto done;
- }
-
- wresult = svcctl_setvalue_sz(key, "Description", description);
-
-done:
- talloc_free(mem_ctx);
- return;
-}
-
-/********************************************************************
-********************************************************************/
-
-static void add_new_svc_name(struct registry_key *key_parent,
- const char *name)
-{
- struct registry_key *key_service = NULL, *key_secdesc = NULL;
- WERROR wresult;
- struct security_descriptor *sd = NULL;
- TALLOC_CTX *mem_ctx = talloc_stackframe();
- enum winreg_CreateAction action = REG_ACTION_NONE;
-
- wresult = reg_createkey(mem_ctx, key_parent, name, REG_KEY_ALL,
- &key_service, &action);
-
- if (!W_ERROR_IS_OK(wresult)) {
- DEBUG(0, ("add_new_svc_name: reg_createkey failed for %s\\%s: "
- "%s\n", key_parent->key->name, name,
- win_errstr(wresult)));
- goto done;
- }
-
- /* now for the service values */
-
- fill_service_values(key_service);
-
- /* now add the security descriptor */
-
- sd = construct_service_sd(key_secdesc);
- if (sd == NULL) {
- DEBUG(0, ("add_new_svc_name: Failed to create default "
- "sec_desc!\n"));
- goto done;
- }
-
- wresult = svcctl_set_secdesc_internal(key_service, sd);
-
-done:
- talloc_free(mem_ctx);
- return;
-}
-
-/********************************************************************
-********************************************************************/
-
-void svcctl_init_keys( void )
-{
- const char **service_list = lp_svcctl_list();
- int i;
- struct registry_key *key = NULL;
- struct registry_key *subkey = NULL;
- WERROR wresult;
- TALLOC_CTX *mem_ctx = talloc_stackframe();
-
- /* bad mojo here if the lookup failed. Should not happen */
-
- wresult = reg_open_path(mem_ctx, KEY_SERVICES, REG_KEY_ALL, get_root_nt_token(), &key);
-
- if ( !W_ERROR_IS_OK(wresult) ) {
- DEBUG(0,("svcctl_init_keys: key lookup failed! (%s)\n",
- win_errstr(wresult)));
- goto done;
- }
-
- /* the builtin services exist */
-
- for ( i=0; builtin_svcs[i].servicename; i++ )
- add_new_svc_name(key, builtin_svcs[i].servicename);
-
- for ( i=0; service_list && service_list[i]; i++ ) {
-
- /* only add new services */
-
- wresult = reg_openkey(mem_ctx, key, service_list[i], REG_KEY_ALL, &subkey);
- if (W_ERROR_IS_OK(wresult)) {
- continue;
- }
-
- /* Add the new service key and initialize the appropriate values */
-
- add_new_svc_name(key, service_list[i]);
- }
-
- /* initialize the control hooks */
-
- init_service_op_table();
-
-done:
- talloc_free(mem_ctx);
- return;
-}
-
-/********************************************************************
- This is where we do the dirty work of filling in things like the
- Display name, Description, etc...Always return a default secdesc
- in case of any failure.
-********************************************************************/
-
-struct security_descriptor *svcctl_get_secdesc( TALLOC_CTX *ctx, const char *name, struct security_token *token )
-{
- struct registry_key *key = NULL;
- struct registry_value *value;
- struct security_descriptor *ret_sd = NULL;
- char *path= NULL;
- WERROR wresult;
- NTSTATUS status;
- TALLOC_CTX *mem_ctx = talloc_stackframe();
-
- path = talloc_asprintf(mem_ctx, "%s\\%s\\%s", KEY_SERVICES, name,
- "Security");
- if (path == NULL) {
- goto done;
- }
-
- wresult = reg_open_path(mem_ctx, path, REG_KEY_ALL, token, &key);
- if ( !W_ERROR_IS_OK(wresult) ) {
- DEBUG(0,("svcctl_get_secdesc: key lookup failed! [%s] (%s)\n",
- path, win_errstr(wresult)));
- goto done;
- }
-
- wresult = reg_queryvalue(mem_ctx, key, "Security", &value);
- if (W_ERROR_EQUAL(wresult, WERR_BADFILE)) {
- goto fallback_to_default_sd;
- } else if (!W_ERROR_IS_OK(wresult)) {
- DEBUG(0, ("svcctl_get_secdesc: error getting value 'Security': "
- "%s\n", win_errstr(wresult)));
- goto done;
- }
-
- status = unmarshall_sec_desc(ctx, value->data.data,
- value->data.length, &ret_sd);
-
- if (NT_STATUS_IS_OK(status)) {
- goto done;
- }
-
-fallback_to_default_sd:
- DEBUG(6, ("svcctl_get_secdesc: constructing default secdesc for "
- "service [%s]\n", name));
- ret_sd = construct_service_sd(ctx);
-
-done:
- talloc_free(mem_ctx);
- return ret_sd;
-}
-
-/********************************************************************
- Wrapper to make storing a Service sd easier
-********************************************************************/
-
-static WERROR svcctl_set_secdesc_internal(struct registry_key *key,
- struct security_descriptor *sec_desc)
-{
- struct registry_key *key_security = NULL;
- WERROR wresult;
- struct registry_value value;
- NTSTATUS status;
- enum winreg_CreateAction action = REG_ACTION_NONE;
- TALLOC_CTX *mem_ctx = talloc_stackframe();
-
- wresult = reg_createkey(mem_ctx, key, "Security", REG_KEY_ALL, &key_security, &action);
- if (!W_ERROR_IS_OK(wresult)) {
- DEBUG(0, ("svcctl_set_secdesc: reg_createkey failed: "
- "[%s\\Security] (%s)\n", key->key->name,
- win_errstr(wresult)));
- goto done;
- }
-
- status = marshall_sec_desc(mem_ctx, sec_desc, &value.data.data,
- &value.data.length);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0, ("svcctl_set_secdesc: marshall_sec_desc() failed: %s\n",
- nt_errstr(status)));
- wresult = ntstatus_to_werror(status);
- goto done;
- }
-
- value.type = REG_BINARY;
-
- wresult = reg_setvalue(key_security, "Security", &value);
- if (!W_ERROR_IS_OK(wresult)) {
- DEBUG(0, ("svcctl_set_secdesc: reg_setvalue failed: %s\n",
- win_errstr(wresult)));
- }
-
-done:
- talloc_free(mem_ctx);
- return wresult;
-}
-
-bool svcctl_set_secdesc(const char *name, struct security_descriptor *sec_desc,
- struct security_token *token)
-{
- struct registry_key *key = NULL;
- WERROR wresult;
- char *path = NULL;
- bool ret = false;
- TALLOC_CTX *mem_ctx = talloc_stackframe();
-
- path = talloc_asprintf(mem_ctx, "%s\\%s", KEY_SERVICES, name);
- if (path == NULL) {
- goto done;
- }
-
- wresult = reg_open_path(mem_ctx, path, REG_KEY_ALL, token, &key);
- if ( !W_ERROR_IS_OK(wresult) ) {
- DEBUG(0, ("svcctl_set_secdesc: key lookup failed! [%s] (%s)\n",
- path, win_errstr(wresult)));
- goto done;
- }
-
- wresult = svcctl_set_secdesc_internal(key, sec_desc);
-
- ret = W_ERROR_IS_OK(wresult);
-
-done:
- talloc_free(mem_ctx);
- return ret;
-}
-
-const char *svcctl_get_string_value(TALLOC_CTX *ctx, const char *key_name,
- const char *value_name,
- struct security_token *token)
-{
- const char *result = NULL;
- struct registry_key *key = NULL;
- struct registry_value *value = NULL;
- char *path = NULL;
- WERROR wresult;
- TALLOC_CTX *mem_ctx = talloc_stackframe();
-
- path = talloc_asprintf(mem_ctx, "%s\\%s", KEY_SERVICES, key_name);
- if (path == NULL) {
- goto done;
- }
-
- wresult = reg_open_path(mem_ctx, path, REG_KEY_READ, token, &key);
- if (!W_ERROR_IS_OK(wresult)) {
- DEBUG(0, ("svcctl_get_string_value: key lookup failed! "
- "[%s] (%s)\n", path, win_errstr(wresult)));
- goto done;
- }
-
- wresult = reg_queryvalue(mem_ctx, key, value_name, &value);
- if (!W_ERROR_IS_OK(wresult)) {
- DEBUG(0, ("svcctl_get_string_value: error getting value "
- "'%s': %s\n", value_name, win_errstr(wresult)));
- goto done;
- }
-
- if (value->type != REG_SZ) {
- goto done;
- }
-
- pull_reg_sz(ctx, &value->data, &result);
-
- goto done;
-
-done:
- talloc_free(mem_ctx);
- return result;
-}
-
-/********************************************************************
-********************************************************************/
-
-const char *svcctl_lookup_dispname(TALLOC_CTX *ctx, const char *name, struct security_token *token )
-{
- const char *display_name = NULL;
-
- display_name = svcctl_get_string_value(ctx, name, "DisplayName", token);
-
- if (display_name == NULL) {
- display_name = talloc_strdup(ctx, name);
- }
-
- return display_name;
-}
-
-/********************************************************************
-********************************************************************/
-
-const char *svcctl_lookup_description(TALLOC_CTX *ctx, const char *name, struct security_token *token )
-{
- const char *description = NULL;
-
- description = svcctl_get_string_value(ctx, name, "Description", token);
-
- if (description == NULL) {
- description = talloc_strdup(ctx, "Unix Service");
- }
-
- return description;
-}