class_serverService.inc 15.6 KB
Newer Older
1 2 3 4
<?php
/*
  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
  Copyright (C) 2003  Cajus Pollmeier
5
  Copyright (C) 2011-2018  FusionDirectory
6 7 8 9 10 11 12 13 14 15 16 17 18

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
19
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20 21
*/

22
class ServerService extends simpleManagement
23
{
24
  static public $actionStatus = array(
25 26 27 28
    'start'   => 'running',
    'stop'    => 'stopped',
    'restart' => 'running',
    'status'  => '',
29 30
  );

31 32 33 34 35 36 37
  protected $departmentBrowser      = FALSE;
  protected $departmentRootVisible  = FALSE;
  protected $baseMode               = FALSE;

  protected $autoFilter   = FALSE;
  protected $autoActions  = FALSE;

38 39 40
  var $ignore_account   = TRUE;
  var $skipFooter       = TRUE;
  var $skipHeader       = TRUE;
41
  var $skipCpHandler    = TRUE;
42 43 44 45 46
  var $plugins          = array();
  var $current          = "";
  var $dialog           = FALSE;
  var $read_only        = FALSE;
  var $backup           = NULL;
47
  var $acl;
48
  var $cn;
49 50 51
  var $parent;
  var $acl_category     = "";
  var $acl_base         = "";
52

53 54
  public static $skipSnapshots = TRUE;

55 56
  var $is_account;

57
  /* Return plugin information for acl handling */
Benoit Mortier's avatar
Benoit Mortier committed
58 59 60
  static function plInfo()
  {
    return array(
61 62 63 64 65
      'plShortName'   => _('Services'),
      'plDescription' => _('Server services'),
      'plSelfModify'  => FALSE,
      'plObjectType'  => array('server'),
      'plPrority'     => 5,
66
      'plSubTabs'     => 'SERVERSERVICE',
Benoit Mortier's avatar
Benoit Mortier committed
67

68
      'plProvidedAcls'  => array()
Benoit Mortier's avatar
Benoit Mortier committed
69 70 71
    );
  }

72
  function __construct ($dn, $object, $parent = NULL)
73
  {
74
    global $config;
75

76
    $this->dn     = $dn;
77 78
    if (isset($object->attrs)) {
      $this->attrs = $object->attrs;
79
    }
80
    $this->parent = $parent;
81

82 83 84
    /* Initialize acl base */
    $this->set_acl_base($this->dn);

85 86
    $this->filterXMLPath  = get_template_path('serverService-filter.xml', TRUE, dirname(__FILE__));
    $this->listXMLPath    = get_template_path('serverService-list.xml',   TRUE, dirname(__FILE__));
87 88

    // Initialize list of used and useable services.
89 90 91
    foreach ($config->data['TABS']['SERVERSERVICE'] as $plug) {
      if (class_available($plug['CLASS'])) {
        $name = $plug['CLASS'];
92

93
        $this->plugins[$name] = new $name($dn, $this);
94

95
        /* Capture all service objectClasses, necessary for acl handling */
96 97
        if (isset($this->plugins[$name]->objectclasses)) {
          foreach ($this->plugins[$name]->objectclasses as $oc) {
98 99 100 101 102 103
            $this->objectclasses[] = $oc;
          }
        }
      }
    }

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    uasort($this->plugins,
      function($a, $b)
      {
        $aName = get_class($a);
        $bName = get_class($b);
        if (isset($a->DisplayName)) {
          $aName = $a->DisplayName;
        }
        if (isset($b->DisplayName)) {
          $bName = $b->DisplayName;
        }
        return strcmp($aName, $bName);
      }
    );

119 120 121 122 123 124 125 126 127
    parent::__construct();
  }

  function configureHeadpage ()
  {
    parent::configureHeadpage();
    $this->headpage->registerElementFilter('filterServiceStatus', 'ServerService::filterServiceStatus');

    $this->registerAction('new',                'newService');
128
    foreach ($this->plugins as $name => $obj) {
129 130 131 132 133 134 135 136 137
      $this->registerAction('new_'.$name,       'newService');
    }
    $this->registerAction('remove',             'removeService');
    $this->registerAction('saveService',        'saveService');
    $this->registerAction('cancelService',      'cancelEdit');
    $this->registerAction('status',             'updateServiceStatus');
    $this->registerAction('restart',            'updateServiceStatus');
    $this->registerAction('stop',               'updateServiceStatus');
    $this->registerAction('start',              'updateServiceStatus');
138 139 140 141 142 143 144
    $this->updateActionMenu();
  }

  function updateActionMenu()
  {
    $this->headpage->xmlData['actionmenu']['action'][0]['action'] = array();
    $available_services = $this->getAllUnusedServices();
145
    foreach ($available_services as $class => $label) {
146
      $icon = 'geticon.php?context=actions&icon=document-new&size=16';
147 148 149 150
      $infos = pluglist::pluginInfos($class);
      if (isset($infos['plIcon'])) {
        $icon = $infos['plIcon'];
      }
151
      $this->headpage->xmlData['actionmenu']['action'][0]['action'][] = array(
152
        'name' => 'new_'.$class,
153
        'type' => 'entry',
154
        'image' => $icon,
155 156 157
        'label' => $label,
      );
    }
158 159 160
  }

  /*! \brief    Filter extra POST and GET variables for this plugin.
161
   */
162 163
  function detectPostActions()
  {
164
    $action = parent::detectPostActions();
165
    if (isset($_POST['SaveService'])) {
166
      $action['action'] = 'saveService';
167
    } elseif (isset($_POST['CancelService'])) {
168
      $action['action'] = 'cancelService';
169 170
    }
    return $action;
171 172 173 174 175
  }

  /*! \brief  Edit an existing service here.
   *          Somebody clicked on the paper and pencil icon.
   */
176
  function editEntry($action, array $target, array $all)
177 178
  {
    $s_entry = array_pop($target);
179
    if ($this->plugins[$s_entry]->acl_is_readable("")) {
180 181
      $this->backup       = get_object_vars($this->plugins[$s_entry]);
      $this->dialogObject = $this->plugins[$s_entry];
182
      $this->dialog       = TRUE;
183
      $this->current      = $s_entry;
184 185 186 187 188 189 190 191
    }
  }

  /*! \brief  Editing an object was caneled.
   *          Close dialogs/tabs and remove locks.
   */
  protected function cancelEdit()
  {
192
    if (($this->backup == NULL) && $this->current) {
193
      $this->plugins[$this->current] = new $this->current($this->dn, $this);
194
      $this->plugins[$this->current]->set_acl_base($this->acl_base);
195
      $this->plugins[$this->current]->set_acl_category(preg_replace('/\/$/', '', $this->acl_category));
196 197
    } elseif (is_array($this->backup)) {
      foreach ($this->backup as $name => $value) {
198 199 200 201
        $this->plugins[$this->current]->$name = $value;
      }
    }
    $this->backup   = NULL;
202
    $this->current  = '';
203 204 205
    $this->closeDialogs();
  }

206
  /*! \brief  Let the user create a new service
207
   */
208
  function newService($action = '')
209 210
  {
    $this->closeDialogs();
211
    $serv = preg_replace('/^new_/', '', $action);
212 213 214 215
    $this->plugins[$serv]->is_account = TRUE;
    $this->dialogObject               = $this->plugins[$serv];
    $this->current                    = $serv;
    $this->dialog                     = TRUE;
216 217 218 219 220 221 222 223
  }

  /*! \brief  Save the currently edited service.
   */
  function saveService()
  {
    $this->dialogObject->save_object();
    $msgs = $this->dialogObject->check();
224 225
    if (count($msgs)) {
      foreach ($msgs as $msg) {
226
        msg_dialog::display(_('Error'), $msg, ERROR_DIALOG);
227
      }
228
    } else {
229 230 231
      $this->plugins[$this->current] = $this->dialogObject;
      $this->closeDialogs();
      $this->backup = NULL;
232
      $this->updateActionMenu();
233 234 235
    }
  }

236
  /*!\brief   Close all opened dialogs
237 238 239 240
   *          And reset "dialog open" flags to display bottom buttons again.
   */
  function closeDialogs()
  {
241
    parent::closeDialogs();
242
    $this->dialog = FALSE;
243 244
    /* Get our dn back */
    $this->dn     = $this->last_dn;
245
    set_object_info($this->dn);
246 247 248 249
  }

  /*! \brief    Remove the selected service(s)
   */
250
  protected function removeService($action, array $target)
251
  {
252
    foreach ($target as $s_entry) {
253
      $new_obj = new $s_entry($this->dn, $this);
254
      $new_obj->set_acl_base($this->acl_base);
255
      $new_obj->set_acl_category(preg_replace("/\/$/", "", $this->acl_category));
256
      $tmp = $new_obj->getListEntry();
257

258
      /* Check if we are allowed to remove this service */
259
      if ($tmp['AllowRemove']) {
260 261
        $this->plugins[$s_entry]              = $new_obj;
        $this->plugins[$s_entry]->is_account  = FALSE;
262 263 264 265
      }
    }
  }

266
  function renderList()
267
  {
268
    /* Save services in session, will be used by filterServerService when updating list */
269
    session::set('ServerService', $this->plugins);
270
    return parent::renderList();
271 272 273 274
  }

  function updateServicesVars ($service)
  {
275
    foreach (array('cn','dn') as $var) {
276 277 278 279
      if (isset($this->$var)) {
        $this->plugins[$service]->$var = $this->$var;
      }
    }
280 281 282 283
  }

  /*! \brief    Updates the status for a list of services.
   */
284
  function updateServiceStatus($action, array $target)
285
  {
286
    /* Skip if this is a new server */
287 288
    if ($this->dn == 'new') {
      msg_dialog::display(_('Information'), _('Cannot update service status until it has been saved!'), INFO_DIALOG);
289 290 291 292
      return;
    }

    /* Is this an existing action */
293
    if (!isset(static::$actionStatus[$action])) {
294
      msg_dialog::display(_('Error'), sprintf(_('Unknown action "%s"'), $action), ERROR_DIALOG);
295 296 297
      return;
    }

298
    /* Handle state changes for services */
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
    foreach ($target as $service) {
      $this->updateSingleServiceStatus($action, $service);
    }
  }

  /*! \brief    Updates the status of a service and
   *             calls an external hook if specified in fusiondirectory.conf
   */
  private function updateSingleServiceStatus($action, $service)
  {
    if ($this->plugins[$service]->is_account) {
      $this->updateServicesVars($service);

      $s_daemon = new supportDaemon();
      if ($s_daemon->is_error()) {
        msg_dialog::display(
315
          sprintf(_('Could not get execute action %s on service %s.'), $action, $service),
316 317 318
          msgPool::siError($s_daemon->get_error()), ERROR_DIALOG
        );
      } else {
319
        $target = $this->parent->getBaseObject()->macAddress;
320 321 322 323 324 325 326
        if (empty($target)) {
          msg_dialog::display(
            sprintf(_('Could not get execute action %s on service %s.'), $action, $service),
            _('This server has no mac address.'), ERROR_DIALOG
          );
          return;
        }
327 328 329
        if (is_array($target)) {
          $target = $target[0];
        }
330
        if ($action == 'status') {
331
          $res = $s_daemon->append_call('Service.is_running', $target, array('args' => array($service)));
332

333 334
          if ($s_daemon->is_error()) {
            msg_dialog::display(
335
              sprintf(_('Could not get execute action %s on service %s.'), $action, $service),
336 337 338
              msgPool::siError($s_daemon->get_error()), ERROR_DIALOG
            );
          } else {
339
            $this->plugins[$service]->setStatus($res == 'yes' ? 'running' : 'stopped');
340 341
          }
        } else {
342
          $res = $s_daemon->append_call('Service.manage', $target, array('args' => array($service, $action)));
343 344 345

          if ($s_daemon->is_error()) {
            msg_dialog::display(
346
              sprintf(_('Could not get execute action %s on service %s.'), $action, $service),
347 348
              msgPool::siError($s_daemon->get_error()), ERROR_DIALOG
            );
349
          } elseif (preg_match('/^done/', $res)) {
350
            $this->plugins[$service]->setStatus(static::$actionStatus[$action]);
351
          }
352
        }
353
        return $res;
354 355 356 357 358
      }
    }
  }

  /*! \brief   Returns a list of all used services
359
   *            CLASSNAME => _($this->plugins[*]->DisplayName);
360 361 362 363
   */
  function getAllUsedServices()
  {
    $ret = array();
364 365 366
    foreach ($this->plugins as $name => $obj) {
      if ($obj->is_account) {
        if (isset($obj->DisplayName)) {
367
          $ret[$name] = $obj->DisplayName;
368
        } else {
369 370 371 372
          $ret[$name] = $name;
        }
      }
    }
373
    return $ret;
374 375 376 377 378 379 380
  }

  /*! \brief    Returns a list of all unused services.
   */
  function getAllUnusedServices()
  {
    $tmp = $this->getAllUsedServices();
381 382
    $pool_of_ocs = array();
    foreach ($tmp as $name => $value) {
383
      $pool_of_ocs[] = get_class($this->plugins[$name]);
384
      if (isset($this->plugins[$name]->conflicts)) {
385
        $pool_of_ocs = array_merge($pool_of_ocs, $this->plugins[$name]->conflicts);
386 387 388 389
      }
    }

    $ret = array();
390
    foreach ($this->plugins as $name => $obj) {
391 392 393
      if (!$obj->acl_is_createable()) {
        continue;
      }
394 395

      /* Skip all pluigns that will lead into conflicts */
396
      $conflicts = array();
397
      if (isset($obj->conflicts)) {
398 399 400 401 402
        $conflicts = $obj->conflicts;
      }
      $conflicts[] = get_class($obj);
      if (count(array_uintersect($conflicts, $pool_of_ocs, "strcasecmp"))) {
        continue;
403 404
      }

405 406 407 408
      if (isset($obj->DisplayName)) {
        $ret[$name] = $obj->DisplayName;
      } else {
        $ret[$name] = $name;
409 410
      }
    }
411
    return $ret;
412 413 414 415 416
  }

  /*! \brief    No checks here.
   */
  function check()
417 418
  {
    return array();
419 420 421 422 423 424
  }

  /*! \brief    Keep posted form values in opened dialogs
   */
  function save_object()
  {
425
    // save_object of the dialog is called in management::execute
426 427
  }

428
  /*! \brief Remove all active services
429
   */
430
  function remove($fulldelete = FALSE)
431
  {
432
    $errors = array();
433 434 435
    foreach ($this->plugins as $name => $obj) {
      $this->updateServicesVars($name);
      if ($this->plugins[$name]->initially_was_account) {
436
        $result = $this->plugins[$name]->remove($fulldelete);
437 438 439
        if (!empty($result)) {
          $errors = array_merge($errors, $result);
        }
440 441
      }
    }
442
    return $errors;
443 444 445 446 447 448
  }

  /*! \brief    Save all active services
   */
  function save()
  {
449
    $errors = array();
450 451
    foreach ($this->plugins as $name => $obj) {
      $this->updateServicesVars($name);
452

453
      if ($this->plugins[$name]->is_account) {
454
        $result = $this->plugins[$name]->save();
455
      } elseif ($this->plugins[$name]->initially_was_account) {
456
        $result = $this->plugins[$name]->remove(FALSE);
457 458 459
      }
      if (!empty($result)) {
        $errors = array_merge($errors, $result);
460 461
      }
    }
462
    return $errors;
463 464
  }

465 466 467 468 469 470 471 472
  /*! \brief    Prepare active services to be copied.
   */
  function resetCopyInfos()
  {
    $this->dn = 'new';
    foreach ($this->plugins as &$plugin) {
      $plugin->resetCopyInfos();
    }
473
    unset($plugin);
474 475
  }

476
  /*! \brief    Forward plugin acls
477 478 479 480
   */
  function set_acl_base($base)
  {
    $this->acl_base = $base;
481
    foreach ($this->plugins as $name => $obj) {
482 483 484 485
      $this->plugins[$name]->set_acl_base($base);
    }
  }

486
  /*! \brief    Forward plugin acls
487 488 489 490
   */
  function set_acl_category($category)
  {
    $this->acl_category = $category;
491
    foreach ($this->plugins as $name => $obj) {
492 493 494
      $this->plugins[$name]->set_acl_category($category);
    }
  }
495 496 497 498 499 500 501 502

  function setTemplate ($bool)
  {
    foreach ($this->plugins as &$plugin) {
      $plugin->setTemplate($bool);
    }
    unset($plugin);
  }
503 504 505 506 507 508 509 510 511 512 513 514 515

  function getRequiredAttributes()
  {
    return array();
  }

  function adapt_from_template($attrs, $skip = array())
  {
    foreach ($this->plugins as &$plugin) {
      $plugin->adapt_from_template($attrs, $skip);
    }
    unset($plugin);
  }
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546

  /*! \brief    Returns the services list.
   *            Used in the filter class for services.
   *             class_filterServerService.inc
   */
  static function getServiceList()
  {
    return session::get('ServerService');
  }

  /*! \brief    Returns an image for the service status.
   *            Used in the service list class.
   */
  static function filterServiceStatus($a, $b, $c, $d)
  {
    $img = '';
    if (isset($d['0'])) {
      switch ($d[0]) {
        case 'stopped':
          $img = 'geticon.php?context=status&icon=task-stopped&size=16';
          break;
        case 'running':
          $img = 'geticon.php?context=status&icon=task-running&size=16';
          break;
      }
    }
    if (!empty($img)) {
      return '<img src="'.$img.'"/>';
    }
    return '&nbsp;';
  }
547 548
}
?>