X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=gosa-core%2Finclude%2Fclass_gosaSupportDaemon.inc;h=3a7d09ce4365793cfab00b120f92e54893d34566;hb=9fee4c10382768e341abef8b0b69751e6b050e19;hp=0c99d158c203c0942d16894f7baeba9632978b05;hpb=6b1fb6284c23fb66defc27630b2aa7f0b2c0db4c;p=gosa.git diff --git a/gosa-core/include/class_gosaSupportDaemon.inc b/gosa-core/include/class_gosaSupportDaemon.inc index 0c99d158c..3a7d09ce4 100644 --- a/gosa-core/include/class_gosaSupportDaemon.inc +++ b/gosa-core/include/class_gosaSupportDaemon.inc @@ -31,10 +31,20 @@ class gosaSupportDaemon private $s_error = ""; private $b_error = FALSE; - private $is_connected = FALSE; - + private $is_connected = FALSE; + private $is_configured = FALSE; protected $use_alternative_xml_parse_method = FALSE; + public function get_host() + { + return($this->s_host); + } + + public function get_port() + { + return($this->i_port); + } + /*! \brief Creates a new gosaSupportDaemon object. @param string Host The Host where the daemon is running on. @param integer Port The port which the daemon use. @@ -42,10 +52,18 @@ class gosaSupportDaemon @param boolean Connect Directly connect to daemon socket. @param float Timeout The timelimit for all socket actions. */ - public function __construct($connect=TRUE,$timeout=10) + public function __construct($connect=TRUE,$timeout=null) { #FIXME: bad idea about referencing global variables from within classes global $config; + if(!isset($config) || !$config){ + $config = session::global_get('config'); + } + + // Detect timeout + if($timeout == null){ + $timeout = $config->get_cfg_value("gosaSupportTimeout",15); + } /* This should only be the case if we call this from setup. __autoload() @@ -53,14 +71,14 @@ class gosaSupportDaemon if(!is_object($config)) { return; } # load from config, store statically - if ($config->get_cfg_value("gosa_si") != ""){ + if ($config->get_cfg_value("gosaSupportURI") != ""){ if ($this->s_host == ""){ - $this->s_host= preg_replace("/^.*@([^:]+):.*$/", "$1", $config->get_cfg_value("gosa_si")); - $this->i_port= preg_replace("/^.*@[^:]+:(.*)$/", "$1", $config->get_cfg_value("gosa_si")); - $this->s_encryption_key = preg_replace("/^(.*)@[^:]+:.*$/", "$1", $config->get_cfg_value("gosa_si")); + $this->s_host= preg_replace("/^.*@([^:]+):.*$/", "$1", $config->get_cfg_value("gosaSupportURI")); + $this->i_port= preg_replace("/^.*@[^:]+:(.*)$/", "$1", $config->get_cfg_value("gosaSupportURI")); + $this->s_encryption_key = preg_replace("/^(.*)@[^:]+:.*$/", "$1", $config->get_cfg_value("gosaSupportURI")); } - + $this->is_configured = TRUE; $this->f_timeout = $timeout; if($connect){ $this->connect(); @@ -69,6 +87,12 @@ class gosaSupportDaemon } + public function is_configured() + { + return($this->is_configured); + } + + /*! \brief Establish daemon connection. @return boolean Returns true if the connection was succesfully established. */ @@ -85,7 +109,7 @@ class gosaSupportDaemon new log("debug","gosaSupportDaemon::connect()", "Cannot connect to si-server", array(),$this->get_error()); } }else{ - $this->set_error(msgPool::cmdnotfound("GOSA_SI",_("GOsa support daemon"))); + $this->set_error(msgPool::cmdnotfound("gosaSupportURI",_("GOsa support daemon"))); } return($this->is_connected); } @@ -106,13 +130,26 @@ class gosaSupportDaemon $data = array("module_name" => $mod); $res = $this->send_data("gosa_get_hosts_with_module",$this->s_host.":".$this->i_port,$data,TRUE); $hosts = array(); - if(isset($res['XML'])){ + if(isset($res['XML'][0])){ foreach($res['XML'][0] as $name => $data){ - if(preg_match("/^HOST[0-9]*$/",$name) && $name != "HOST"){ - $hosts[] = $data[0]['MAC'][0]['VALUE']; + if(preg_match("/^ANSWER[0-9]*$/",$name)){ + if(isset($data[0]['MAC'][0]['VALUE']) && $data[0]['MAC'][0]['VALUE'] != ""){ + $hosts[] = $data[0]['MAC'][0]['VALUE']; + } elseif(isset($data[0]['IP'][0]['VALUE']) && $data[0]['IP'][0]['VALUE'] != "") { + $hosts[] = $data[0]['IP'][0]['VALUE']; + } } } } + + if(count($hosts) == 0){ + @DEBUG(DEBUG_SI, __LINE__, "".__CLASS__."::".__FUNCTION__."" , + __FILE__, "Found: 0", $info=$mod); + }else{ + @DEBUG(DEBUG_SI, __LINE__, "".__CLASS__."::".__FUNCTION__."" , + __FILE__, "Found: ".count($hosts)."", $info=$mod); + } + return($hosts); } @@ -131,6 +168,29 @@ class gosaSupportDaemon */ private function set_error($str) { + /****** + Debug handling + ******/ + $debug = debug_backtrace(); + $file = __FILE__; + $function = __FUNCTION__; + $line = __LINE__; + $class = __CLASS__; + foreach($debug as $info){ + if(!in_array($info['function'],array("send_data","_send","set_error","connect"))){ + $file = $info['file']; + $line = $info['line']; + $class = get_class($this); + $function = $info['function']; + break; + } + } + @DEBUG(DEBUG_SI, $line, "".$class."::".$function."" , $file, "".htmlentities($str)."", $info=""); + + /****** + Set error string. + ******/ + $this->b_error = TRUE; $this->s_error = $str; } @@ -169,7 +229,7 @@ class gosaSupportDaemon $ret .= $msg." "; } } - $ret = preg_replace("/ /"," ",$ret); + $ret = str_replace(" "," ",$ret); return($ret); } @@ -181,34 +241,16 @@ class gosaSupportDaemon "
gosa_get_available_kernel
". "GOSA". "GOSA". - "".$release."". + "".$release."". ""; $ret = array(); if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } + $entries = $this->_send($xml_msg,TRUE); - $entries = $this->xml_to_array($str); + /* Check if returned values represent a valid answer */ if(isset($entries['XML']) && is_array($entries['XML'])){ - - /* Check if returned values represent a valid answer */ if(isset($entries['XML'])){ - if(isset($entries['XML']['ERROR_STRING'])) { - $this->set_error($entries['XML']['ERROR_STRING']); - new log("debug","GOsa-si", - get_class($this)."::".__FUNCTION__, array(), - "FAILED error was ".$this->get_error()); - return($ret); - } - - /* Unset header tags */ $ret = $entries['XML']; foreach($ret as $key => $entry){ if(!preg_match("/^answer/i",$key)){ @@ -230,36 +272,16 @@ class gosaSupportDaemon $ret = array(); if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML']) && is_array($entries['XML'])){ - /* Check if returned values represent a valid answer */ - if(isset($entries['XML'])){ - if(isset($entries['XML']['ERROR_STRING'])) { - $this->set_error($entries['XML']['ERROR_STRING']); - new log("debug","GOsa-si", - get_class($this)."::".__FUNCTION__, array(), - "FAILED error was ".$this->get_error()); - return($ret); - } - - /* Unset header tags */ - foreach(array("HEADER","SOURCE","TARGET","SESSION_ID") as $type){ - if(isset($entries['XML'][$type])){ - unset($entries['XML'][$type]); - } + /* Unset header tags */ + foreach(array("HEADER","SOURCE","TARGET","SESSION_ID") as $type){ + if(isset($entries['XML'][$type])){ + unset($entries['XML'][$type]); } - $ret = $entries['XML']; } + $ret = $entries['XML']; } } return($ret); @@ -268,7 +290,6 @@ class gosaSupportDaemon public function FAI_get_packages($release,$attrs,$package,$from=-1,$to=-1) { - $this->reset_error(); $ret = array(); /* Check Parameter */ @@ -317,27 +338,11 @@ class gosaSupportDaemon ""; if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML']) && is_array($entries['XML'])){ /* Check if returned values represent a valid answer */ if(isset($entries['XML'])){ - if(isset($entries['XML']['ERROR_STRING'])) { - $this->set_error($entries['XML']['ERROR_STRING']); - new log("debug","GOsa-si", - get_class($this)."::".__FUNCTION__, array(), - "FAILED error was ".$this->get_error()); - return($ret); - } /* Unset header tags */ foreach(array("HEADER","SOURCE","TARGET","SESSION_ID") as $type){ @@ -357,41 +362,22 @@ class gosaSupportDaemon public function FAI_get_server($name = "") { - $this->reset_error(); $xml_msg = "
gosa_query_fai_server
GOSAGOSA
"; $ret = array(); if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + /* Check if returned values represent a valid answer */ + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML']) && is_array($entries['XML'])){ - /* Check if returned values represent a valid answer */ - if(isset($entries['XML'])){ - if(isset($entries['XML']['ERROR_STRING'])) { - $this->set_error($entries['XML']['ERROR_STRING']); - new log("debug","GOsa-si", - get_class($this)."::".__FUNCTION__, array(), - "FAILED error was ".$this->get_error()); - return($ret); - } - - /* Unset header tags */ - foreach(array("HEADER","SOURCE","TARGET","SESSION_ID") as $type){ - if(isset($entries['XML'][$type])){ - unset($entries['XML'][$type]); - } + /* Unset header tags */ + foreach(array("HEADER","SOURCE","TARGET","SESSION_ID") as $type){ + if(isset($entries['XML'][$type])){ + unset($entries['XML'][$type]); } - $ret = $entries['XML']; } + $ret = $entries['XML']; } } return($ret); @@ -400,41 +386,21 @@ class gosaSupportDaemon public function FAI_get_classes($name) { - $this->reset_error(); $xml_msg = "
gosa_query_fai_release
GOSAGOSA". - "".$name."
";; + "".$name."";; $ret = array(); if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML']) && is_array($entries['XML'])){ - /* Check if returned values represent a valid answer */ - if(isset($entries['XML'])){ - if(isset($entries['XML']['ERROR_STRING'])) { - $this->set_error($entries['XML']['ERROR_STRING']); - new log("debug","GOsa-si", - get_class($this)."::".__FUNCTION__, array($name), - "FAILED error was ".$this->get_error()); - return($ret); - } - - /* Unset header tags */ - foreach(array("HEADER","SOURCE","TARGET","SESSION_ID") as $type){ - if(isset($entries['XML'][$type])){ - unset($entries['XML'][$type]); - } + /* Unset header tags */ + foreach(array("HEADER","SOURCE","TARGET","SESSION_ID") as $type){ + if(isset($entries['XML'][$type])){ + unset($entries['XML'][$type]); } - $ret = $entries['XML']; } + $ret = $entries['XML']; } } return($ret); @@ -446,7 +412,6 @@ class gosaSupportDaemon */ public function get_queued_entries($event_types = array("*"),$from=-1,$to=-1,$sort="timestamp DESC") { - $this->reset_error(); $ret = array(); $tags = ""; @@ -479,33 +444,16 @@ class gosaSupportDaemon "; if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML']) && is_array($entries['XML'])){ - /* Check if returned values represent a valid answer */ - if(isset($entries['XML'])){ - - /* Unset header tags */ - foreach(array("HEADER","SOURCE","TARGET") as $type){ - unset($entries['XML'][$type]); - } - $ret = $entries['XML']; + /* Unset header tags */ + foreach(array("HEADER","SOURCE","TARGET","SESSION_ID") as $type){ + unset($entries['XML'][$type]); } + $ret = $entries['XML']; } } - - /* Remove session ID. No one is interested in this... */ - unset($ret['SESSION_ID']); - return($ret); } @@ -520,7 +468,6 @@ class gosaSupportDaemon trigger_error("Requires an array as parameter."); return; } - $this->reset_error(); $ret = array(); @@ -542,16 +489,7 @@ class gosaSupportDaemon "; if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML']) && is_array($entries['XML'])){ foreach($entries['XML'] as $entry){ if(is_array($entry) && array_key_exists("ID",$entry)){ @@ -574,7 +512,6 @@ class gosaSupportDaemon trigger_error("Requires an array as parameter."); return; } - $this->reset_error(); $ret = array(); @@ -596,16 +533,7 @@ class gosaSupportDaemon "; if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML'])){ foreach($entries['XML'] as $name => $entry){ if(preg_match("/^ANSWER[0-9]*$/",$name)){ @@ -628,7 +556,6 @@ class gosaSupportDaemon trigger_error("Requires an array as parameter."); return; } - $this->reset_error(); $ret = array(); @@ -650,16 +577,7 @@ class gosaSupportDaemon "; if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML'])){ foreach($entries['XML'] as $name => $entry){ if(preg_match("/^ANSWER[0-9]*$/",$name)){ @@ -683,7 +601,6 @@ class gosaSupportDaemon return; } - $this->reset_error(); $xml_msg = "
gosa_query_jobdb
@@ -700,16 +617,7 @@ class gosaSupportDaemon
"; if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return(FALSE); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if( isset($entries['XML']['HEADER']) && $entries['XML']['HEADER']=="answer" && isset($entries['XML']['ANSWER1'])){ @@ -730,7 +638,6 @@ class gosaSupportDaemon trigger_error("Requires an integer as parameter."); return; } - $this->reset_error(); $ret = array(); $xml_msg = " @@ -747,16 +654,7 @@ class gosaSupportDaemon "; if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if( isset($entries['XML']['HEADER']) && $entries['XML']['HEADER']=="answer" && isset($entries['XML']['ANSWER1'])){ @@ -778,7 +676,6 @@ class gosaSupportDaemon return; } - $this->reset_error(); $ret = array(); @@ -800,16 +697,7 @@ class gosaSupportDaemon "; if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = $this->o_sock->read(); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return($ret); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML']) || isset($entries['COUNT'])){ new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::remove_entries()", $ids,"SUCCESS"); return(TRUE); @@ -943,7 +831,6 @@ class gosaSupportDaemon */ public function update_entries($ids,$data) { - $this->reset_error(); if(!is_array($ids)){ trigger_error("Requires an array as first parameter."); return; @@ -987,17 +874,7 @@ class gosaSupportDaemon "; if($this->connect()){ - - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ - if($this->o_sock->is_error()){ - $this->set_error($this->o_sock->get_error()); - return(FALSE); - } - - $entries = $this->xml_to_array($str); + $entries = $this->_send($xml_msg,TRUE); if(isset($entries['XML'])){ if(isset($entries['XML']['ERROR_STRING'])) { $this->set_error($entries['XML']['ERROR_STRING']); @@ -1041,16 +918,11 @@ class gosaSupportDaemon $xml_msg ="
gosa_count_jobdb
GOSAGOSA
"; $this->connect(); if($this->connect()){ - $this->o_sock->write($xml_msg); - $str = trim($this->o_sock->read()); - - /* Check if something went wrong while reading */ + $entries = $this->_send($xml_msg,TRUE); if($this->o_sock->is_error()){ $this->set_error($this->o_sock->get_error()); return(0); } - - $entries = $this->xml_to_array($str); if(isset($entries['XML'])){ return($entries['XML']['COUNT']); } @@ -1093,13 +965,12 @@ class gosaSupportDaemon /* Allows simply appending a new DaemonEvent */ - public function append($event) + public function append($event, $skip_add_mac = FALSE) { if(!($event instanceof DaemonEvent)){ return(FALSE); } - $this->reset_error(); /* Add to queue if new */ @@ -1123,7 +994,9 @@ class gosaSupportDaemon /* Append an entry for each target */ foreach($targets as $target){ - $data['macaddress'] = $target; + if(!$skip_add_mac){ + $data['macaddress'] = $target; + } $this->send_data($action,$target,$data,$request_answer); if($this->is_error()){ @@ -1144,14 +1017,43 @@ class gosaSupportDaemon } -/*! \brief Returns an array containing all queued entries. + /*! \brief Returns an array containing all queued entries. @return Array All queued entries as an array. */ public function _send($data, $answer_expected= FALSE) { - $this->reset_error(); + $ret = array(); + if(!$this->connect()){ + return($ret); + } + + $this->reset_error(); + + /****** + Debug handling + ******/ + $debug = debug_backtrace(); + $file = __FILE__; + $function = __FUNCTION__; + $line = __LINE__; + $class = __CLASS__; + foreach($debug as $info){ + if(!in_array($info['function'],array("send_data","_send"))){ + $file = $info['file']; + $line = $info['line']; + $class = get_class($this); + $function = $info['function']; + break; + } + } + @DEBUG(DEBUG_SI, $line, "".$class."::".$function."" , $file, "".htmlentities($data)."", $info=""); + $start = microtime(1); + + /******* + Start sending data + *******/ if($this->connect()){ $this->o_sock->write($data); if ($answer_expected){ @@ -1160,6 +1062,7 @@ class gosaSupportDaemon /* Check if something went wrong while reading */ if($this->o_sock->is_error()){ $this->set_error($this->o_sock->get_error()); + @DEBUG(DEBUG_SI, $line, "".$class."::".$function."" , $file, sprintf('%.7f', microtime(1) - $start) , "FAILED Duration:"); return($ret); } @@ -1167,10 +1070,19 @@ class gosaSupportDaemon if(isset($entries['XML']) && is_array($entries['XML'])){ $ret = $entries; if($this->use_alternative_xml_parse_method) { + + // --------- Seems broken, check for 'ERROR' but using 'ERROR_STRING' if(isset($entries['XML'][0]['ERROR'][0]['VALUE']) && $entries['XML'][0]['ERROR'][0]['VALUE'] == "1"){ $this->set_error($entries['XML'][0]['ERROR_STRING'][0]['VALUE']); new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::_send()", array($data=>$data),"FAILED ".$this->get_error()); + // --------- + + + }elseif(isset($entries['XML'][0]['ERROR'][0]['VALUE'])){ + $this->set_error($entries['XML'][0]['ERROR'][0]['VALUE']); + new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::_send()", + array($data=>$data),"FAILED ".$this->get_error()); } }else{ if(isset($entries['XML']['ERROR_STRING'])) { @@ -1191,6 +1103,8 @@ class gosaSupportDaemon array($data=>$data),"Fire & forget, not result.! ".$this->get_error()); } } + + @DEBUG(DEBUG_SI, $line, "".$class."::".$function."" , $file, sprintf('%.7f', microtime(1) - $start) , "Duration:"); return($ret); } @@ -1236,6 +1150,9 @@ class gosaSupportDaemon function clean_queue_from_mac($mac) { global $config; + if(!isset($config) || !$config){ + $config = session::global_get('config'); + } /* First of all we have to check which jobs are startet * for $mac @@ -1299,7 +1216,7 @@ class gosaSupportDaemon { if (tests::is_mac($target)){ /* Get communication object */ - $d= new gosaSupportDaemon(TRUE,0.5); + $d= new gosaSupportDaemon(TRUE,2); $answer= $d->_send("
gosa_ping
GOSA$target
", TRUE); return (count($answer) ? TRUE:FALSE); } @@ -1604,7 +1521,7 @@ class gosaSupportDaemon */ public function krb5_get_policy($server,$name) { - $res = array(); + $ret = array(); /* Check if the given name is a valid request value */ @@ -1865,6 +1782,9 @@ class gosaSupportDaemon /* Get list of available log files */ + if(!is_array($entry)){ + $entry = array($entry); + } foreach($entry as $log_date){ $xml_msg2 = "
gosa_show_log_files_by_date_and_mac
@@ -1873,7 +1793,7 @@ class gosaSupportDaemon ".$log_date." ".$mac."
"; - + $ret[$mac][$log_date] = array(); $res = $this->_send($xml_msg2,TRUE); $ret[$mac][$log_date]['DATE_STR'] = $log_date; @@ -1972,7 +1892,7 @@ class gosaSupportDaemon There may be some Header lines like: -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.6 (GNU/Linux) */ - if(preg_match("/".normalizePreg("BEGIN PGP PUBLIC KEY BLOCK")."/",$key)){ + if(preg_match("/BEGIN PGP PUBLIC KEY BLOCK/",$key)){ /* Remove header */ $key = preg_replace("/^.*\n\n/sim","",$key);