2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula File Daemon Status routines
22 * Kern Sibbald, August MMI
27 #include "lib/status.h"
29 extern void *start_heap;
31 extern bool GetWindowsVersionString(char *buf, int maxsiz);
34 /* Forward referenced functions */
35 static void list_running_jobs(STATUS_PKT *sp);
36 static void list_status_header(STATUS_PKT *sp);
38 /* Static variables */
39 static char qstatus1[] = ".status %127s\n";
40 static char qstatus2[] = ".status %127s api=%d api_opts=%127s";
42 static char OKqstatus[] = "2000 OK .status\n";
43 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
46 * General status generator
48 void output_status(STATUS_PKT *sp)
50 list_status_header(sp);
51 list_running_jobs(sp);
52 list_terminated_jobs(sp); /* defined in lib/status.h */
55 static void api_list_status_header(STATUS_PKT *sp)
59 OutputWriter wt(sp->api_opts);
62 wt.start_group("header");
64 OT_STRING, "name", my_name,
65 OT_STRING, "version", VERSION " (" BDATE ")",
66 OT_STRING, "uname", HOST_OS " " DISTNAME " " DISTVER,
67 OT_UTIME, "started", daemon_start_time,
68 OT_INT, "jobs_run", num_jobs_run,
69 OT_INT, "jobs_running",job_count(),
70 OT_STRING, "winver", buf,
71 OT_INT64, "debug", debug_level,
72 OT_INT, "trace", get_trace(),
73 OT_INT64, "bwlimit", me->max_bandwidth_per_job,
74 OT_PLUGINS, "plugins", b_plugin_list,
77 sendit(p, strlen(p), sp);
80 static void list_status_header(STATUS_PKT *sp)
82 POOL_MEM msg(PM_MESSAGE);
83 char b1[32], b2[32], b3[32], b4[32], b5[35];
84 int64_t memused = (char *)sbrk(0)-(char *)start_heap;
86 char dt[MAX_TIME_LENGTH];
89 api_list_status_header(sp);
93 len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
94 my_name, "", VERSION, BDATE, HOST_OS,
96 sendit(msg.c_str(), len, sp);
97 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
98 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d running=%d.\n"),
99 dt, num_jobs_run, job_count());
100 sendit(msg.c_str(), len, sp);
101 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
102 edit_uint64_with_commas(memused, b1),
103 edit_uint64_with_commas(sm_bytes, b2),
104 edit_uint64_with_commas(sm_max_bytes, b3),
105 edit_uint64_with_commas(sm_buffers, b4),
106 edit_uint64_with_commas(sm_max_buffers, b5));
107 sendit(msg.c_str(), len, sp);
108 len = Mmsg(msg, _(" Sizes: boffset_t=%d size_t=%d debug=%s trace=%d "
109 "mode=%d,%d bwlimit=%skB/s\n"),
110 sizeof(boffset_t), sizeof(size_t),
111 edit_uint64(debug_level, b2), get_trace(), (int)DEVELOPER_MODE, 0,
112 edit_uint64_with_commas(me->max_bandwidth_per_job/1024, b1));
113 sendit(msg.c_str(), len, sp);
114 if (b_plugin_list && b_plugin_list->size() > 0) {
117 pm_strcpy(msg, " Plugin: ");
118 foreach_alist(plugin, b_plugin_list) {
119 len = pm_strcat(msg, plugin->file);
120 /* Print plugin version when debug activated */
121 if (debug_level > 0 && plugin->pinfo) {
122 pInfo *info = (pInfo *)plugin->pinfo;
124 pm_strcat(msg, NPRT(info->plugin_version));
125 len = pm_strcat(msg, ")");
128 pm_strcat(msg, "\n ");
133 len = pm_strcat(msg, "\n");
134 sendit(msg.c_str(), len, sp);
139 * List running jobs in for humans.
141 static void list_running_jobs_plain(STATUS_PKT *sp)
143 int total_sec, inst_sec;
144 uint64_t total_bps, inst_bps;
145 POOL_MEM msg(PM_MESSAGE);
146 char b1[50], b2[50], b3[50], b4[50], b5[50], b6[50];
150 time_t now = time(NULL);
151 char dt[MAX_TIME_LENGTH];
153 Dmsg0(1000, "Begin status jcr loop.\n");
154 len = Mmsg(msg, _("\nRunning Jobs:\n"));
155 sendit(msg.c_str(), len, sp);
157 bstrftime_nc(dt, sizeof(dt), njcr->start_time);
158 if (njcr->JobId == 0) {
159 len = Mmsg(msg, _("Director connected %sat: %s\n"),
160 (njcr->dir_bsock && njcr->dir_bsock->tls)?_("using TLS "):"",
163 len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
164 njcr->JobId, njcr->Job);
165 sendit(msg.c_str(), len, sp);
166 len = Mmsg(msg, _(" %s %s Job started: %s\n"),
167 job_level_to_str(njcr->getJobLevel()),
168 job_type_to_str(njcr->getJobType()), dt);
170 sendit(msg.c_str(), len, sp);
171 if (njcr->JobId == 0) {
174 if (njcr->last_time == 0) {
175 njcr->last_time = njcr->start_time;
177 total_sec = now - njcr->start_time;
178 inst_sec = now - njcr->last_time;
179 if (total_sec <= 0) {
185 /* Instanteous bps not smoothed */
186 inst_bps = (njcr->JobBytes - njcr->LastJobBytes) / inst_sec;
187 if (njcr->LastRate <= 0) {
188 njcr->LastRate = inst_bps;
190 /* Smooth the instantaneous bps a bit */
191 inst_bps = (2 * njcr->LastRate + inst_bps) / 3;
192 /* total bps (AveBytes/sec) since start of job */
193 total_bps = njcr->JobBytes / total_sec;
194 len = Mmsg(msg, _(" Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s Errors=%d\n"
195 " Bwlimit=%s ReadBytes=%s\n"),
196 edit_uint64_with_commas(njcr->JobFiles, b1),
197 edit_uint64_with_commas(njcr->JobBytes, b2),
198 edit_uint64_with_commas(total_bps, b3),
199 edit_uint64_with_commas(inst_bps, b4),
200 njcr->JobErrors, edit_uint64_with_commas(njcr->max_bandwidth, b5),
201 edit_uint64_with_commas(njcr->ReadBytes, b6));
202 sendit(msg.c_str(), len, sp);
204 if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
205 len = Mmsg(msg, _(" Files: Restored=%s Expected=%s Completed=%d%%\n"),
206 edit_uint64_with_commas(njcr->num_files_examined, b1),
207 edit_uint64_with_commas(njcr->ExpectedFiles, b2),
208 (100*njcr->num_files_examined)/njcr->ExpectedFiles);
210 len = Mmsg(msg, _(" Files: Examined=%s Backed up=%s\n"),
211 edit_uint64_with_commas(njcr->num_files_examined, b1),
212 edit_uint64_with_commas(njcr->JobFiles, b2));
214 /* Update only every 10 seconds */
215 if (now - njcr->last_time > 10) {
216 njcr->LastRate = inst_bps;
217 njcr->LastJobBytes = njcr->JobBytes;
218 njcr->last_time = now;
220 sendit(msg.c_str(), len, sp);
221 if (njcr->JobFiles > 0) {
223 len = Mmsg(msg, _(" Processing file: %s\n"), njcr->last_fname);
225 sendit(msg.c_str(), len, sp);
229 if (njcr->store_bsock) {
230 len = Mmsg(msg, " SDReadSeqNo=%" lld " fd=%d SDtls=%d\n",
231 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd,
232 (njcr->store_bsock->tls)?1:0);
233 sendit(msg.c_str(), len, sp);
235 len = Mmsg(msg, _(" SDSocket closed.\n"));
236 sendit(msg.c_str(), len, sp);
242 len = Mmsg(msg, _("No Jobs running.\n"));
243 sendit(msg.c_str(), len, sp);
245 sendit(_("====\n"), 5, sp);
249 * List running jobs for Bat or Bweb in a format
250 * simpler to parse. Be careful when changing this
253 static void list_running_jobs_api(STATUS_PKT *sp)
255 OutputWriter ow(sp->api_opts);
260 /* API v1, edit with comma, space before the name, sometime ' ' as separator */
263 p = ow.get_output(OT_CLEAR, OT_START_OBJ, OT_END);
265 if (njcr->JobId == 0) {
266 ow.get_output(OT_UTIME, "DirectorConnected", njcr->start_time,
267 OT_INT, "DirTLS", (njcr->dir_bsock && njcr->dir_bsock->tls)?1:0,
270 ow.get_output(OT_INT32, "JobId", njcr->JobId,
271 OT_STRING, "Job", njcr->Job,
272 OT_JOBLEVEL,"Level", njcr->getJobLevel(),
273 OT_JOBTYPE, "Type", njcr->getJobType(),
274 OT_JOBSTATUS, "Status", njcr->getJobStatus(),
275 OT_UTIME, "StartTime", njcr->start_time,
279 sendit(p, strlen(p), sp);
280 if (njcr->JobId == 0) {
283 sec = time(NULL) - njcr->start_time;
287 bps = (int)(njcr->JobBytes / sec);
288 ow.get_output(OT_CLEAR,
289 OT_INT32, "JobFiles", njcr->JobFiles,
290 OT_SIZE, "JobBytes", njcr->JobBytes,
291 OT_INT, "Bytes/sec", bps,
292 OT_INT, "Errors", njcr->JobErrors,
293 OT_INT64, "Bwlimit", njcr->max_bandwidth,
294 OT_SIZE, "ReadBytes", njcr->ReadBytes,
297 ow.get_output(OT_INT32, "Files Examined", njcr->num_files_examined, OT_END);
299 if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
300 ow.get_output(OT_INT32, "Expected Files", njcr->ExpectedFiles,
301 OT_INT32, "Percent Complete", 100*(njcr->num_files_examined/njcr->ExpectedFiles),
305 sendit(p, strlen(p), sp);
306 ow.get_output(OT_CLEAR, OT_END);
308 if (njcr->JobFiles > 0) {
310 ow.get_output(OT_STRING, "Processing file", njcr->last_fname, OT_END);
314 if (njcr->store_bsock) {
315 ow.get_output(OT_INT64, "SDReadSeqNo", (int64_t)njcr->store_bsock->read_seqno,
316 OT_INT, "fd", njcr->store_bsock->m_fd,
317 OT_INT, "SDtls", (njcr->store_bsock->tls)?1:0,
320 ow.get_output(OT_STRING, "SDSocket", "closed", OT_END);
322 ow.get_output(OT_END_OBJ, OT_END);
323 sendit(p, strlen(p), sp);
328 static void list_running_jobs(STATUS_PKT *sp)
331 list_running_jobs_api(sp);
333 list_running_jobs_plain(sp);
338 * Status command from Director
340 int status_cmd(JCR *jcr)
342 BSOCK *user = jcr->dir_bsock;
347 sp.api = false; /* no API output */
350 user->signal(BNET_EOD);
355 * .status command from Director
357 int qstatus_cmd(JCR *jcr)
359 BSOCK *dir = jcr->dir_bsock;
366 cmd = get_memory(dir->msglen+1);
368 if (sscanf(dir->msg, qstatus2, cmd, &sp.api, sp.api_opts) != 3) {
369 if (sscanf(dir->msg, qstatus1, cmd) != 1) {
370 pm_strcpy(&jcr->errmsg, dir->msg);
371 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
372 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
373 dir->signal(BNET_EOD);
380 if (strcasecmp(cmd, "current") == 0) {
381 dir->fsend(OKqstatus, cmd);
383 if (njcr->JobId != 0) {
384 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
388 } else if (strcasecmp(cmd, "last") == 0) {
389 dir->fsend(OKqstatus, cmd);
390 if ((last_jobs) && (last_jobs->size() > 0)) {
391 job = (s_last_job*)last_jobs->last();
392 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
394 } else if (strcasecmp(cmd, "header") == 0) {
396 list_status_header(&sp);
397 } else if (strcasecmp(cmd, "running") == 0) {
399 list_running_jobs(&sp);
400 } else if (strcasecmp(cmd, "terminated") == 0) {
401 sp.api = MAX(sp.api, 1);
402 list_terminated_jobs(&sp); /* defined in lib/status.h */
404 pm_strcpy(&jcr->errmsg, dir->msg);
405 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
406 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
407 dir->signal(BNET_EOD);
412 dir->signal(BNET_EOD);