* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
}
return $ret;
}
+
+ /**
+ * Get decoded and human readable LStat values.
+ *
+ * @param string $lstat LStat value to decode
+ * @return array decoded LStat values
+ */
+ public function lstat_human($lstat) {
+ $value = $this->decode($lstat);
+ $value['mode'] = $this->get_human_mode($value['mode']);
+ return $value;
+ }
+
+ /**
+ * Get human readable mode/attributes (ex. drwx-r-xr-x).
+ *
+ * @param integer $dmode mode value in decimal LStat form
+ * @return string mode in human readable form
+ */
+ private function get_human_mode($dmode) {
+ $ts = [
+ 0140000 => 'ssocket',
+ 0120000 => 'llink',
+ 0100000 => '-file',
+ 0060000 => 'bblock',
+ 0040000 => 'ddir',
+ 0020000 => 'cchar',
+ 0010000 => 'pfifo'
+ ];
+
+ $p = $dmode;
+ $t = decoct($dmode & 0170000); // File Encoding Bit
+ $mode = (key_exists(octdec($t), $ts)) ? $ts[octdec($t)]{0} : 'u';
+ $mode .= (($p & 0x0100) ? 'r' : '-') . (($p & 0x0080) ? 'w' : '-');
+ $mode .= (($p & 0x0040) ? (($p & 0x0800) ?'s':'x'):(($p & 0x0800) ? 'S' : '-'));
+ $mode .= (($p & 0x0020) ? 'r':'-').(($p & 0x0010)? 'w' : '-');
+ $mode .= (($p & 0x0008) ? (($p & 0x0400) ?'s':'x'):(($p & 0x0400) ? 'S' : '-'));
+ $mode .= (($p & 0x0004) ? 'r':'-').(($p & 0x0002) ? 'w' : '-');
+ $mode .= (($p & 0x0001) ? (($p & 0x0200) ?'t':'x'):(($p & 0x0200) ? 'T' : '-'));
+ return $mode;
+ }
}
?>
$sth->execute();
return $sth->fetchAll(PDO::FETCH_ASSOC);
}
+
+ /**
+ * Get job file list
+ *
+ * @param integer $jobid job identifier
+ * @param string $type file list type: saved, deleted or all.
+ * @param integer $offset SQL query offset
+ * @param integer $limit SQL query limit
+ * @param string $search search file keyword
+ * @return array jobs job list
+ */
+ public function getJobFiles($jobid, $type, $offset = 0, $limit = 100, $search = null, $fetch_group = false) {
+ $type_crit = '';
+ switch ($type) {
+ case 'saved': $type_crit = ' AND FileIndex > 0 '; break;
+ case 'deleted': $type_crit = ' AND FileIndex <= 0 '; break;
+ case 'all': $type_crit = ''; break;
+ default: $type_crit = ' AND FileIndex > 0 '; break;
+ }
+
+ $search_crit = '';
+ if (is_string($search)) {
+ $search_crit = " AND (LOWER(Path.Path) LIKE LOWER('%$search%') OR LOWER(Filename.Name) LIKE LOWER('%$search%')) ";
+ }
+
+ $fname_col = 'Path.Path || Filename.Name';
+ $db_params = $this->getModule('api_config')->getConfig('db');
+ if ($db_params['type'] === Database::MYSQL_TYPE) {
+ $fname_col = 'CONCAT(Path.Path, Filename.Name)';
+ }
+
+ $offset_sql = '';
+ if ($offset) {
+ $offset_sql = ' OFFSET ' . $offset;
+ }
+
+ $limit_sql = '';
+ if ($limit) {
+ $limit_sql = ' LIMIT ' . $limit;
+ }
+
+ $sql = "SELECT $fname_col AS file,
+ F.lstat AS lstat,
+ F.fileindex AS fileindex
+ FROM (
+ SELECT PathId AS pathid,
+ FilenameId AS filenameid,
+ Lstat AS lstat,
+ FileIndex AS fileindex
+ FROM
+ File
+ WHERE
+ JobId=$jobid
+ $type_crit
+ UNION ALL
+ SELECT PathId AS pathid,
+ FilenameId AS filenameid,
+ File.Lstat AS lstat,
+ File.FileIndex AS fileindex
+ FROM BaseFiles
+ JOIN File ON (BaseFiles.FileId = File.FileId)
+ WHERE
+ BaseFiles.JobId=$jobid
+ ) AS F, Filename, Path
+ WHERE Filename.FilenameId=F.FilenameId
+ AND Path.PathId=F.PathId
+ $search_crit
+ $offset_sql $limit_sql";
+ $connection = JobRecord::finder()->getDbConnection();
+ $connection->setActive(true);
+ $pdo = $connection->getPdoInstance();
+ $sth = $pdo->prepare($sql);
+ $sth->execute();
+ $result = [];
+ if ($fetch_group) {
+ $result = $sth->fetchAll(PDO::FETCH_COLUMN);
+ } else {
+ $result = $sth->fetchAll(PDO::FETCH_ASSOC);
+
+ // decode LStat value
+ if (is_array($result)) {
+ $blstat = $this->getModule('blstat');
+ $result_len = count($result);
+ for ($i = 0; $i < $result_len; $i++) {
+ $result[$i]['lstat'] = $blstat->lstat_human($result[$i]['lstat']);
+ }
+ }
+ }
+ return $result;
+ }
}
?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$offset = $this->Request->contains('offset') ? intval($this->Request['offset']) : 0;
$limit = $this->Request->contains('limit') ? intval($this->Request['limit']) : 0;
$search = $this->Request->contains('search') && $misc->isValidPath($this->Request['search']) ? $this->Request['search'] : null;
+ $details = $this->Request->contains('details') && $misc->isValidBooleanTrue($this->Request['details']) ? $this->Request['details'] : false;
$result = $this->getModule('bconsole')->bconsoleCommand(
$this->director,
array_shift($result->output);
$job = $this->getModule('job')->getJobById($jobid);
if (is_object($job) && in_array($job->name, $result->output)) {
- $cmd = array('list', 'files');
- if (is_string($type)) {
- /**
- * NOTE: type param has to be used BEFORE jobid=xx, otherwise it doesn't work.
- * This behavior is also described in Bacula source code (src/dird/ua_output.c).
- */
- $cmd[] = 'type="' . $type . '"';
- }
- $cmd[] = 'jobid="' . $jobid . '"';
- $result = $this->getModule('bconsole')->bconsoleCommand(
- $this->director,
- $cmd
- );
- if ($result->exitcode === 0) {
- $file_list = $this->getModule('list')->parseListFilesOutput($result->output);
- if (is_string($search)) {
- // Find items
- $file_list = $this->getModule('list')->findFileListItems($file_list, $search);
- }
- $total_items = count($file_list);
- if ($offset > 0) {
- if ($limit > 0) {
- $file_list = array_slice($file_list, $offset, $limit);
- } else {
- $file_list = array_slice($file_list, $offset);
- }
- } elseif ($limit > 0) {
- $file_list = array_slice($file_list, 0, $limit);
- }
- $this->output = array('items' => $file_list, 'total' => $total_items);
- $this->error = GenericError::ERROR_NO_ERRORS;
+ if ($details) {
+ $result = $this->getDetailedOutput([
+ 'jobid' => $jobid,
+ 'type' => $type,
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'search' => $search
+ ]);
} else {
- $this->output = $result->output;
- $this->error = $result->exitcode;
+ $result = $this->getSimpleOutput([
+ 'jobid' => $jobid,
+ 'type' => $type,
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'search' => $search
+ ]);
+ //NOTE: Standarize raw and json output in new version API v2
+ $result = [
+ 'items' => $result,
+ 'totals' => count($result)
+ ];
}
+ $this->output = $result;
+ $this->error = GenericError::ERROR_NO_ERRORS;
} else {
$this->output = JobError::MSG_ERROR_JOB_DOES_NOT_EXISTS;
$this->error = JobError::ERROR_JOB_DOES_NOT_EXISTS;
$this->error = $result->exitcode;
}
}
+
+ /**
+ * Get simple output with file list and total number of items.
+ *
+ * @params array $params job parameters to get file list
+ * @return array file list
+ */
+ protected function getSimpleOutput($params = []) {
+ $result = $this->getModule('job')->getJobFiles(
+ $params['jobid'],
+ $params['type'],
+ $params['offset'],
+ $params['limit'],
+ $params['search'],
+ true
+ );
+ return $result;
+ }
+
+ /**
+ * Get detailed output with file list.
+ * It also includes LStat value.
+ *
+ * @params array $params job parameters to get file list
+ * @return array file list
+ */
+ protected function getDetailedOutput($params = []) {
+ $result = $this->getModule('job')->getJobFiles(
+ $params['jobid'],
+ $params['type'],
+ $params['offset'],
+ $params['limit'],
+ $params['search']
+ );
+ return $result;
+ }
}
?>
"schema": {
"type": "string"
}
+ },
+ {
+ "name": "details",
+ "in": "query",
+ "description": "Show more details (including LStat value)",
+ "required": false,
+ "schema": {
+ "type": "boolean"
+ }
}
]
}