--- /dev/null
+#
+# Bacula Tray Monitor Configuration File
+#
+# Copyright (C) 2000-2017 Kern Sibbald
+# License: BSD 2-Clause; see file LICENSE-FOSS
+#
+
+Monitor {
+ Name = @basename@-mon
+ RefreshInterval = 120 seconds
+}
+
+Client {
+ Name = @basename@-fd
+ Password = "@mon_fd_password@" # password for FileDaemon
+ Address = @hostname@
+ Port = @fd_port@
+}
+
+#Storage {
+# Name = @basename@-sd
+# Address = @hostname@
+# Port = @sd_port@
+# Password = "@mon_sd_password@" # password for StorageDaemon
+#}
+#
+#Director {
+# Name = @basename@-dir
+# Address = @hostname@
+# Port = @dir_port@
+# Password = "@mon_dir_password@" # password for the Directors
+#}
--- /dev/null
+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#ifndef CONF_H
+#define CONF_H
+
+#include "common.h"
+#include "ui_main-conf.h"
+#include "ui_res-conf.h"
+#include "tray_conf.h"
+
+class Conf: public QDialog
+{
+ Q_OBJECT
+
+private:
+ CONFIG *config;
+ RES_HEAD **rhead;
+public:
+ int items;
+ QLineEdit::EchoMode passtype;
+ Ui::Conf UIConf;
+ Conf();
+ ~Conf();
+ bool parse_config();
+ void addResource(RESMON *res, const char *title);
+ void addRes(int type, const char *title); /* create the resource */
+public slots:
+ void accept();
+ void selectCommandDir();
+ void addDir();
+ void addStore();
+ void addClient();
+ void togglePassword();
+};
+
+class ConfTab: public QWidget
+{
+ Q_OBJECT
+
+public:
+ Ui::ResConf ui;
+ RESMON *res;
+ int type;
+ bool new_resource;
+ ConfTab(RESMON *r): QWidget() {
+ res = r;
+ type = r->type;
+ new_resource = r->new_resource;
+ ui.setupUi(this);
+ connect(ui.bpDelete, SIGNAL(clicked()), this, SLOT(disable()));
+ };
+ ~ConfTab() {
+ if (new_resource && res) {
+ free_resource((RES*) res, res->type);
+ res = NULL;
+ }
+ };
+public slots:
+ void disable() {
+ setEnabled(false);
+ };
+ void selectCaCertificateFile();
+ void selectCaCertificateDir();
+ void selectCertificate();
+ void selectKey();
+};
+
+#endif
#!/bin/sh
+# Copyright (C) 2000-2017 Kern Sibbald
+# License: BSD 2-Clause; see file LICENSE-FOSS
+#
sbindir=@sbindir@
sysconfdir=@sysconfdir@
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Conf</class>
+ <widget class="QDialog" name="Conf">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>556</width>
+ <height>337</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configuration</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tabMonitor">
+ <attribute name="title">
+ <string>Monitor Configuration</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="toolTip">
+ <string>The Monitor name will be used during the authentication phase.</string>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="editName">
+ <property name="toolTip">
+ <string>The Monitor name will be used during the authentication phase.</string>
+ </property>
+ <property name="maxLength">
+ <number>127</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Refresh Interval:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="spinRefresh">
+ <property name="minimum">
+ <number>5</number>
+ </property>
+ <property name="maximum">
+ <number>9999</number>
+ </property>
+ <property name="value">
+ <number>120</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="toolTip">
+ <string>Specify the "Command Directory" where the tray-monitor program will check regularly for jobs to run</string>
+ </property>
+ <property name="text">
+ <string>Command Directory:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="editCommandDir">
+ <property name="toolTip">
+ <string>Specify the "Command Directory" where the tray-monitor program will check regularly for jobs to run</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="bpCommandDir">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="toolTip">
+ <string>Display or Hide advanced options in the "Run Job" window</string>
+ </property>
+ <property name="text">
+ <string>Display Advanced Options:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="cbDspAdvanced">
+ <property name="toolTip">
+ <string>Display or Hide advanced options in the "Run Job" window</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QPushButton" name="bpSave">
+ <property name="toolTip">
+ <string>Save and Apply the changes</string>
+ </property>
+ <property name="text">
+ <string>Save</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../main.qrc">
+ <normaloff>:/images/label.png</normaloff>:/images/label.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="bpCancel">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../main.qrc">
+ <normaloff>:/images/A.png</normaloff>:/images/A.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="bpStrip">
+ <property name="toolTip">
+ <string>Show/Hide Passwords</string>
+ </property>
+ <property name="text">
+ <string>Password</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../main.qrc">
+ <normaloff>:/images/zoom.png</normaloff>:/images/zoom.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="bpAddClient">
+ <property name="toolTip">
+ <string>Add Client resource to monitor</string>
+ </property>
+ <property name="text">
+ <string>Client </string>
+ </property>
+ <property name="icon">
+ <iconset resource="../main.qrc">
+ <normaloff>:/images/mark.png</normaloff>:/images/mark.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="bpAddStorage">
+ <property name="toolTip">
+ <string>Add Storage resource to monitor</string>
+ </property>
+ <property name="text">
+ <string>Storage</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../main.qrc">
+ <normaloff>:/images/mark.png</normaloff>:/images/mark.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="bpAddDir">
+ <property name="toolTip">
+ <string>Add Director resource to monitor</string>
+ </property>
+ <property name="text">
+ <string> Director</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../main.qrc">
+ <normaloff>:/images/mark.png</normaloff>:/images/mark.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../main.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>bpSave</sender>
+ <signal>clicked()</signal>
+ <receiver>Conf</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>511</x>
+ <y>30</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>521</x>
+ <y>46</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>Conf</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>511</x>
+ <y>76</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>521</x>
+ <y>159</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpStrip</sender>
+ <signal>clicked()</signal>
+ <receiver>Conf</receiver>
+ <slot>togglePassword()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>511</x>
+ <y>178</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>496</x>
+ <y>142</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpAddClient</sender>
+ <signal>clicked()</signal>
+ <receiver>Conf</receiver>
+ <slot>addClient()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>511</x>
+ <y>239</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>521</x>
+ <y>245</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpAddStorage</sender>
+ <signal>clicked()</signal>
+ <receiver>Conf</receiver>
+ <slot>addStore()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>511</x>
+ <y>272</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>521</x>
+ <y>289</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpAddDir</sender>
+ <signal>clicked()</signal>
+ <receiver>Conf</receiver>
+ <slot>addDir()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>511</x>
+ <y>313</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>521</x>
+ <y>331</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpCommandDir</sender>
+ <signal>clicked()</signal>
+ <receiver>Conf</receiver>
+ <slot>selectCommandDir()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>405</x>
+ <y>135</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>466</x>
+ <y>112</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+ <slots>
+ <slot>togglePassword()</slot>
+ <slot>addClient()</slot>
+ <slot>addStore()</slot>
+ <slot>addDir()</slot>
+ <slot>selectCommandDir()</slot>
+ </slots>
+</ui>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ResConf</class>
+ <widget class="QWidget" name="ResConf">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>417</width>
+ <height>541</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>General</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="toolTip">
+ <string>The Name will be used only in the Tray Monitor interface</string>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="editName">
+ <property name="toolTip">
+ <string>The Name will be used only in the Tray Monitor interface</string>
+ </property>
+ <property name="maxLength">
+ <number>127</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Description:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="editDescription">
+ <property name="maxLength">
+ <number>512</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelPassword">
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="editPassword">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="maxLength">
+ <number>127</number>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::PasswordEchoOnEdit</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Address:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="editAddress">
+ <property name="maxLength">
+ <number>1024</number>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="editPort">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maxLength">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Timeout:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QLineEdit" name="editTimeout">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="maxLength">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="labelRemote">
+ <property name="toolTip">
+ <string>Use Client Initiated backup/restore feature</string>
+ </property>
+ <property name="text">
+ <string>Remote</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QCheckBox" name="cbRemote">
+ <property name="toolTip">
+ <string>Use Client Initiated backup/restore feature</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="toolTip">
+ <string>Update the tray monitor icon with the status of this component</string>
+ </property>
+ <property name="text">
+ <string>Monitor:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QCheckBox" name="cbMonitor">
+ <property name="toolTip">
+ <string>Update the tray monitor icon with the status of this component</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <widget class="QCheckBox" name="cbUseSetIp">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="labelSetIp">
+ <property name="text">
+ <string>Use SetIp:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>TLS</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="3" column="2">
+ <widget class="QToolButton" name="bpCertificate">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>CA Certificate File:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="cbTLSEnabled">
+ <property name="text">
+ <string>Enabled</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QToolButton" name="bpCaCertificateFile">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Key File:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="editCaCertificateDir"/>
+ </item>
+ <item row="2" column="2">
+ <widget class="QToolButton" name="bpCaCertificateDir">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Certificate File:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="editKey"/>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="editCertificate"/>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="editCaCertificateFile"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>CA Certificate Directory:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QToolButton" name="bpKey">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <zorder>cbTLSEnabled</zorder>
+ <zorder>editCaCertificateFile</zorder>
+ <zorder>label_5</zorder>
+ <zorder>label_6</zorder>
+ <zorder>editCaCertificateDir</zorder>
+ <zorder>label_7</zorder>
+ <zorder>editCertificate</zorder>
+ <zorder>label_8</zorder>
+ <zorder>editKey</zorder>
+ <zorder>bpCaCertificateFile</zorder>
+ <zorder>bpCaCertificateDir</zorder>
+ <zorder>bpCertificate</zorder>
+ <zorder>bpKey</zorder>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QPushButton" name="bpDelete">
+ <property name="maximumSize">
+ <size>
+ <width>64</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>64</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../main.qrc">
+ <normaloff>:/images/purge.png</normaloff>:/images/purge.png</iconset>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>editName</tabstop>
+ <tabstop>editDescription</tabstop>
+ <tabstop>editPassword</tabstop>
+ <tabstop>editAddress</tabstop>
+ <tabstop>editPort</tabstop>
+ <tabstop>editTimeout</tabstop>
+ <tabstop>cbRemote</tabstop>
+ <tabstop>cbTLSEnabled</tabstop>
+ <tabstop>editCaCertificateFile</tabstop>
+ <tabstop>bpCaCertificateFile</tabstop>
+ <tabstop>editCaCertificateDir</tabstop>
+ <tabstop>bpCaCertificateDir</tabstop>
+ <tabstop>editCertificate</tabstop>
+ <tabstop>bpCertificate</tabstop>
+ <tabstop>editKey</tabstop>
+ <tabstop>bpKey</tabstop>
+ </tabstops>
+ <resources>
+ <include location="../main.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>bpCaCertificateFile</sender>
+ <signal>clicked()</signal>
+ <receiver>ResConf</receiver>
+ <slot>selectCaCertificateFile()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>461</x>
+ <y>294</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>521</x>
+ <y>247</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpCaCertificateDir</sender>
+ <signal>clicked()</signal>
+ <receiver>ResConf</receiver>
+ <slot>selectCaCertificateDir()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>452</x>
+ <y>334</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>501</x>
+ <y>355</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpCertificate</sender>
+ <signal>clicked()</signal>
+ <receiver>ResConf</receiver>
+ <slot>selectCertificate()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>459</x>
+ <y>364</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>495</x>
+ <y>384</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>bpKey</sender>
+ <signal>clicked()</signal>
+ <receiver>ResConf</receiver>
+ <slot>selectKey()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>461</x>
+ <y>395</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>481</x>
+ <y>410</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbTLSEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>editCaCertificateFile</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>132</x>
+ <y>271</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>249</x>
+ <y>291</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbTLSEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>editCaCertificateDir</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>120</x>
+ <y>274</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>203</x>
+ <y>325</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbTLSEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>editCertificate</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>68</x>
+ <y>271</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>220</x>
+ <y>360</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbTLSEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>editKey</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>51</x>
+ <y>275</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>288</x>
+ <y>392</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbTLSEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>bpCaCertificateFile</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>161</x>
+ <y>267</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>449</x>
+ <y>291</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbTLSEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>bpCaCertificateDir</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>145</x>
+ <y>271</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>455</x>
+ <y>329</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbTLSEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>bpCertificate</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>140</x>
+ <y>266</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>459</x>
+ <y>358</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbTLSEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>bpKey</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>118</x>
+ <y>272</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>458</x>
+ <y>389</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+ <slots>
+ <slot>selectCaCertificateFile()</slot>
+ <slot>selectCaCertificateDir()</slot>
+ <slot>selectCertificate()</slot>
+ <slot>selectKey()</slot>
+ </slots>
+</ui>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>runForm</class>
+ <widget class="QWidget" name="runForm">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>568</width>
+ <height>407</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Run job</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QLabel" name="run">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>11</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string><h3>Run a Job</h3></string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>5</height>
+ </size>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="../main.qrc">:/images/runit.png</pixmap>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab1">
+ <attribute name="title">
+ <string>Properties</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string/>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Job:</string>
+ </property>
+ <property name="buddy">
+ <cstring>jobCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="jobCombo">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="sizeAdjustPolicy">
+ <enum>QComboBox::AdjustToContents</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>When:</string>
+ </property>
+ <property name="buddy">
+ <cstring>dateTimeEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDateTimeEdit" name="dateTimeEdit">
+ <property name="currentSection">
+ <enum>QDateTimeEdit::YearSection</enum>
+ </property>
+ <property name="displayFormat">
+ <string>yyyy-MM-dd hh:mm:ss</string>
+ </property>
+ <property name="calendarPopup">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="boxEstimate">
+ <property name="toolTip">
+ <string><html><head/><body><p>Job statistics computed from the Catalog with previous jobs.</p><p>For accurate information, it is possible to use the bconsole &quot;estimate&quot; command.</p></body></html></string>
+ </property>
+ <property name="title">
+ <string>Estimate:</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout_3">
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelJobBytes_2">
+ <property name="text">
+ <string>Job Bytes:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="labelJobBytes">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelJobFiles_2">
+ <property name="text">
+ <string>Job Files:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="labelJobFiles">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>Level:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="labelJobLevel">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab2">
+ <attribute name="title">
+ <string>Advanced</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Level:</string>
+ </property>
+ <property name="buddy">
+ <cstring>levelCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="levelCombo"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Client:</string>
+ </property>
+ <property name="buddy">
+ <cstring>clientCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="clientCombo"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>FileSet:</string>
+ </property>
+ <property name="buddy">
+ <cstring>filesetCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="filesetCombo"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Pool:</string>
+ </property>
+ <property name="buddy">
+ <cstring>poolCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="poolCombo"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Storage:</string>
+ </property>
+ <property name="buddy">
+ <cstring>storageCombo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QComboBox" name="storageCombo"/>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Catalog:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QComboBox" name="catalogCombo"/>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Priority:</string>
+ </property>
+ <property name="buddy">
+ <cstring>prioritySpin</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QSpinBox" name="prioritySpin">
+ <property name="minimumSize">
+ <size>
+ <width>60</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>60</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>10000</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>5</height>
+ </size>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="okButton">
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../main.qrc"/>
+ </resources>
+ <connections/>
+</ui>
--- /dev/null
+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#include "runjob.h"
+#include <QMessageBox>
+
+static void fillcombo(QComboBox *cb, alist *lst, bool addempty=true)
+{
+ if (lst && lst->size() > 0) {
+ QStringList list;
+ char *str;
+ if (addempty) {
+ list << QString("");
+ }
+ foreach_alist(str, lst) {
+ list << QString(str);
+ }
+ cb->addItems(list);
+ } else {
+ cb->setEnabled(false);
+ }
+}
+
+RunJob::RunJob(RESMON *r): QDialog(), res(r), tabAdvanced(NULL)
+{
+ int nbjob;
+ if (res->jobs->size() == 0) {
+ QMessageBox msgBox;
+ msgBox.setText(_("This restricted console does not have access to Backup jobs"));
+ msgBox.setIcon(QMessageBox::Warning);
+ msgBox.exec();
+ deleteLater();
+ return;
+
+ }
+
+ ui.setupUi(this);
+ setModal(true);
+ connect(ui.cancelButton, SIGNAL(clicked()), this, SLOT(close_cb()));
+ connect(ui.okButton, SIGNAL(clicked()), this, SLOT(runjob()));
+ connect(ui.jobCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(jobChanged(int)));
+ connect(ui.levelCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(levelChanged(int)));
+ ui.dateTimeEdit->setMinimumDate(QDate::currentDate());
+ ui.dateTimeEdit->setMaximumDate(QDate::currentDate().addDays(7));
+ ui.dateTimeEdit->setDate(QDate::currentDate());
+ ui.dateTimeEdit->setTime(QTime::currentTime());
+ ui.boxEstimate->setVisible(false);
+
+ res->mutex->lock();
+ nbjob = res->jobs->size();
+ fillcombo(ui.jobCombo, res->jobs, (nbjob > 1));
+ fillcombo(ui.clientCombo, res->clients);
+ fillcombo(ui.filesetCombo,res->filesets);
+ fillcombo(ui.poolCombo, res->pools);
+ fillcombo(ui.storageCombo,res->storages);
+ fillcombo(ui.catalogCombo,res->catalogs);
+ res->mutex->unlock();
+ connect(ui.tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChange(int)));
+ QStringList levels;
+ levels << "" << "Incremental" << "Differential" << "Full";
+ ui.levelCombo->addItems(levels);
+
+ MONITOR *m = (MONITOR*) GetNextRes(R_MONITOR, NULL);
+ if (!m->display_advanced_options) {
+ tabAdvanced = ui.tabWidget->widget(1);
+ ui.tabWidget->removeTab(1);
+ }
+
+ show();
+};
+
+void RunJob::tabChange(int idx)
+{
+ QString q = ui.tabWidget->tabText(idx);
+ if (q.contains("Advanced")) {
+ if (ui.jobCombo->currentText().compare("") == 0) {
+ pm_strcpy(curjob, "");
+ ui.tab2->setEnabled(false);
+
+ } else if (ui.jobCombo->currentText().compare(curjob.c_str()) != 0) {
+ task *t = new task();
+ char *job = bstrdup(ui.jobCombo->currentText().toUtf8().data());
+ pm_strcpy(curjob, job); // Keep the job name to not refresh the Advanced tab the next time
+
+ Dmsg1(10, "get defaults for %s\n", job);
+ res->mutex->lock();
+ bfree_and_null(res->defaults.job);
+ res->defaults.job = job;
+ res->mutex->unlock();
+
+ ui.tab2->setEnabled(false);
+ connect(t, SIGNAL(done(task *)), this, SLOT(fill_defaults(task *)), Qt::QueuedConnection);
+ t->init(res, TASK_DEFAULTS);
+ res->wrk->queue(t);
+ }
+ }
+}
+
+void RunJob::runjob()
+{
+ POOL_MEM tmp;
+ char *p;
+
+ p = ui.jobCombo->currentText().toUtf8().data();
+ if (!p || !*p) {
+ QMessageBox msgBox;
+ msgBox.setText(_("Nothing selected"));
+ msgBox.setIcon(QMessageBox::Warning);
+ msgBox.exec();
+ return;
+ }
+
+ Mmsg(command, "run job=\"%s\" yes", p);
+
+ if (strcmp(p, NPRTB(res->defaults.job)) == 0 || strcmp("", NPRTB(res->defaults.job)) == 0) {
+ p = ui.storageCombo->currentText().toUtf8().data();
+ if (p && *p && strcmp(p, NPRTB(res->defaults.storage)) != 0) {
+ Mmsg(tmp, " storage=\"%s\"", p);
+ pm_strcat(command, tmp.c_str());
+ }
+
+ p = ui.clientCombo->currentText().toUtf8().data();
+ if (p && *p && strcmp(p, NPRTB(res->defaults.client)) != 0) {
+ Mmsg(tmp, " client=\"%s\"", p);
+ pm_strcat(command, tmp.c_str());
+ }
+
+ p = ui.levelCombo->currentText().toUtf8().data();
+ if (p && *p && strcmp(p, NPRTB(res->defaults.level)) != 0) {
+ Mmsg(tmp, " level=\"%s\"", p);
+ pm_strcat(command, tmp.c_str());
+ }
+
+ p = ui.poolCombo->currentText().toUtf8().data();
+ if (p && *p && strcmp(p, NPRTB(res->defaults.pool)) != 0) {
+ Mmsg(tmp, " pool=\"%s\"", p);
+ pm_strcat(command, tmp.c_str());
+ }
+
+ p = ui.filesetCombo->currentText().toUtf8().data();
+ if (p && *p && strcmp(p, NPRTB(res->defaults.fileset)) != 0) {
+ Mmsg(tmp, " fileset=\"%s\"", p);
+ pm_strcat(command, tmp.c_str());
+ }
+
+ if (res->defaults.priority && res->defaults.priority != ui.prioritySpin->value()) {
+ Mmsg(tmp, " priority=\"%d\"", res->defaults.priority);
+ pm_strcat(command, tmp.c_str());
+ }
+ }
+
+ QDate dnow = QDate::currentDate();
+ QTime tnow = QTime::currentTime();
+ QDate dval = ui.dateTimeEdit->date();
+ QTime tval = ui.dateTimeEdit->time();
+
+ if (dval > dnow || (dval == dnow && tval > tnow)) {
+ Mmsg(tmp, " when=\"%s %s\"", dval.toString("yyyy-MM-dd").toUtf8().data(), tval.toString("hh:mm:00").toUtf8().data());
+ pm_strcat(command, tmp.c_str());
+ }
+
+ if (res->type == R_CLIENT) {
+ pm_strcat(command, " fdcalled=1");
+ }
+
+ // Build the command and run it!
+ task *t = new task();
+ connect(t, SIGNAL(done(task *)), this, SLOT(jobStarted(task *)), Qt::QueuedConnection);
+ t->arg = command.c_str();
+ t->init(res, TASK_RUN);
+ res->wrk->queue(t);
+}
+
+void RunJob::jobStarted(task *t)
+{
+ Dmsg1(10, "%s\n", command.c_str());
+ Dmsg1(10, "-> jobid=%d\n", t->result.i);
+ deleteLater();
+ delete t;
+}
+
+void RunJob::close_cb(task *t)
+{
+ deleteLater();
+ delete t;
+}
+
+void RunJob::close_cb()
+{
+ task *t = new task();
+ connect(t, SIGNAL(done(task *)), this, SLOT(close_cb(task *)), Qt::QueuedConnection);
+ t->init(res, TASK_DISCONNECT);
+ res->wrk->queue(t);
+}
+
+void RunJob::jobChanged(int)
+{
+ char *p;
+ ui.levelCombo->setCurrentIndex(0);
+ ui.storageCombo->setCurrentIndex(0);
+ ui.filesetCombo->setCurrentIndex(0);
+ ui.clientCombo->setCurrentIndex(0);
+ ui.storageCombo->setCurrentIndex(0);
+ ui.poolCombo->setCurrentIndex(0);
+ ui.catalogCombo->setCurrentIndex(0);
+
+ p = ui.jobCombo->currentText().toUtf8().data();
+ if (p && *p) {
+ task *t = new task();
+ pm_strcpy(info, p);
+ connect(t, SIGNAL(done(task *)), this, SLOT(jobInfo(task *)), Qt::QueuedConnection);
+ t->arg = info.c_str(); // Jobname
+ t->arg2 = NULL; // Level
+ t->init(res, TASK_INFO);
+ res->wrk->queue(t);
+ }
+}
+
+void RunJob::levelChanged(int)
+{
+ char *p;
+ p = ui.jobCombo->currentText().toUtf8().data();
+ if (p && *p) {
+ pm_strcpy(info, p);
+ p = ui.levelCombo->currentText().toUtf8().data();
+ if (p && *p) {
+ task *t = new task();
+ pm_strcpy(level, p);
+ connect(t, SIGNAL(done(task *)), this, SLOT(jobInfo(task *)), Qt::QueuedConnection);
+ t->arg = info.c_str(); // Jobname
+ t->arg2 = level.c_str(); // Level
+ t->init(res, TASK_INFO);
+ res->wrk->queue(t);
+ }
+ }
+}
+
+void RunJob::jobInfo(task *t)
+{
+ char ed1[50];
+ res->mutex->lock();
+ if (res->infos.CorrNbJob == 0) {
+ ui.boxEstimate->setVisible(false);
+ } else {
+ QString t;
+ edit_uint64_with_suffix(res->infos.JobBytes, ed1);
+ strncat(ed1, "B", sizeof(ed1));
+ ui.labelJobBytes->setText(QString(ed1));
+ ui.labelJobFiles->setText(QString(edit_uint64_with_commas(res->infos.JobFiles, ed1)));
+ ui.labelJobLevel->setText(QString(job_level_to_str(res->infos.JobLevel)));
+ t = tr("Computed over %1 job%2, the correlation is %3/100.").arg(res->infos.CorrNbJob).arg(res->infos.CorrNbJob>1?"s":"").arg(res->infos.CorrJobBytes);
+ ui.labelJobBytes_2->setToolTip(t);
+ t = tr("Computed over %1 job%2, The correlation is %3/100.").arg(res->infos.CorrNbJob).arg(res->infos.CorrNbJob>1?"s":"").arg(res->infos.CorrJobFiles);
+ ui.labelJobFiles_2->setToolTip(t);
+ ui.boxEstimate->setVisible(true);
+ }
+ res->mutex->unlock();
+ t->deleteLater();
+}
+
+static void set_combo(QComboBox *dest, char *str)
+{
+ if (str) {
+ int idx = dest->findText(QString(str), Qt::MatchExactly);
+ if (idx >= 0) {
+ dest->setCurrentIndex(idx);
+ }
+ }
+}
+
+void RunJob::fill_defaults(task *t)
+{
+ if (t->status == true) {
+ res->mutex->lock();
+ set_combo(ui.levelCombo, res->defaults.level);
+ set_combo(ui.filesetCombo, res->defaults.fileset);
+ set_combo(ui.clientCombo, res->defaults.client);
+ set_combo(ui.storageCombo, res->defaults.storage);
+ set_combo(ui.poolCombo, res->defaults.pool);
+ set_combo(ui.catalogCombo, res->defaults.catalog);
+ res->mutex->unlock();
+ }
+
+ ui.tab2->setEnabled(true);
+ t->deleteLater();
+}
+
+RunJob::~RunJob()
+{
+ Dmsg0(10, "~RunJob()\n");
+ if (tabAdvanced) {
+ delete tabAdvanced;
+ }
+}
+
+void TSched::init(const char *cmd_dir)
+{
+ bool started = (timer >= 0);
+ if (started) {
+ stop();
+ }
+
+ bfree_and_null(command_dir);
+ command_dir = bstrdup(cmd_dir);
+
+ if (started) {
+ start();
+ }
+}
+
+TSched::TSched() {
+ timer = -1;
+ command_dir = NULL;
+}
+
+TSched::~TSched() {
+ if (timer >= 0) {
+ stop();
+ }
+ bfree_and_null(command_dir);
+}
+
+#ifndef HAVE_READDIR_R
+int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
+#else
+#include <dirent.h>
+#endif
+
+bool TSched::read_command_file(const char *file, alist *lst, btime_t mtime)
+{
+ POOLMEM *line;
+ bool ret=false;
+ char *p;
+ TSchedJob *s;
+ Dmsg1(50, "open command file %s\n", file);
+ FILE *fp = fopen(file, "r");
+ if (!fp) {
+ return false;
+ }
+ line = get_pool_memory(PM_FNAME);
+
+ /* Get the first line, client/component:command */
+ while (bfgets(line, fp) != NULL) {
+ strip_trailing_junk(line);
+ Dmsg1(50, "%s\n", line);
+ if (line[0] == '#') {
+ continue;
+ }
+
+ if ((p = strchr(line, ':')) != NULL) {
+ *p=0;
+ s = new TSchedJob(line, p+1, mtime);
+ lst->append(s);
+ ret = true;
+ }
+ }
+
+ free_pool_memory(line);
+ fclose(fp);
+ return ret;
+}
+
+#include "lib/plugins.h"
+#include "lib/cmd_parser.h"
+
+void TSched::timerEvent(QTimerEvent *event)
+{
+ POOL_MEM tmp, command;
+ TSchedJob *j;
+ alist lst(10, not_owned_by_alist);
+ arg_parser parser;
+ int i;
+ task *t;
+ RESMON *res;
+ scan_for_commands(&lst);
+
+ foreach_alist(j, (&lst)) {
+ if (parser.parse_cmd(j->command) == bRC_OK) {
+ if ((i = parser.find_arg_with_value("job")) > 0) {
+ QMessageBox msgbox;
+ foreach_res(res, R_CLIENT) {
+ if (strcmp(res->hdr.name, j->component) == 0) {
+ break;
+ }
+ }
+ if (!res) {
+ foreach_res(res, R_DIRECTOR) {
+ if (strcmp(res->hdr.name, j->component) == 0) {
+ break;
+ }
+ }
+ }
+ if (!res) {
+ msgbox.setIcon(QMessageBox::Information);
+ msgbox.setText(QString("Unable to find the component \"%1\" to run the job \"%2\".").arg(j->component, j->command));
+ msgbox.setStandardButtons(QMessageBox::Ignore);
+ } else {
+
+ msgbox.setIcon(QMessageBox::Information);
+ msgbox.setText(QString("The job \"%1\" will start automatically in few seconds...").arg(parser.argv[i]));
+ msgbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Ignore);
+ msgbox.setDefaultButton(QMessageBox::Ok);
+ msgbox.button(QMessageBox::Ok)->animateClick(6000);
+ }
+ switch(msgbox.exec()) {
+ case QMessageBox::Ok:
+ Mmsg(command, "%s yes", j->command);
+
+ if (res->type == R_CLIENT) {
+ pm_strcat(command, " fdcalled=1");
+ }
+
+ // Build the command and run it!
+ t = new task();
+ connect(t, SIGNAL(done(task *)), this, SLOT(jobStarted(task *)), Qt::QueuedConnection);
+ t->arg = command.c_str();
+ t->init(res, TASK_RUN);
+ res->wrk->queue(t);
+
+ break;
+ case QMessageBox::Cancel:
+ case QMessageBox::Ignore:
+ break;
+ }
+ }
+ }
+ delete j;
+ }
+}
+
+void TSched::jobStarted(task *t)
+{
+ Dmsg1(10, "-> jobid=%d\n", t->result.i);
+ t->deleteLater();
+}
+
+
+bool TSched::scan_for_commands(alist *commands)
+{
+ int name_max, len;
+ DIR* dp = NULL;
+ POOL_MEM fname(PM_FNAME), fname2(PM_FNAME);
+ bool ret=false, found=false;
+ struct dirent *entry = NULL, *result;
+ struct stat statp;
+
+ name_max = pathconf(".", _PC_NAME_MAX);
+ if (name_max < 1024) {
+ name_max = 1024;
+ }
+
+ if (!(dp = opendir(command_dir))) {
+ berrno be;
+ Dmsg2(0, "Failed to open directory %s: ERR=%s\n",
+ command_dir, be.bstrerror());
+ goto bail_out;
+ }
+
+ entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
+ for ( ;; ) {
+ if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
+ if (!found) {
+ goto bail_out;
+ }
+ break;
+ }
+ if (strcmp(result->d_name, ".") == 0 ||
+ strcmp(result->d_name, "..") == 0) {
+ continue;
+ }
+ len = strlen(result->d_name);
+ if (len <= 5) {
+ continue;
+ }
+ if (strcmp(result->d_name + len - 5, ".bcmd") != 0) {
+ continue;
+ }
+
+ Mmsg(fname, "%s/%s", command_dir, result->d_name);
+
+ if (lstat(fname.c_str(), &statp) != 0 || !S_ISREG(statp.st_mode)) {
+ continue; /* ignore directories & special files */
+ }
+
+ if (read_command_file(fname.c_str(), commands, statp.st_mtime)) {
+ Mmsg(fname2, "%s.ok", fname.c_str());
+ unlink(fname2.c_str());
+ rename(fname.c_str(), fname2.c_str()); // TODO: We should probably unlink the file
+ }
+ }
+bail_out:
+ if (entry) {
+ free(entry);
+ }
+ if (dp) {
+ closedir(dp);
+ }
+ return ret;
+}
--- /dev/null
+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#ifndef RUN_H
+#define RUN_H
+
+#include "common.h"
+#include "ui_run.h"
+#include "tray_conf.h"
+#include "task.h"
+
+class RunJob: public QDialog
+{
+ Q_OBJECT
+
+public:
+ RESMON *res;
+ QWidget *tabAdvanced;
+ POOL_MEM command;
+ POOL_MEM info;
+ POOL_MEM level;
+ POOL_MEM curjob;
+ Ui::runForm ui;
+ RunJob(RESMON *r);
+ ~RunJob();
+
+public slots:
+ void jobChanged(int);
+ void levelChanged(int);
+ void jobStarted(task *);
+ void jobInfo(task *);
+ void fill_defaults(task *);
+ void tabChange(int idx);
+ void runjob();
+ /* close the window properly */
+ void close_cb(task *t);
+ void close_cb();
+};
+
+/* Object that can scan a directory to find jobs */
+class TSched: public QObject
+{
+ Q_OBJECT
+private:
+ char *command_dir;
+ bool read_command_file(const char *file, alist *lst, btime_t mtime);
+ int timer;
+
+public:
+ TSched();
+ ~TSched();
+ void init(const char *cmd_dir);
+ bool scan_for_commands(alist *lst);
+ void start() {
+ timer = startTimer(60000); // 1-minute timer
+ };
+ void stop() {
+ if (timer >= 0) {
+ killTimer(timer);
+ timer = -1;
+ }
+ };
+public slots:
+ void jobStarted(task *t);
+protected:
+ void timerEvent(QTimerEvent *event);
+
+};
+
+
+/* Job found in the command directory */
+class TSchedJob: public QObject
+{
+ Q_OBJECT
+
+public:
+ char *component; // Name of the daemon
+ char *command; // job command
+ btime_t create_date; // When the command file was created
+ TSchedJob() : component(NULL), command(NULL) {};
+
+ TSchedJob(const char *comp, const char *cmd, btime_t cd) {
+ component = bstrdup(comp);
+ command = bstrdup(cmd);
+ create_date = cd;
+ };
+
+ ~TSchedJob() {
+ clear();
+ };
+ void clear() {
+ if (component) {
+ bfree_and_null(component);
+ }
+ if (command) {
+ bfree_and_null(command);
+ }
+ create_date = 0;
+ };
+};
+
+#endif
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>sdStatus</class>
+ <widget class="QWidget" name="sdStatus">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>518</width>
+ <height>435</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Storage Daemon Status</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="1">
+ <widget class="QLabel" name="labelName">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QLabel" name="labelPlugins">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Started:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="labelStarted">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="labelVersion">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Version:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Plugins:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Running Jobs</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QTableWidget" name="tableRunning">
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Terminated Jobs</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QTableWidget" name="tableTerminated">
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="statusBar">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../main.qrc">
+ <normaloff>:/images/view-refresh.png</normaloff>:/images/view-refresh.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../main.qrc"/>
+ </resources>
+ <connections/>
+</ui>
--- /dev/null
+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#include "sdstatus.h"
+#include "../util/fmtwidgetitem.h"
+#include "jcr.h"
+
+void SDStatus::doUpdate()
+{
+ if (count == 0) {
+ count++;
+ task *t = new task();
+ status.pushButton->setEnabled(false);
+ connect(t, SIGNAL(done(task *)), this, SLOT(taskDone(task *)), Qt::QueuedConnection);
+ t->init(res, TASK_STATUS);
+ res->wrk->queue(t);
+ status.statusBar->setText(QString("Trying to connect to Storage..."));
+ Dmsg1(50, "doUpdate(%p)\n", res);
+ }
+}
+
+void SDStatus::taskDone(task *t)
+{
+ count--;
+ if (!t->status) {
+ status.statusBar->setText(QString(t->errmsg));
+
+ } else {
+ status.statusBar->clear();
+ if (t->type == TASK_STATUS) {
+ char ed1[50];
+ struct s_last_job *ljob;
+ struct s_running_job *rjob;
+ res->mutex->lock();
+ status.labelName->setText(QString(res->name));
+ status.labelVersion->setText(QString(res->version));
+ status.labelStarted->setText(QString(res->started));
+ status.labelPlugins->setText(QString(res->plugins));
+ /* Clear the table first */
+ Freeze(*status.tableRunning);
+ Freeze(*status.tableTerminated);
+ QStringList headerlistR = (QStringList() << tr("JobId")
+ << tr("Job") << tr("Level") << tr("Client")
+ << tr("Storage")
+ << tr("Files") << tr("Bytes") << tr("Errors"));
+ status.tableRunning->clear();
+ status.tableRunning->setRowCount(0);
+ status.tableRunning->setColumnCount(headerlistR.count());
+ status.tableRunning->setHorizontalHeaderLabels(headerlistR);
+ status.tableRunning->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ status.tableRunning->verticalHeader()->hide();
+ status.tableRunning->setSortingEnabled(true);
+
+ if (res->running_jobs) {
+ status.tableRunning->setRowCount(res->running_jobs->size());
+ int row=0;
+ foreach_alist(rjob, res->running_jobs) {
+ int col=0;
+ TableItemFormatter item(*status.tableRunning, row++);
+ item.setNumericFld(col++, QString(edit_uint64(rjob->JobId, ed1)));
+ item.setTextFld(col++, QString(rjob->Job));
+ item.setJobLevelFld(col++, QString(rjob->JobLevel));
+ item.setTextFld(col++, QString(rjob->Client));
+ item.setTextFld(col++, QString(rjob->Storage));
+ item.setNumericFld(col++, QString(edit_uint64(rjob->JobFiles, ed1)));
+ item.setBytesFld(col++, QString(edit_uint64(rjob->JobBytes, ed1)));
+ item.setNumericFld(col++, QString(edit_uint64(rjob->Errors, ed1)));
+ }
+ } else {
+ Dmsg0(0, "Strange, the list is NULL\n");
+ }
+
+ QStringList headerlistT = (QStringList() << tr("JobId")
+ << tr("Job") << tr("Level")
+ << tr("Status") << tr("Files") << tr("Bytes")
+ << tr("Errors"));
+
+ status.tableTerminated->clear();
+ status.tableTerminated->setRowCount(0);
+ status.tableTerminated->setColumnCount(headerlistT.count());
+ status.tableTerminated->setHorizontalHeaderLabels(headerlistT);
+ status.tableTerminated->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ status.tableTerminated->verticalHeader()->hide();
+ status.tableTerminated->setSortingEnabled(true);
+
+ if (res->terminated_jobs) {
+ status.tableTerminated->setRowCount(res->terminated_jobs->size());
+ int row=0;
+ foreach_dlist(ljob, res->terminated_jobs) {
+ int col=0;
+ TableItemFormatter item(*status.tableTerminated, row++);
+ item.setNumericFld(col++, QString(edit_uint64(ljob->JobId, ed1)));
+ item.setTextFld(col++, QString(ljob->Job));
+ item.setJobLevelFld(col++, QString(ljob->JobLevel));
+ item.setJobStatusFld(col++, QString(ljob->JobStatus));
+ item.setNumericFld(col++, QString(edit_uint64(ljob->JobFiles, ed1)));
+ item.setBytesFld(col++, QString(edit_uint64(ljob->JobBytes, ed1)));
+ item.setNumericFld(col++, QString(edit_uint64(ljob->Errors, ed1)));
+ }
+ } else {
+ Dmsg0(0, "Strange, the list is NULL\n");
+ }
+ res->mutex->unlock();
+ }
+ Dmsg1(50, " Task %p OK\n", t);
+ }
+ t->deleteLater();
+ status.pushButton->setEnabled(true);
+}
--- /dev/null
+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#include "common.h"
+#include "ui_sd-monitor.h"
+#include "task.h"
+#include "status.h"
+
+class SDStatus: public ResStatus
+{
+ Q_OBJECT
+
+public:
+ Ui::sdStatus status;
+
+ SDStatus(RESMON *d): ResStatus(d)
+ {
+ status.setupUi(this);
+ QObject::connect(status.pushButton, SIGNAL(clicked()), this, SLOT(doUpdate()), Qt::QueuedConnection);
+ };
+ ~SDStatus() {
+ };
+public slots:
+ void doUpdate();
+ void taskDone(task *);
+};
--- /dev/null
+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#include "status.h"
+#include "lib/worker.h"
+
+void ResStatus::doUpdate()
+{
+ if (count == 0) {
+ task *t = new task();
+ connect(t, SIGNAL(done(task *)), this, SLOT(taskDone(task *)), Qt::QueuedConnection);
+ t->init(res, TASK_STATUS);
+ res->wrk->queue(t);
+ Dmsg0(0, "doUpdate()\n");
+ count++;
+ }
+}
+
+void ResStatus::taskDone(task *t)
+{
+ if (!t->status) {
+ Dmsg2(0, " Task %p failed => %s\n", t, t->errmsg);
+ }
+ delete t;
+ count--;
+}
--- /dev/null
+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#ifndef STATUS_H
+#define STATUS_H
+
+#include "common.h"
+#include <QWidget>
+#include "tray_conf.h"
+#include "task.h"
+
+class ResStatus: public QWidget
+{
+ Q_OBJECT
+
+public:
+ int count;
+ RESMON *res;
+ ResStatus(RESMON *c): count(0), res(c) {
+ };
+ virtual ~ResStatus() {
+ };
+public slots:
+ virtual void doUpdate();
+ virtual void taskDone(task *t);
+};
+
+#endif
--- /dev/null
+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#include "task.h"
+#include "jcr.h"
+#define dbglvl 10
+int authenticate_daemon(JCR *jcr, MONITOR *monitor, RESMON *res);
+
+static void *handle_task(void *data)
+{
+ task *t;
+ worker *wrk = (worker *)data;
+ lmgr_init_thread();
+
+ wrk->set_running();
+ Dmsg0(dbglvl, "Worker started\n");
+
+ while (!wrk->is_quit_state()) {
+ if (wrk->is_wait_state()) {
+ wrk->wait();
+ continue;
+ }
+ t = (task *)wrk->dequeue();
+ if (!t) {
+ continue;
+ }
+ /* Do the work */
+ switch(t->type) {
+ case TASK_STATUS:
+ t->do_status();
+ break;
+ case TASK_RESOURCES:
+ t->get_resources();
+ break;
+ case TASK_DEFAULTS:
+ t->get_job_defaults();
+ break;
+ case TASK_RUN:
+ t->run_job();
+ break;
+ case TASK_BWLIMIT:
+ t->set_bandwidth();
+ break;
+ case TASK_INFO:
+ t->get_job_info(t->arg2);
+ break;
+ case TASK_DISCONNECT:
+ t->disconnect_bacula();
+ t->mark_as_done();
+ break;
+ default:
+ Mmsg(t->errmsg, "Unknown task");
+ t->mark_as_failed();
+ break;
+ }
+ }
+ Dmsg0(dbglvl, "Worker stoped\n");
+ lmgr_cleanup_thread();
+ return NULL;
+}
+
+bool task::set_bandwidth()
+{
+ bool ret = false;
+ btimer_t *tid = NULL;
+ if (res->type != R_CLIENT) {
+ mark_as_failed();
+ Mmsg(errmsg, _("Bandwidth can set only set on Client"));
+ return false;
+ }
+ if (!arg || !*arg) {
+ mark_as_failed();
+ Mmsg(errmsg, _("Bandwidth parameter is invalid"));
+ return false;
+ }
+
+ if (res->proxy_sent) {
+ free_bsock(res->bs);
+ }
+
+ if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+ if (!connect_bacula()) {
+ mark_as_failed();
+ return false;
+ }
+ }
+
+ tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+ res->bs->fsend("setbandwidth limit=%s\n", NPRTB(arg));
+ while (get_next_line(res)) {
+ Dmsg1(dbglvl, "-> %s\n", curline);
+ }
+
+ if (tid) {
+ stop_thread_timer(tid);
+ }
+
+ /* Do not reuse the same socket */
+ disconnect_bacula();
+
+ if (ret) {
+ mark_as_done();
+ } else {
+ mark_as_failed();
+ }
+ return ret;
+}
+
+RESMON *task::get_res()
+{
+ return res;
+}
+
+void task::lock_res()
+{
+ res->mutex->lock();
+}
+
+void task::unlock_res()
+{
+ res->mutex->unlock();
+}
+
+bool task::disconnect_bacula()
+{
+ free_bsock(res->bs);
+ return true;
+}
+
+bool task::connect_bacula()
+{
+ JCR jcr;
+ bool ret = false;
+ memset(&jcr, 0, sizeof(jcr));
+ curend = curline = NULL;
+
+ RESMON *r = get_res();
+ MONITOR *monitor = (MONITOR*)GetNextRes(R_MONITOR, NULL);
+
+ if (r->type == R_CLIENT) {
+ r->proxy_sent = false;
+ if (r->bs && (r->bs->is_error() || !r->bs->is_open())) {
+ free_bsock(r->bs);
+ }
+ if (!r->bs) {
+ r->bs = new_bsock();
+ Dmsg0(dbglvl, "Trying to connect to FD\n");
+ if (r->bs->connect(NULL, r->connect_timeout, 0, 0, _("Client daemon"),
+ r->address, NULL, r->port, 0))
+ {
+ Dmsg0(dbglvl, "Connect done!\n");
+ jcr.file_bsock = r->bs;
+ if (!authenticate_daemon(&jcr, monitor, r)) {
+ Dmsg0(dbglvl, "Unable to authenticate\n");
+ Mmsg(errmsg, "Unable to authenticate with the FileDaemon");
+ free_bsock(r->bs);
+ return false;
+ }
+ Dmsg0(dbglvl, "Authenticate OK\n");
+ ret = true;
+ } else {
+ Mmsg(errmsg, "Unable to connect to the FileDaemon");
+ Dmsg0(dbglvl, "Connect error!\n");
+ }
+ } else {
+ ret = true;
+ }
+ }
+ if (r->type == R_STORAGE) {
+ if (r->bs && (r->bs->is_error() || !r->bs->is_open())) {
+ free_bsock(r->bs);
+ }
+ if (!r->bs) {
+ r->bs = new_bsock();
+ Dmsg0(dbglvl, "Trying to connect to FD\n");
+ if (r->bs->connect(NULL, r->connect_timeout, 0, 0, _("Storage daemon"),
+ r->address, NULL, r->port, 0))
+ {
+ Dmsg0(dbglvl, "Connect done!\n");
+ jcr.store_bsock = r->bs;
+ if (!authenticate_daemon(&jcr, monitor, r)) {
+ Dmsg0(dbglvl, "Unable to authenticate\n");
+ Mmsg(errmsg, "Unable to authenticate with the Storage Daemon");
+ free_bsock(r->bs);
+ return false;
+ }
+ Dmsg0(dbglvl, "Authenticate OK\n");
+ ret = true;
+ } else {
+ Mmsg(errmsg, "Unable to connect to the Storage Daemon");
+ Dmsg0(dbglvl, "Connect error!\n");
+ }
+ } else {
+ ret = true;
+ }
+ }
+ if (r->type == R_DIRECTOR) {
+ if (r->bs && (r->bs->is_error() || !r->bs->is_open())) {
+ free_bsock(r->bs);
+ }
+ if (!r->bs) {
+ r->bs = new_bsock();
+ Dmsg0(dbglvl, "Trying to connect to DIR\n");
+ if (r->bs->connect(NULL, r->connect_timeout, 0, 0, _("Director daemon"),
+ r->address, NULL, r->port, 0))
+ {
+ Dmsg0(dbglvl, "Connect done!\n");
+ jcr.dir_bsock = r->bs;
+ if (!authenticate_daemon(&jcr, monitor, r)) {
+ Dmsg0(dbglvl, "Unable to authenticate\n");
+ Mmsg(errmsg, "Unable to authenticate with the Director");
+ free_bsock(r->bs);
+ return false;
+ }
+ Dmsg0(dbglvl, "Authenticate OK\n");
+ ret = true;
+ } else {
+ Mmsg(errmsg, "Unable to connect to the Director");
+ Dmsg0(dbglvl, "Connect error!\n");
+ }
+ } else {
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+bool task::read_status_running(RESMON *r)
+{
+ bool ret = false;
+ char *start, *end;
+ struct s_running_job *item = NULL;
+ alist *running_jobs = New(alist(10, owned_by_alist));
+
+ while (r->bs->recv() >= -1) {
+ if (r->bs->msglen < 0 &&
+ r->bs->msglen != BNET_CMD_BEGIN &&
+ r->bs->msglen != BNET_CMD_OK)
+ {
+ Dmsg1(dbglvl, "Got Signal %s\n", bnet_sig_to_ascii(r->bs->msglen));
+ break;
+ }
+ Dmsg2(dbglvl, "RECV -> %s:%d\n", r->bs->msg, r->bs->msglen);
+ start = r->bs->msg;
+
+ while ((end = strchr(start, '\n')) != NULL) {
+ *end = 0;
+ Dmsg1(dbglvl, "line=[%s]\n", start);
+ if (strncasecmp(start, "jobid=", 6) == 0) {
+ if (item) {
+ Dmsg1(dbglvl, "Append item %ld\n", item->JobId);
+ running_jobs->append(item);
+ }
+ item = (struct s_running_job *)malloc(sizeof(struct s_running_job));
+ memset(item, 0, sizeof(struct s_running_job));
+ item->JobId = str_to_uint64(start + 6);
+
+ } else if (!item) {
+ Dmsg0(dbglvl, "discard line\n");
+
+ } else if (strncasecmp(start, "level=", 6) == 0) {
+ item->JobLevel = start[6];
+
+ } else if (strncasecmp(start, "type=", 5) == 0) {
+ item->JobType = start[5];
+
+ } else if (strncasecmp(start, "status=", 7) == 0) {
+ item->JobStatus = start[7];
+
+ } else if (strncasecmp(start, "jobbytes=", 9) == 0) {
+ item->JobBytes = str_to_uint64(start + 9);
+
+ } else if (strncasecmp(start, "jobfiles=", 9) == 0) {
+ item->JobFiles = str_to_uint64(start + 9);
+
+ } else if (strncasecmp(start, "job=", 4) == 0) {
+ bstrncpy(item->Job, start + 4, sizeof(item->Job));
+
+ } else if (strncasecmp(start, "starttime_epoch=", 16) == 0) {
+ item->start_time = str_to_uint64(start + 16);
+
+ } else if (strncasecmp(start, "schedtime_epoch=", 16) == 0) {
+ item->sched_time = str_to_uint64(start + 16);
+
+ } else if (strncasecmp(start, "bytes/sec=", 10) == 0) {
+ item->bytespersec = str_to_uint64(start + 10);
+
+ } else if (strncasecmp(start, "avebytes_sec=", 13) == 0) {
+ item->bytespersec = str_to_uint64(start + 13);
+
+ } else if (strncasecmp(start, "errors=", 7) == 0) {
+ item->Errors = str_to_uint64(start + 7);
+
+ } else if (strncasecmp(start, "readbytes=", 10) == 0) {
+ item->ReadBytes = str_to_uint64(start + 10);
+
+ } else if (strncasecmp(start, "processing file=", 16) == 0) {
+ bstrncpy(item->CurrentFile, start + 16, sizeof(item->CurrentFile));
+
+ } else if (strncasecmp(start, "clientname=", 11) == 0) {
+ bstrncpy(item->Client, start + 11, sizeof(item->Client));
+
+ } else if (strncasecmp(start, "fileset=", 8) == 0) {
+ bstrncpy(item->FileSet, start + 8, sizeof(item->FileSet));
+
+ } else if (strncasecmp(start, "storage=", 8) == 0) {
+ bstrncpy(item->Storage, start + 8, sizeof(item->Storage));
+
+ } else if (strncasecmp(start, "rstorage=", 8) == 0) {
+ bstrncpy(item->RStorage, start + 8, sizeof(item->Storage));
+
+ } else if (strncasecmp(start, "sdtls=", 6) == 0) {
+ item->SDtls = str_to_uint64(start + 6);
+ }
+ start = end+1;
+ }
+ r->last_update = time(NULL);
+
+ if (r->bs->is_error()) {
+ Mmsg(errmsg, "Got error on the socket communication line");
+ goto bail_out;
+ }
+ }
+ if (item) {
+ Dmsg1(dbglvl, "Append item %ld\n", item->JobId);
+ running_jobs->append(item);
+ }
+ ret = true;
+
+bail_out:
+ r->mutex->lock();
+ if (r->running_jobs) {
+ delete r->running_jobs;
+ }
+ r->running_jobs = running_jobs;
+ r->mutex->unlock();
+
+ return ret;
+}
+
+bool task::read_status_terminated(RESMON *r)
+{
+ bool ret = false;
+ char *start, *end;
+ struct s_last_job *item = NULL;
+
+ r->mutex->lock();
+ if (r->terminated_jobs) {
+ delete r->terminated_jobs;
+ }
+ r->terminated_jobs = New(dlist(item, &item->link));
+ r->mutex->unlock();
+
+ while (r->bs->recv() >= -1) {
+ if (r->bs->msglen < 0 &&
+ r->bs->msglen != BNET_CMD_BEGIN &&
+ r->bs->msglen != BNET_CMD_OK)
+ {
+ Dmsg1(dbglvl, "Got Signal %s\n", bnet_sig_to_ascii(r->bs->msglen));
+ break;
+ }
+
+ Dmsg2(dbglvl, "RECV -> %s:%d\n", r->bs->msg, r->bs->msglen);
+ r->mutex->lock();
+ start = r->bs->msg;
+
+ while ((end = strchr(start, '\n')) != NULL) {
+ *end = 0;
+ Dmsg1(dbglvl, "line=[%s]\n", start);
+ if (strncasecmp(start, "jobid=", 6) == 0) {
+ if (item) {
+ Dmsg1(dbglvl, "Append item %ld\n", item->JobId);
+ r->terminated_jobs->append(item);
+ }
+ item = (struct s_last_job *)malloc(sizeof(struct s_last_job));
+ memset(item, 0, sizeof(struct s_last_job));
+ item->JobId = str_to_uint64(start + 6);
+
+ } else if (!item) {
+ Dmsg0(dbglvl, "discard line\n");
+
+ } else if (strncasecmp(start, "level=", 6) == 0) {
+ item->JobLevel = start[6];
+
+ } else if (strncasecmp(start, "type=", 5) == 0) {
+ item->JobType = start[5];
+
+ } else if (strncasecmp(start, "status=", 7) == 0) {
+ item->JobStatus = start[7];
+
+ } else if (strncasecmp(start, "jobbytes=", 9) == 0) {
+ item->JobBytes = str_to_uint64(start + 9);
+
+ } else if (strncasecmp(start, "jobfiles=", 9) == 0) {
+ item->JobFiles = str_to_uint64(start + 9);
+
+ } else if (strncasecmp(start, "job=", 4) == 0) {
+ bstrncpy(item->Job, start + 4, sizeof(item->Job));
+
+ } else if (strncasecmp(start, "starttime_epoch=", 16) == 0) {
+ item->start_time = str_to_uint64(start + 16);
+
+ } else if (strncasecmp(start, "endtime_epoch=", 14) == 0) {
+ item->end_time = str_to_uint64(start + 14);
+
+ } else if (strncasecmp(start, "errors=", 7) == 0) {
+ item->Errors = str_to_uint64(start + 7);
+ }
+ start = end+1;
+ }
+ r->last_update = time(NULL);
+ r->mutex->unlock();
+
+ if (r->bs->is_error()) {
+ Mmsg(errmsg, "Got error on the socket communication line");
+ goto bail_out;
+ }
+ }
+ if (item) {
+ r->mutex->lock();
+ Dmsg1(dbglvl, "Append item %ld\n", item->JobId);
+ r->terminated_jobs->append(item);
+ r->mutex->unlock();
+ }
+ ret = true;
+
+bail_out:
+ return ret;
+}
+
+bool task::read_status_header(RESMON *r)
+{
+ bool ret = false;
+ char *start, *end;
+
+ while (r->bs->recv() >= -1) {
+ if (r->bs->msglen < 0 &&
+ r->bs->msglen != BNET_CMD_BEGIN &&
+ r->bs->msglen != BNET_CMD_OK)
+ {
+ Dmsg1(dbglvl, "Got Signal %d\n", r->bs->msglen);
+ break;
+ }
+
+ Dmsg2(dbglvl, "RECV -> %s:%d\n", r->bs->msg, r->bs->msglen);
+ r->mutex->lock();
+ start = r->bs->msg;
+
+ while ((end = strchr(start, '\n')) != NULL) {
+ *end = 0;
+ Dmsg1(dbglvl, "line=[%s]\n", start);
+ if (strncasecmp(start, "name=", 5) == 0) {
+ bstrncpy(r->name, start + 5, sizeof(r->name));
+
+ } else if (strncasecmp(start, "version=", 8) == 0) {
+ bstrncpy(r->version, start + 8, sizeof(r->version));
+
+ } else if (strncasecmp(start, "plugins=", 8) == 0) {
+ bstrncpy(r->plugins, start + 8, sizeof(r->plugins));
+
+ } else if (strncasecmp(start, "bwlimit=", 8) == 0) {
+ r->bwlimit = str_to_uint64(start + 8);
+
+ } else if (strncasecmp(start, "started=", 8) == 0) {
+ bstrncpy(r->started, start + 8, sizeof(r->started));
+
+ } else if (strncasecmp(start, "reloaded=", 9) == 0) {
+ bstrncpy(r->reloaded, start + 9, sizeof(r->reloaded));
+ }
+ start = end+1;
+ }
+
+ if (r->bs->is_error()) {
+ r->mutex->unlock();
+ Mmsg(errmsg, "Got error on the socket communication line");
+ goto bail_out;
+
+ }
+ r->last_update = time(NULL);
+ r->mutex->unlock();
+ }
+ ret = true;
+bail_out:
+ return ret;
+}
+
+
+bool task::do_status()
+{
+ bool ret = false;
+ btimer_t *tid = NULL;
+
+ /* We don't want to use a proxy session */
+ if (res->type == R_CLIENT && res->proxy_sent) {
+ free_bsock(res->bs);
+ }
+ if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+ if (!connect_bacula()) {
+ goto bail_out;
+ }
+ }
+ /* TODO: */
+ tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+ if (res->type == R_CLIENT || res->type == R_STORAGE) {
+ Dmsg0(dbglvl, "Send status command header\n");
+ res->bs->fsend(".status header api=2\n");
+ // TODO: Update a local set of variables and commit everything when it's done
+ ret = read_status_header(res);
+
+ if (ret) {
+ res->bs->fsend(".status terminated api=2\n");
+ ret = read_status_terminated(res);
+ }
+ if (ret) {
+ res->bs->fsend(".status running api=2\n");
+ ret = read_status_running(res);
+ }
+ }
+ if (res->type == R_DIRECTOR) {
+ Dmsg0(dbglvl, "-> .api 2\n");
+ res->bs->fsend(".api 2\n");
+ while (get_next_line(res)) {
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ Dmsg0(dbglvl, "Send status command header\n");
+ res->bs->fsend(".status dir header\n");
+ // TODO: Update a local set of variables and commit everything when it's done
+ ret = read_status_header(res);
+
+ if (ret) {
+ Dmsg0(dbglvl, "Send status command terminated\n");
+ res->bs->fsend(".status dir terminated\n");
+ ret = read_status_terminated(res);
+ }
+ if (ret) {
+ Dmsg0(dbglvl, "Send status command running\n");
+ res->bs->fsend(".status dir running\n");
+ ret = read_status_running(res);
+ }
+ }
+bail_out:
+ if (tid) {
+ stop_thread_timer(tid);
+ }
+ /* Use a new socket the next time */
+ disconnect_bacula();
+ if (ret) {
+ mark_as_done();
+ } else {
+ mark_as_failed();
+ }
+ return ret;
+}
+
+bool task::get_next_line(RESMON *r)
+{
+ /* We are currently reading a line */
+ if (curline && curend && r->bs->msglen > 0 && curend < (r->bs->msg + r->bs->msglen - 1)) {
+ curline = curend + 1; /* skip \0 */
+ if ((curend = strchr(curline, '\n')) != NULL) {
+ *curend = '\0';
+ }
+ return true;
+ }
+ curline = curend = NULL;
+ do {
+ r->bs->recv();
+
+ if (r->bs->msglen < 0) {
+ Dmsg1(dbglvl, "<- %s\n", bnet_sig_to_ascii(r->bs->msglen));
+ switch(r->bs->msglen) {
+ case BNET_ERROR_MSG:
+ r->bs->recv();
+ strip_trailing_junk(r->bs->msg);
+ Dmsg1(0, "ERROR: %s\n", r->bs->msg);
+ break;
+ case BNET_MAIN_PROMPT: // stop
+ return false;
+ case BNET_CMD_OK:
+ case BNET_CMD_BEGIN:
+ case BNET_MSGS_PENDING:
+ break;
+ case BNET_TERMINATE:
+ return false;
+ default: // error or question?
+ return false;
+ }
+
+ } else if (r->bs->msglen == 0) { // strange
+ return false;
+
+ } else {
+ Dmsg1(10, "<- %s\n", r->bs->msg);
+ curline = r->bs->msg;
+ curend = strchr(curline, '\n');
+ if (curend) {
+ *curend = 0;
+ }
+ return true; // something to read
+ }
+ } while (!r->bs->is_error());
+ return false;
+}
+
+bool task::get_job_defaults()
+{
+ bool ret = false;
+ btimer_t *tid = NULL;
+ char *p;
+
+ if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+ if (!connect_bacula()) {
+ goto bail_out;
+ }
+ }
+
+ res->mutex->lock();
+ bfree_and_null(res->defaults.client);
+ bfree_and_null(res->defaults.pool);
+ bfree_and_null(res->defaults.storage);
+ bfree_and_null(res->defaults.level);
+ bfree_and_null(res->defaults.type);
+ bfree_and_null(res->defaults.fileset);
+ bfree_and_null(res->defaults.catalog);
+
+ tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+ if (res->type == R_CLIENT && !res->proxy_sent) {
+ res->proxy_sent = true;
+ res->bs->fsend("proxy\n");
+ while (get_next_line(res)) {
+ if (strncmp(curline, "2000", 4) != 0) {
+ pm_strcpy(errmsg, curline);
+ goto bail_out;
+ }
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ }
+
+ res->bs->fsend(".api 2\n");
+ while (get_next_line(res)) {
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ res->bs->fsend(".defaults job=\"%s\"\n", res->defaults.job);
+ while (get_next_line(res)) {
+ Dmsg1(dbglvl, "line = [%s]\n", curline);
+ if ((p = strchr(curline, '=')) == NULL) {
+ continue;
+ }
+ *p++ = 0;
+ if (strcasecmp(curline, "client") == 0) {
+ res->defaults.client = bstrdup(p);
+
+ } else if (strcasecmp(curline, "pool") == 0) {
+ res->defaults.pool = bstrdup(p);
+
+ } else if (strcasecmp(curline, "storage") == 0) {
+ res->defaults.storage = bstrdup(p);
+
+ } else if (strcasecmp(curline, "level") == 0) {
+ res->defaults.level = bstrdup(p);
+
+ } else if (strcasecmp(curline, "type") == 0) {
+ res->defaults.type = bstrdup(p);
+
+ } else if (strcasecmp(curline, "fileset") == 0) {
+ res->defaults.fileset = bstrdup(p);
+
+ } else if (strcasecmp(curline, "catalog") == 0) {
+ res->defaults.catalog = bstrdup(p);
+
+ } else if (strcasecmp(curline, "priority") == 0) {
+ res->defaults.priority = str_to_uint64(p);
+ }
+ }
+ ret = true;
+bail_out:
+ if (tid) {
+ stop_thread_timer(tid);
+ }
+ if (ret) {
+ mark_as_done();
+ } else {
+ mark_as_failed();
+ }
+ res->mutex->unlock();
+ return ret;
+}
+
+bool task::get_job_info(const char *level)
+{
+ bool ret = false;
+ btimer_t *tid = NULL;
+ char *p;
+
+ if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+ if (!connect_bacula()) {
+ goto bail_out;
+ }
+ }
+ res->mutex->lock();
+ memset(&res->infos, 0, sizeof(res->infos));
+
+ tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+ if (res->type == R_CLIENT && !res->proxy_sent) {
+ res->proxy_sent = true;
+ res->bs->fsend("proxy\n");
+ while (get_next_line(res)) {
+ if (strncmp(curline, "2000", 4) != 0) {
+ pm_strcpy(errmsg, curline);
+ goto bail_out;
+ }
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ }
+
+ res->bs->fsend(".api 2\n");
+ while (get_next_line(res)) {
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ if (level) {
+ res->bs->fsend(".estimate job=\"%s\" level=%s\n", arg, level);
+ } else {
+ res->bs->fsend(".estimate job=\"%s\"\n", arg);
+ }
+ while (get_next_line(res)) {
+ Dmsg1(dbglvl, "line = [%s]\n", curline);
+ if ((p = strchr(curline, '=')) == NULL) {
+ continue;
+ }
+ *p++ = 0;
+ if (strcasecmp(curline, "level") == 0) {
+ res->infos.JobLevel = p[0];
+
+ } else if (strcasecmp(curline, "jobbytes") == 0) {
+ res->infos.JobBytes = str_to_uint64(p);
+
+ } else if (strcasecmp(curline, "jobfiles") == 0) {
+ res->infos.JobFiles = str_to_uint64(p);
+
+ } else if (strcasecmp(curline, "corrbytes") == 0) {
+ res->infos.CorrJobBytes = str_to_uint64(p);
+
+ } else if (strcasecmp(curline, "corrfiles") == 0) {
+ res->infos.CorrJobFiles = str_to_uint64(p);
+
+ } else if (strcasecmp(curline, "nbjob") == 0) {
+ res->infos.CorrNbJob = str_to_uint64(p);
+ }
+ }
+ ret = true;
+bail_out:
+ res->mutex->unlock();
+ if (tid) {
+ stop_thread_timer(tid);
+ }
+ if (ret) {
+ mark_as_done();
+ } else {
+ mark_as_failed();
+ }
+ return ret;
+}
+
+bool task::run_job()
+{
+ bool ret = false;
+ char *p;
+ btimer_t *tid = NULL;
+
+ if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+ if (!connect_bacula()) {
+ goto bail_out;
+ }
+ }
+
+ tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+ if (res->type == R_CLIENT && !res->proxy_sent) {
+ res->proxy_sent = true;
+ res->bs->fsend("proxy\n");
+ while (get_next_line(res)) {
+ if (strncmp(curline, "2000", 4) != 0) {
+ pm_strcpy(errmsg, curline);
+ goto bail_out;
+ }
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ }
+
+ res->bs->fsend(".api 2\n");
+ while (get_next_line(res)) {
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ if (res->type == R_DIRECTOR && res->use_setip) {
+ res->bs->fsend("setip\n");
+ while (get_next_line(res)) {
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ }
+ res->bs->fsend("%s\n", arg);
+ while (get_next_line(res)) {
+ if ((p = strstr(curline, "JobId=")) != NULL && sscanf(p, "JobId=%d\n", &result.i) == 1) {
+ ret = true;
+ }
+ }
+ // Close the socket, it's over or we don't want to reuse it
+ disconnect_bacula();
+
+bail_out:
+
+ if (tid) {
+ stop_thread_timer(tid);
+ }
+ if (ret) {
+ mark_as_done();
+ } else {
+ mark_as_failed();
+ }
+ return ret;
+}
+
+/* Get resources to run a job */
+bool task::get_resources()
+{
+ bool ret = false;
+ btimer_t *tid = NULL;
+
+ if (!res->bs || !res->bs->is_open() || res->bs->is_error()) {
+ if (!connect_bacula()) {
+ goto bail_out;
+ }
+ }
+
+ res->mutex->lock();
+ if (res->jobs) {
+ delete res->jobs;
+ }
+ res->jobs = New(alist(10, owned_by_alist));
+ if (res->clients) {
+ delete res->clients;
+ }
+ res->clients = New(alist(10, owned_by_alist));
+ if (res->filesets) {
+ delete res->filesets;
+ }
+ res->filesets = New(alist(10, owned_by_alist));
+ if (res->pools) {
+ delete res->pools;
+ }
+ res->pools = New(alist(10, owned_by_alist));
+ if (res->storages) {
+ delete res->storages;
+ }
+ res->storages = New(alist(10, owned_by_alist));
+ if (res->catalogs) {
+ delete res->catalogs;
+ }
+ res->catalogs = New(alist(10, owned_by_alist));
+
+ tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120);
+ if (res->type == R_CLIENT && !res->proxy_sent) {
+ res->proxy_sent = true;
+ res->bs->fsend("proxy\n");
+ while (get_next_line(res)) {
+ if (strncmp(curline, "2000", 4) != 0) {
+ pm_strcpy(errmsg, curline);
+ goto bail_out;
+ }
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+
+ res->bs->fsend(".api 2\n");
+ while (get_next_line(res)) {
+ Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline);
+ }
+
+ res->bs->fsend(".jobs type=B\n");
+ while (get_next_line(res)) {
+ res->jobs->append(bstrdup(curline));
+ }
+
+ res->bs->fsend(".pools\n");
+ while (get_next_line(res)) {
+ res->pools->append(bstrdup(curline));
+ }
+
+ res->bs->fsend(".clients\n");
+ while (get_next_line(res)) {
+ res->clients->append(bstrdup(curline));
+ }
+
+ res->bs->fsend(".filesets\n");
+ while (get_next_line(res)) {
+ res->filesets->append(bstrdup(curline));
+ }
+
+ res->bs->fsend(".storage\n");
+ while (get_next_line(res)) {
+ res->storages->append(bstrdup(curline));
+ }
+
+ res->bs->fsend(".catalog\n");
+ while (get_next_line(res)) {
+ res->catalogs->append(bstrdup(curline));
+ }
+
+bail_out:
+ res->mutex->unlock();
+
+ if (tid) {
+ stop_thread_timer(tid);
+ }
+ if (ret) {
+ mark_as_done();
+ } else {
+ mark_as_failed();
+ }
+ return ret;
+}
+
+worker *worker_start()
+{
+ worker *w = New(worker());
+ w->start(handle_task, w);
+ return w;
+}
+
+void worker_stop(worker *w)
+{
+ if (w) {
+ w->stop();
+ delete w;
+ }
+}
#
# Bacula Tray Monitor Configuration File
#
+# Copyright (C) 2000-2017 Kern Sibbald
+# License: BSD 2-Clause; see file LICENSE-FOSS
+#
Monitor {
Name = @basename@-mon
#
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#
+# Copyright (C) 2000-2017 Kern Sibbald
+# License: BSD 2-Clause; see file LICENSE-FOSS
+#
# CONFIG options for Windows are pulled from win32/qmake.conf
CONFIG += qt
#CONFIG += qt debug
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#
# CONFIG options for Windows are pulled from win32/qmake.conf
-CONFIG += qt
+#
+# Copyright (C) 2000-2017 Kern Sibbald
+# License: BSD 2-Clause; see file LICENSE-FOSS
+#
+
+CONFIG += qt cross-win32
#CONFIG += qt debug
cross-win32 {
- LIBS += -mwindows -L../../win32/release32 -lbacula
+ LIBS += -mwindows -L../../win32/release32 -lbacula -lpthread
INCLUDEPATH += ../../win32/compat
}
!cross-win32 {
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#
# CONFIG options for Windows are pulled from win32/qmake.conf
-CONFIG += qt
+#
+# Copyright (C) 2000-2017 Kern Sibbald
+# License: BSD 2-Clause; see file LICENSE-FOSS
+#
+
+CONFIG += qt cross-win32
#CONFIG += qt debug
cross-win32 {
- LIBS += -mwindows -L../../win32/release64 -lbacula
+ LIBS += -mwindows -L../../win32/release64 -lbacula -lpthread
INCLUDEPATH += ../../win32/compat
}
!cross-win32 {