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 */
56 static const bool have_lzo = true;
58 static const bool have_lzo = false;
62 static void api_list_status_header(STATUS_PKT *sp)
66 OutputWriter wt(sp->api_opts);
69 wt.start_group("header");
71 OT_STRING, "name", my_name,
72 OT_STRING, "version", VERSION " (" BDATE ")",
73 OT_STRING, "uname", HOST_OS " " DISTNAME " " DISTVER,
74 OT_UTIME, "started", daemon_start_time,
75 OT_INT, "jobs_run", num_jobs_run,
76 OT_INT, "jobs_running",job_count(),
77 OT_STRING, "winver", buf,
78 OT_INT64, "debug", debug_level,
79 OT_INT, "trace", get_trace(),
80 OT_INT64, "bwlimit", me->max_bandwidth_per_job,
81 OT_PLUGINS, "plugins", b_plugin_list,
84 sendit(p, strlen(p), sp);
87 static void list_status_header(STATUS_PKT *sp)
89 POOL_MEM msg(PM_MESSAGE);
90 char b1[32], b2[32], b3[32], b4[32], b5[35];
91 int64_t memused = (char *)sbrk(0)-(char *)start_heap;
93 char dt[MAX_TIME_LENGTH];
96 api_list_status_header(sp);
100 len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s\n"),
101 my_name, "", VERSION, BDATE, HOST_OS,
103 sendit(msg.c_str(), len, sp);
104 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
105 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d running=%d.\n"),
106 dt, num_jobs_run, job_count());
107 sendit(msg.c_str(), len, sp);
108 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
109 edit_uint64_with_commas(memused, b1),
110 edit_uint64_with_commas(sm_bytes, b2),
111 edit_uint64_with_commas(sm_max_bytes, b3),
112 edit_uint64_with_commas(sm_buffers, b4),
113 edit_uint64_with_commas(sm_max_buffers, b5));
114 sendit(msg.c_str(), len, sp);
115 len = Mmsg(msg, _(" Sizes: boffset_t=%d size_t=%d debug=%s trace=%d "
116 "mode=%d,%d bwlimit=%skB/s\n"),
117 sizeof(boffset_t), sizeof(size_t),
118 edit_uint64(debug_level, b2), get_trace(), (int)DEVELOPER_MODE, 0,
119 edit_uint64_with_commas(me->max_bandwidth_per_job/1024, b1));
120 sendit(msg.c_str(), len, sp);
121 if (b_plugin_list && b_plugin_list->size() > 0) {
124 pm_strcpy(msg, " Plugin: ");
125 foreach_alist(plugin, b_plugin_list) {
126 len = pm_strcat(msg, plugin->file);
127 /* Print plugin version when debug activated */
128 if (debug_level > 0 && plugin->pinfo) {
129 pInfo *info = (pInfo *)plugin->pinfo;
131 pm_strcat(msg, NPRT(info->plugin_version));
132 len = pm_strcat(msg, ")");
135 pm_strcat(msg, "\n ");
140 len = pm_strcat(msg, "\n");
141 sendit(msg.c_str(), len, sp);
146 * List running jobs in for humans.
148 static void list_running_jobs_plain(STATUS_PKT *sp)
150 int total_sec, inst_sec;
151 uint64_t total_bps, inst_bps;
152 POOL_MEM msg(PM_MESSAGE);
153 char b1[50], b2[50], b3[50], b4[50], b5[50], b6[50];
157 time_t now = time(NULL);
158 char dt[MAX_TIME_LENGTH];
160 Dmsg0(1000, "Begin status jcr loop.\n");
161 len = Mmsg(msg, _("\nRunning Jobs:\n"));
162 sendit(msg.c_str(), len, sp);
164 bstrftime_nc(dt, sizeof(dt), njcr->start_time);
165 if (njcr->JobId == 0) {
166 len = Mmsg(msg, _("Director connected %sat: %s\n"),
167 (njcr->dir_bsock && njcr->dir_bsock->tls)?_("using TLS "):"",
170 len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
171 njcr->JobId, njcr->Job);
172 sendit(msg.c_str(), len, sp);
173 len = Mmsg(msg, _(" %s %s Job started: %s\n"),
174 job_level_to_str(njcr->getJobLevel()),
175 job_type_to_str(njcr->getJobType()), dt);
177 sendit(msg.c_str(), len, sp);
178 if (njcr->JobId == 0) {
181 if (njcr->last_time == 0) {
182 njcr->last_time = njcr->start_time;
184 total_sec = now - njcr->start_time;
185 inst_sec = now - njcr->last_time;
186 if (total_sec <= 0) {
192 /* Instanteous bps not smoothed */
193 inst_bps = (njcr->JobBytes - njcr->LastJobBytes) / inst_sec;
194 if (njcr->LastRate <= 0) {
195 njcr->LastRate = inst_bps;
197 /* Smooth the instantaneous bps a bit */
198 inst_bps = (2 * njcr->LastRate + inst_bps) / 3;
199 /* total bps (AveBytes/sec) since start of job */
200 total_bps = njcr->JobBytes / total_sec;
201 len = Mmsg(msg, _(" Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s Errors=%d\n"
202 " Bwlimit=%s ReadBytes=%s\n"),
203 edit_uint64_with_commas(njcr->JobFiles, b1),
204 edit_uint64_with_commas(njcr->JobBytes, b2),
205 edit_uint64_with_commas(total_bps, b3),
206 edit_uint64_with_commas(inst_bps, b4),
207 njcr->JobErrors, edit_uint64_with_commas(njcr->max_bandwidth, b5),
208 edit_uint64_with_commas(njcr->ReadBytes, b6));
209 sendit(msg.c_str(), len, sp);
211 if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
212 len = Mmsg(msg, _(" Files: Restored=%s Expected=%s Completed=%d%%\n"),
213 edit_uint64_with_commas(njcr->num_files_examined, b1),
214 edit_uint64_with_commas(njcr->ExpectedFiles, b2),
215 (100*njcr->num_files_examined)/njcr->ExpectedFiles);
217 len = Mmsg(msg, _(" Files: Examined=%s Backed up=%s\n"),
218 edit_uint64_with_commas(njcr->num_files_examined, b1),
219 edit_uint64_with_commas(njcr->JobFiles, b2));
221 /* Update only every 10 seconds */
222 if (now - njcr->last_time > 10) {
223 njcr->LastRate = inst_bps;
224 njcr->LastJobBytes = njcr->JobBytes;
225 njcr->last_time = now;
227 sendit(msg.c_str(), len, sp);
228 if (njcr->JobFiles > 0) {
230 len = Mmsg(msg, _(" Processing file: %s\n"), njcr->last_fname);
232 sendit(msg.c_str(), len, sp);
236 if (njcr->store_bsock) {
237 len = Mmsg(msg, " SDReadSeqNo=%" lld " fd=%d SDtls=%d\n",
238 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd,
239 (njcr->store_bsock->tls)?1:0);
240 sendit(msg.c_str(), len, sp);
242 len = Mmsg(msg, _(" SDSocket closed.\n"));
243 sendit(msg.c_str(), len, sp);
249 len = Mmsg(msg, _("No Jobs running.\n"));
250 sendit(msg.c_str(), len, sp);
252 sendit(_("====\n"), 5, sp);
256 * List running jobs for Bat or Bweb in a format
257 * simpler to parse. Be careful when changing this
260 static void list_running_jobs_api(STATUS_PKT *sp)
262 OutputWriter ow(sp->api_opts);
267 /* API v1, edit with comma, space before the name, sometime ' ' as separator */
270 p = ow.get_output(OT_CLEAR, OT_START_OBJ, OT_END);
272 if (njcr->JobId == 0) {
273 ow.get_output(OT_UTIME, "DirectorConnected", njcr->start_time,
274 OT_INT, "DirTLS", (njcr->dir_bsock && njcr->dir_bsock->tls)?1:0,
277 ow.get_output(OT_INT32, "JobId", njcr->JobId,
278 OT_STRING, "Job", njcr->Job,
279 OT_JOBLEVEL,"Level", njcr->getJobLevel(),
280 OT_JOBTYPE, "Type", njcr->getJobType(),
281 OT_JOBSTATUS, "Status", njcr->getJobStatus(),
282 OT_UTIME, "StartTime", njcr->start_time,
286 sendit(p, strlen(p), sp);
287 if (njcr->JobId == 0) {
290 sec = time(NULL) - njcr->start_time;
294 bps = (int)(njcr->JobBytes / sec);
295 ow.get_output(OT_CLEAR,
296 OT_INT32, "JobFiles", njcr->JobFiles,
297 OT_SIZE, "JobBytes", njcr->JobBytes,
298 OT_INT, "Bytes/sec", bps,
299 OT_INT, "Errors", njcr->JobErrors,
300 OT_INT64, "Bwlimit", njcr->max_bandwidth,
301 OT_SIZE, "ReadBytes", njcr->ReadBytes,
304 ow.get_output(OT_INT32, "Files Examined", njcr->num_files_examined, OT_END);
306 if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
307 ow.get_output(OT_INT32, "Expected Files", njcr->ExpectedFiles,
308 OT_INT32, "Percent Complete", 100*(njcr->num_files_examined/njcr->ExpectedFiles),
312 sendit(p, strlen(p), sp);
313 ow.get_output(OT_CLEAR, OT_END);
315 if (njcr->JobFiles > 0) {
317 ow.get_output(OT_STRING, "Processing file", njcr->last_fname, OT_END);
321 if (njcr->store_bsock) {
322 ow.get_output(OT_INT64, "SDReadSeqNo", (int64_t)njcr->store_bsock->read_seqno,
323 OT_INT, "fd", njcr->store_bsock->m_fd,
324 OT_INT, "SDtls", (njcr->store_bsock->tls)?1:0,
327 ow.get_output(OT_STRING, "SDSocket", "closed", OT_END);
329 ow.get_output(OT_END_OBJ, OT_END);
330 sendit(p, strlen(p), sp);
335 static void list_running_jobs(STATUS_PKT *sp)
338 list_running_jobs_api(sp);
340 list_running_jobs_plain(sp);
345 * Status command from Director
347 int status_cmd(JCR *jcr)
349 BSOCK *user = jcr->dir_bsock;
354 sp.api = false; /* no API output */
357 user->signal(BNET_EOD);
362 * .status command from Director
364 int qstatus_cmd(JCR *jcr)
366 BSOCK *dir = jcr->dir_bsock;
373 cmd = get_memory(dir->msglen+1);
375 if (sscanf(dir->msg, qstatus2, cmd, &sp.api, sp.api_opts) != 3) {
376 if (sscanf(dir->msg, qstatus1, cmd) != 1) {
377 pm_strcpy(&jcr->errmsg, dir->msg);
378 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
379 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
380 dir->signal(BNET_EOD);
387 if (strcasecmp(cmd, "current") == 0) {
388 dir->fsend(OKqstatus, cmd);
390 if (njcr->JobId != 0) {
391 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
395 } else if (strcasecmp(cmd, "last") == 0) {
396 dir->fsend(OKqstatus, cmd);
397 if ((last_jobs) && (last_jobs->size() > 0)) {
398 job = (s_last_job*)last_jobs->last();
399 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
401 } else if (strcasecmp(cmd, "header") == 0) {
403 list_status_header(&sp);
404 } else if (strcasecmp(cmd, "running") == 0) {
406 list_running_jobs(&sp);
407 } else if (strcasecmp(cmd, "terminated") == 0) {
408 sp.api = MAX(sp.api, 1);
409 list_terminated_jobs(&sp); /* defined in lib/status.h */
411 pm_strcpy(&jcr->errmsg, dir->msg);
412 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
413 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
414 dir->signal(BNET_EOD);
419 dir->signal(BNET_EOD);