3 * Bacula(R) - The Network Backup Solution
4 * Baculum - Bacula web interface
6 * Copyright (C) 2013-2016 Kern Sibbald
8 * The main author of Baculum is Marcin Haba.
9 * The original author of Bacula is Kern Sibbald, with contributions
10 * from many others, a complete list can be found in the file AUTHORS.
12 * You may use this file and others of this release according to the
13 * license defined in the LICENSE file, which includes the Affero General
14 * Public License, v3.0 ("AGPLv3") and some additional permissions and
15 * terms pursuant to its AGPLv3 Section 7.
17 * This notice must be preserved when any source code is
18 * conveyed and/or propagated.
20 * Bacula(R) is a registered trademark of Kern Sibbald.
23 Prado::using('Application.Common.Class.Errors');
24 Prado::using('Application.API.Class.BException');
25 Prado::using('Application.API.Class.APIModule');
27 class Bconsole extends APIModule {
31 const BCONSOLE_COMMAND_PATTERN = "%s%s -c %s %s 2>&1 <<END_OF_DATA\n%s\nquit\nEND_OF_DATA";
33 const BCONSOLE_DIRECTORS_PATTERN = "%s%s -c %s -l 2>&1";
35 private $allowed_commands = array(
72 private static $cmd_path;
74 private static $cfg_path;
76 public function init($param) {
77 $this->config = $this->getModule('api_config')->getConfig('bconsole');
78 if(count($this->config) > 0) {
79 $use_sudo = ((integer)$this->config['use_sudo'] === 1);
80 $cmd_path = $this->config['bin_path'];
81 $custom_cfg_path = self::getCfgPath();
82 $cfg_path = isset($custom_cfg_path) ? $custom_cfg_path : $this->config['cfg_path'];
83 $this->setEnvironmentParams($cmd_path, $cfg_path, $use_sudo);
87 public static function setCmdPath($path, $force = false) {
88 // possible to set only once
89 if (is_null(self::$cmd_path) || $force) {
90 self::$cmd_path = $path;
94 public static function getCmdPath() {
95 return self::$cmd_path;
98 public static function setCfgPath($path, $force = false) {
99 // possible to set only once
100 if (is_null(self::$cfg_path) || $force) {
101 self::$cfg_path = $path;
105 public static function getCfgPath() {
106 return self::$cfg_path;
109 public function setUseSudo($use_sudo, $force) {
110 // possible to set only once
111 if (is_null($this->use_sudo) || $force) {
112 $this->use_sudo = $use_sudo;
116 public function getUseSudo() {
117 return $this->use_sudo;
120 private function setEnvironmentParams($cmd_path, $cfg_path, $use_sudo, $force = false) {
121 self::setCmdPath($cmd_path, $force);
122 self::setCfgPath($cfg_path, $force);
123 $this->setUseSudo($use_sudo, $force);
126 private function isCommandValid($command) {
127 $command = trim($command);
128 return in_array($command, $this->allowed_commands);
131 private function prepareResult(array $output, $exitcode, $bconsole_command) {
132 array_pop($output); // deleted 'quit' bconsole command
133 for($i = 0; $i < count($output); $i++) {
134 if(strstr($output[$i], $bconsole_command) == false) {
140 $output = count($output) > 1 ? array_values($output) : array_shift($output);
141 return (object)array('output' => $output, 'exitcode' => (integer)$exitcode);
144 public function bconsoleCommand($director, array $command, $user = null) {
145 if (count($this->config) > 0 && $this->config['enabled'] !== '1') {
146 throw new BConsoleException(
147 BconsoleError::MSG_ERROR_BCONSOLE_DISABLED,
148 BconsoleError::ERROR_BCONSOLE_DISABLED
151 $base_command = count($command) > 0 ? $command[0] : null;
152 if($this->isCommandValid($base_command) === true) {
153 $result = $this->execCommand($director, $command, $user);
155 throw new BConsoleException(
156 BconsoleError::MSG_ERROR_INVALID_COMMAND,
157 BconsoleError::ERROR_INVALID_COMMAND
163 private function execCommand($director, array $command, $user) {
166 if(!is_null($director) && $this->isValidDirector($director) === false) {
167 throw new BConsoleException(
168 BconsoleError::MSG_ERROR_INVALID_DIRECTOR,
169 BconsoleError::ERROR_INVALID_DIRECTOR
172 $dir = is_null($director) ? '': '-D ' . $director;
173 $sudo = ($this->getUseSudo() === true) ? self::SUDO . ' ' : '';
174 $bconsole_command = implode(' ', $command);
176 self::BCONSOLE_COMMAND_PATTERN,
183 exec($cmd, $output, $exitcode);
185 $emsg = ' Output=>' . implode("\n", $output) . ', Exitcode=>' . $exitcode;
186 throw new BConsoleException(
187 BconsoleError::MSG_ERROR_BCONSOLE_CONNECTION_PROBLEM . $emsg,
188 BconsoleError::ERROR_BCONSOLE_CONNECTION_PROBLEM
191 $result = $this->prepareResult($output, $exitcode, $bconsole_command);
194 $this->Application->getModule('logging')->log(
197 Logging::CATEGORY_EXECUTE,
205 public function getDirectors() {
206 $sudo = ($this->getUseSudo() === true) ? self::SUDO . ' ' : '';
208 self::BCONSOLE_DIRECTORS_PATTERN,
213 exec($cmd, $output, $exitcode);
215 $emsg = ' Output=>' . implode("\n", $output) . ', Exitcode=>' . $exitcode;
216 throw new BConsoleException(
217 BconsoleError::MSG_ERROR_BCONSOLE_CONNECTION_PROBLEM . $emsg,
218 BconsoleError::ERROR_BCONSOLE_CONNECTION_PROBLEM
221 $result = (object)array('output' => $output, 'exitcode' => $exitcode);
225 private function isValidDirector($director) {
226 return in_array($director, $this->getDirectors()->output);
229 public function testBconsoleCommand(array $command, $cmd_path, $cfg_path, $use_sudo) {
230 $this->setEnvironmentParams($cmd_path, $cfg_path, $use_sudo, true);
234 $director = array_shift($this->getDirectors()->output);
235 $result = $this->bconsoleCommand($director, $command);
236 } catch (BException $e) {
237 $result = (object)array(
238 'output' => $e->getErrorMessage(),
239 'exitcode' => $e->getErrorCode()