Code

Updated gosaDaemon
[gosa.git] / gosa-core / include / class_gosaSupportDaemon.inc
1 <?php
2 /*
3  * This code is part of GOsa (http://www.gosa-project.org)
4  * Copyright (C) 2003-2008 GONICUS GmbH
5  *
6  * ID: $$Id$$
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
23 class gosaSupportDaemon
24 {
25   private $s_host       = "";
26   private $i_port       = 0;
27   private $s_encryption_key = "";
29   private $o_sock       = NULL;
30   private $f_timeout    = 2;
31   private $s_error      = "";
32   private $b_error      = FALSE;
34   private $is_connected     = FALSE;
37   /*! \brief  Creates a new gosaSupportDaemon object.
38     @param string   Host    The Host where the daemon is running on.  
39     @param integer  Port    The port which the daemon use.
40     @param string   Key     The encryption string.
41     @param boolean  Connect Directly connect to daemon socket.
42     @param float    Timeout The timelimit for all socket actions.
43    */
44   public function __construct($connect=TRUE,$timeout=0.2)
45   {
46     #FIXME: bad idea about referencing global variables from within classes
47     global $config;
49     # load from config, store statically
50     if (isset($config->current['GOSA_SI'])){
52       if ($this->s_host == ""){
53         $this->s_host= preg_replace("/^.*@([^:]+):.*$/", "$1", $config->current['GOSA_SI']);
54         $this->i_port= preg_replace("/^.*@[^:]+:(.*)$/", "$1", $config->current['GOSA_SI']);
55         $this->s_encryption_key = preg_replace("/^(.*)@[^:]+:.*$/", "$1", $config->current['GOSA_SI']);
56       }
58       $this->f_timeout = $timeout;
59       if($connect){
60         $this->connect();
61       }
62     }
63   }
66   /*! \brief  Establish daemon connection. 
67     @return boolean Returns true if the connection was succesfully established. 
68    */
69   public function connect()
70   {
71     $this->o_sock = new Socket_Client($this->s_host,$this->i_port,TRUE,$this->f_timeout);
72     if($this->o_sock->connected()){ 
73       $this->o_sock->setEncryptionKey($this->s_encryption_key); 
74       $this->is_connected = TRUE;
75     }else{
76       $this->set_error($this->o_sock->get_error());
77       $this->disconnect();
78     }
79     return($this->is_connected);
80   }
83   /*! \brief  Disconnect from gosa daemon.
84    */
85   public function disconnect()
86   {
87     $this->o_sock->close();
88     $this->is_connected = FALSE;
89   }
92   /*! \brief  Sets an error message, which can be returned with get_error().
93     @param  string  The Error message,
94    */
95   private function set_error($str)
96   {
97     $this->b_error = TRUE;
98     $this->s_error = $str;
99   }
102   /*! \brief  Sets an error message, which can be returned with get_error().
103     @param  string  The Error message,
104    */
105   private function reset_error()
106   {
107     $this->b_error = FALSE;
108     $this->s_error = "";
109   }
112   /*! \brief  Checks if an error occured.
113     @return boolean returns TRUE or FALSE, whether there is an error or not.
114    */
115   public function is_error()
116   {
117     return($this->b_error);
118   }
121   /*! \brief  Returns the last error. 
122     @return Returns the last error.
123    */
124   public function get_error()
125   {
126     $str = $this->s_error;
127     $str = preg_replace("/ /","&nbsp;",$str);
128     return($str);
129   }
132   /*! \brief  Returns an array containing all queued entries.
133     @return Array All queued entries as an array.
134    */
135   public function get_queued_entries($event_types = array("*"),$from=-1,$to=-1,$sort="timestamp DESC")
136   {
137     $this->reset_error();
138     $ret = array();
140     $tags = "";
141     foreach($event_types as $type){
142       $tags .= "<phrase><headertag>".$type."</headertag></phrase>";
143     }
144     if(count($event_types) > 1){
145       $tags = "<connector>or</connector>".$tags;
146     }
147     if(count($event_types)){
148       $tags = "<where><clause>".$tags."</clause></where>";
149     }
151     $xml_msg = "<xml>
152       <header>gosa_query_jobdb</header>
153       <target>GOSA</target>
154       <source>GOSA</source>
155       ".$tags."
157       <orderby>".$sort."</orderby>";
158 if($from != -1 && $to != -1){
159 $xml_msg.= "
160       <limit>
161        <from>".$from."</from>
162        <to>".$to."</to>
163       </limit>";
165 $xml_msg.= "
166       </xml>";
168     if($this->connect()){
169       $this->o_sock->write($xml_msg);
170       $str = trim($this->o_sock->read());
171       $entries = $this->xml_to_array($str);
172       if(isset($entries['XML']) && is_array($entries['XML'])){
174         /* Check if returned values represent a valid answer */
175         if(isset($entries['XML'])){
176           
177           /* Unset header tags */
178           foreach(array("HEADER","SOURCE","TARGET") as $type){
179             unset($entries['XML'][$type]);
180           }
181           $ret = $entries['XML']; 
182         }
183       }
184     }
185     
186     return($ret);
187   }
190   /*! \brief  Checks if the given ids are used queue ids.
191     @param  Array   The ids we want to check..
192     @return Array   An array containing all ids as index and TRUE/FALSE as value. 
193    */
194   public function ids_exist($ids)
195   {
196     if(!is_array($ids)){
197       trigger_error("Requires an array as parameter.");
198       return;
199     }
200     $this->reset_error();
202     $ret = array();
204     $xml_msg = "<xml>
205       <header>gosa_query_jobdb</header>
206       <target>GOSA</target>
207       <source>GOSA</source>
208       <where>
209       <clause>
210       <connector>or</connector>";
211     foreach($ids as $id){
212       $xml_msg .= "<phrase>
213         <operator>eq</operator>
214         <id>".$id."</id>
215         </phrase>";
216     }
217     $xml_msg .= "</clause>
218       </where>
219       </xml>";
221     if($this->connect()){
222       $this->o_sock->write($xml_msg);
223       $str = trim($this->o_sock->read());
224       $entries = $this->xml_to_array($str);
225       if(isset($entries['XML']) && is_array($entries['XML'])){
226         foreach($entries['XML'] as $entry){
227           if(isset($entry['ID'])){
228             $ret[] = $entry['ID'];
229           }
230         }
231       }
232     }
233     return($ret);
234   }
237   /*! \brief  Returns an entry containing all requested ids.
238     @param  Array   The IDs of the entries we want to return.
239     @return Array   Of the requested entries. 
240    */
241   public function get_entries_by_id($ids)
242   {
243     if(!is_array($ids)){
244       trigger_error("Requires an array as parameter.");
245       return;
246     }
247     $this->reset_error();
249     $ret = array();
251     $xml_msg = "<xml>
252       <header>gosa_query_jobdb</header>
253       <target>GOSA</target>
254       <source>GOSA</source>
255       <where>
256       <clause>
257       <connector>or</connector>";
258     foreach($ids as $id){
259       $xml_msg .= "<phrase>
260         <operator>eq</operator>
261         <id>".$id."</id>
262         </phrase>";
263     }
264     $xml_msg .= "</clause>
265       </where>
266       </xml>";
268     if($this->connect()){
269       $this->o_sock->write($xml_msg);
270       $str = trim($this->o_sock->read());
271       $entries = $this->xml_to_array($str); 
272       if(isset($entries['XML'])){
273         foreach($entries['XML'] as $name => $entry){
274           if(preg_match("/^ANSWER[0-9]*$/",$name)){
275             $ret[$name] = $entry;
276           }
277         }
278       }
279     }
280     return($ret);
281   }
284   /*! \brief  Checks if the given id is in use.
285     @param  Integer The ID of the entry.
286     @return Boolean TRUE if entry exists. 
287    */
288   public function id_exists($id)
289   {
290     if(!is_numeric($id)){
291       trigger_error("Requires an integer as parameter.");
292       return;
293     }
295     $this->reset_error();
297     $xml_msg = "<xml>
298       <header>gosa_query_jobdb</header>
299       <target>GOSA</target>
300       <source>GOSA</source>
301       <where>
302       <clause>
303       <phrase>
304       <operator>eq</operator>
305       <id>".$id."</id>
306       </phrase>
307       </clause>
308       </where>
309       </xml>";
311     if($this->connect()){
312       $this->o_sock->write($xml_msg);
313       $str = trim($this->o_sock->read());
314       $entries = $this->xml_to_array($str); 
315       if( isset($entries['XML']['HEADER']) && 
316           $entries['XML']['HEADER']=="answer" && 
317           isset($entries['XML']['ANSWER1'])){
318         return(TRUE);
319       }
320     }
321     return(FALSE);
322   }
325   /*! \brief  Returns an entry from the gosaSupportQueue
326     @param  Integer The ID of the entry we want to return.
327     @return Array   Of the requested entry. 
328    */
329   public function get_entry_by_id($id)
330   {
331     if(!is_numeric($id)){
332       trigger_error("Requires an integer as parameter.");
333       return;
334     }
335     $this->reset_error();
336   
337     $ret = array();
338     $xml_msg = "<xml>
339       <header>gosa_query_jobdb</header>
340       <target>GOSA</target>
341       <source>GOSA</source>
342       <where>
343       <clause>
344       <phrase>
345       <operator>eq</operator>
346       <id>".$id."</id>
347       </phrase>
348       </clause>
349       </where>
350       </xml>";
351     if($this->connect()){
352       $this->o_sock->write($xml_msg);
353       $str = trim($this->o_sock->read());
354       $entries = $this->xml_to_array($str); 
355       if( isset($entries['XML']['HEADER']) &&
356           $entries['XML']['HEADER']=="answer" &&
357           isset($entries['XML']['ANSWER1'])){
358         $ret = $entries['XML']['ANSWER1'];
359       }
360     }
361     return($ret);
362   }
365   /*! \brief  Removes a set of entries from the GOsa support queue. 
366     @param  Array The IDs to remove.
367     @return Boolean True on success.
368    */
369   public function remove_entries($ids)
370   {
371     if(!is_array($ids)){
372       trigger_error("Requires an array as parameter.");
373       return;
374     }
376     $this->reset_error();
378     $ret = array();
380     $xml_msg = "<xml>
381       <header>gosa_delete_jobdb_entry</header>
382       <target>GOSA</target>
383       <source>GOSA</source>
384       <where>
385       <clause>
386       <connector>or</connector>";
387     foreach($ids as $id){
388       $xml_msg .= "<phrase>
389         <operator>eq</operator>
390         <id>".$id."</id>
391         </phrase>";
392     }
393     $xml_msg .= "</clause>
394       </where>
395       </xml>";
397     if($this->connect()){
398       $this->o_sock->write($xml_msg);
399       $str = $this->o_sock->read();
400       $entries = $this->xml_to_array($str);
401       if(isset($entries['XML']) || isset($entries['COUNT'])){
402         return(TRUE);
403       }
404     }
405     return(FALSE);
406   }
410   /*! \brief  Removes an entry from the GOsa support queue. 
411     @param  Integer The ID of the entry we want to remove.
412     @return Boolean True on success.
413    */
414   public function remove_entry($id)
415   {
416     $this->reset_error();
418     $xml_msg = "<xml>
419       <header>gosa_delete_jobdb_entry</header>
420       <target>GOSA</target>
421       <source>GOSA</source>
422       <where>
423       <clause>
424       <phrase>
425       <operator>eq</operator>
426       <id>".$id."</id>
427       </phrase>
428       </clause>
429       </where>
430       </xml>";
431     if($this->connect()){
432       $this->o_sock->write($xml_msg);
433       $str = $this->o_sock->read();
434       $entries = $this->xml_to_array($str);
435       if(isset($entries['XML'])){
436         return(TRUE);
437       }
438     }
439     return(FALSE);
440   }
443   /*! \brief  Parses the given xml string into an array 
444     @param  String XML string  
445     @return Array Returns an array containing the xml structure. 
446    */
447   private function xml_to_array($xml)
448   {
449     $params = array();
450     $level = array();
451     $parser  = xml_parser_create_ns();
452     xml_parse_into_struct($parser, $xml, $vals, $index);
454     $err_id = xml_get_error_code($parser);
455     if($err_id){
456       xml_parser_free($parser);
457     }else{
458       xml_parser_free($parser);
460       foreach ($vals as $xml_elem) {
461         if ($xml_elem['type'] == 'open') {
462           if (array_key_exists('attributes',$xml_elem)) {
463             list($level[$xml_elem['level']],$extra) = array_values($xml_elem['attributes']);
464           } else {
465             $level[$xml_elem['level']] = $xml_elem['tag'];
466           }
467         }
468         if ($xml_elem['type'] == 'complete') {
469           $start_level = 1;
470           $php_stmt = '$params';
471           while($start_level < $xml_elem['level']) {
472             $php_stmt .= '[$level['.$start_level.']]';
473             $start_level++;
474           }
475           $php_stmt .= '[$xml_elem[\'tag\']] = $xml_elem[\'value\'];';
476           @eval($php_stmt);
477         }
478       }
479     }
481     if(!isset($params['XML'])){
482       if (!array_key_exists('XML', $params)){
483         $this->set_error(_("Could not parse XML."));
484       }
485       $params = array("COUNT" => 0);
486     }
488     return($params); 
489   }
492   /*! \brief  Updates an entry with a set of new values, 
493     @param  Integer The ID of the entry, we want to update.
494     @param  Array   The variables to update.   
495     @return Boolean Returns TRUE on success. 
496    */
497   public function update_entries($ids,$data)
498   {
499     $this->reset_error();
500     if(!is_array($ids)){
501       trigger_error("Requires an array as first parameter.");
502       return;
503     }
505     if(!is_array($data)){
506       trigger_error("Requires an array as second parameter.");
507       return;
508     }
510     $attr = "";
511     foreach($data as $key => $value){
512       if(is_array($value)){
513         foreach($value as $sub_value){
514           $attr.= "<$key>".strtolower($sub_value)."</$key>\n";
515         }
516       }else{
517         $attr.= "<$key>".strtolower($value)."</$key>\n";
518       }
519     }
521     $xml_msg = "<xml>
522       <header>gosa_update_status_jobdb_entry</header>
523       <target>GOSA</target>
524       <source>GOSA</source>
525       <where>
526       <clause>
527       <connector>or</connector>";
528     foreach($ids as $id){
529       $xml_msg .= "<phrase>
530         <operator>eq</operator>
531         <id>".$id."</id>
532         </phrase>";
533     }
534     $xml_msg .= "</clause>
535       </where>
536       <update>
537       ".$attr." 
538       </update>
539       </xml>";
541     if($this->connect()){
543       $this->o_sock->write($xml_msg);
544       $str      = trim($this->o_sock->read());
545       $entries = $this->xml_to_array($str);
546       if(isset($entries['XML'])){
547         if(isset($entries['XML']['ERROR_STRING'])) {
548           $this->set_error($entries['XML']['ERROR_STRING']);
549           return(FALSE);
550         }
551         return(TRUE);
552       }
553     }
554     return(FALSE);
555   }
558   /*! \brief  Returns the number of currently queued objects.
559       @return Integer  
560    */
561   public function number_of_queued_entries()
562   {
563     $xml_msg ="<xml><header>gosa_count_jobdb</header><target>GOSA</target><source>GOSA</source></xml>";
564     $this->connect();
565     if($this->connect()){
566       $this->o_sock->write($xml_msg);
567       $str     = trim($this->o_sock->read());
568       $entries = $this->xml_to_array($str);
569       if(isset($entries['XML'])){
570         return($entries['XML']['COUNT']);
571       }
572     }
573     return(-1);
574   } 
577   public function send_data($header, $to, $data= array(), $answer_expected = FALSE)
578   {
579     $xml_message= "";
581     /* Prepare data */
582     foreach ($data as $key => $value){
583       if(is_array($value)){
584         foreach($value as $sub_val){
585           $xml_message.= "<$key>$sub_value</$key>";
586         }
587       }else{
588         $xml_message.= "<$key>$value</$key>";
589       }
590     }
592     /* Multiple targets? */
593     if (!is_array($to)){
594       $to_targets= array($to);
595     } else {
596       $to_targets= $to;
597     }
599     /* Build target strings */
600     $target ="";
601     foreach($to_targets as $to){
602       $target.= "<target>$to</target>";
603     }
605     return $this->_send("<xml><header>$header</header><source>GOSA</source>$target".$xml_message."</xml>",$answer_expected);
606   }
609   /* Allows simply appending a new DaemonEvent 
610    */
611   public function append($event)
612   {
613     if(!($event instanceof DaemonEvent)){
614       return(FALSE);
615     }
616   
617     $this->reset_error();
619     /* Add to queue if new 
620      */
621     if($event->is_new()){
623       $request_answer = FALSE;
624       if($event->get_type() == SCHEDULED_EVENT){
625         $action = $event->get_schedule_action();
626         $request_answer = TRUE;
627       }elseif($event->get_type() == TRIGGERED_EVENT){
628         $action = $event->get_trigger_action();
629       }else{
630         trigger_error("Unknown type of queue event given.");
631         return(FALSE);
632       }
634       /* Get event informations, like targets..
635        */
636       $targets    = $event->get_targets();
637       $data       = $event->save();
639       /* Append an entry for each target 
640        */
641       foreach($targets as $target){
642         $data['macaddress'] = $target;
643         $this->send_data($action,$target,$data,$request_answer);
645         if($this->is_error()){
646           return(FALSE);
647         }
648       }
649       return(TRUE);
650     }else{
652       /* Updated edited entry.
653        */
654       $id                 = $event->get_id();
655       $data               = $event->save();
656       return($this->update_entries(array($id),$data));
657     }
659     return(FALSE);
660   }
663 /*! \brief  Returns an array containing all queued entries.
664     @return Array All queued entries as an array.
665    */
666   public function _send($data, $answer_expected= FALSE)
667   {
668     $this->reset_error();
669     $ret = array();
671     if($this->connect()){
672       $this->o_sock->write($data);
673       if ($answer_expected){
674         $str = trim($this->o_sock->read());
675         $entries = $this->xml_to_array($str);
676         if(isset($entries['XML']) && is_array($entries['XML'])){
677           $ret = $entries;
678           if(isset($entries['XML']['ERROR_STRING'])) {
679             $this->set_error($entries['XML']['ERROR_STRING']);
680           }
681         }
682       }
683     }
684     return($ret);
685   }
688   static function send($header, $to, $data= array(), $answer_expected = FALSE)
689   {
690     $xml_message= "";
692     /* Get communication object */
693     $d= new gosaSupportDaemon(TRUE,10);
695     /* Prepare data */
696     foreach ($data as $key => $value){
697       if(is_array($value)){
698         foreach($value as $sub_val){
699           $xml_message.= "<$key>$sub_value</$key>";
700         }
701       }else{
702         $xml_message.= "<$key>$value</$key>";
703       }
704     }
706     /* Multiple targets? */
707     if (!is_array($to)){
708       $to_targets= array($to);
709     } else {
710       $to_targets= $to;
711     }
713     /* Build target strings */
714     $target ="";
715     foreach($to_targets as $to){
716       $target.= "<target>$to</target>";
717     }
719     return $d->_send("<xml><header>$header</header><source>GOSA</source>$target".$xml_message."</xml>",$answer_expected);
720   }
723   /*! \brief  Removes all jobs from the queue that are tiggered with a specific macAddress.
724       @param  String  $mac  The mac address for which we want to remove all jobs.      
725    */
726   function clean_queue_from_mac($mac)
727   {
728     global $config;
730     /* First of all we have to check which jobs are startet 
731      *  for $mac 
732      */
733     $xml_msg ="
734       <xml>
735        <header>gosa_query_jobdb</header>
736        <target>GOSA</target>
737        <source>GOSA</source>
738        <where>
739         <clause>
740          <phrase>
741           <macaddress>".$mac."</macaddress>
742          </phrase>
743         </clause>
744        </where>
745       </xml>
746     ";  
747   
748     $data = $this->_send($xml_msg,TRUE);
749     if(is_array($data) && isset($data['XML'])){
750       $already_aborted = FALSE;
751       foreach($data['XML']  as $name => $entry){
752         if(preg_match("/answer[0-9]*/i",$name)){
753           $entry['STATUS'] = strtoupper($entry['STATUS']);
754           switch($entry['STATUS']){
756             case 'PROCESSING' :
758               /* Send abort event, but only once 
759                */
760               if($already_aborted){
761                 break;
762               }elseif(class_available("DaemonEvent_faireboot")){
763                 $already_aborted = TRUE;
764                 $tmp = new DaemonEvent_faireboot($config);
765                 $tmp->add_targets(array($mac));
766                 $tmp->set_type(TRIGGERED_EVENT);
767                 if(!$this->append($tmp)){
768                   msg_dialog::display(_("Error"), sprintf(_("Cannot send abort event for entry: %s"),$entry['ID']) , ERROR_DIALOG);
769                 }
770                 ;break;
771               }else{
772                 /* Couldn't find abort event, just remove entry */
773               }
775             case 'WAITING':
776             case 'ERROR':
777             default :
778             
779               /* Simply remove entries from queue. 
780                *  Failed or waiting events, can be removed without any trouble.
781                */ 
782               if(!$this->remove_entries(array($entry['ID']))){
783                 msg_dialog::display(_("Error"), sprintf(_("Cannot remove entry: %s"),$entry['ID']) , ERROR_DIALOG);
784               }
785               ;break;
786           }
787     
788         }
789       }
790     }
791   }
794 static function ping($target)
796   if (tests::is_mac($target)){
797     /* Get communication object */
798     $d= new gosaSupportDaemon(TRUE,0.5);
799     $answer= $d->_send("<xml><header>gosa_ping</header><source>GOSA</source><target>$target</target></xml>", TRUE);
800     return (count($answer) ? TRUE:FALSE);
801   }
803   return (FALSE);
808 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
809 ?>