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 int val = (njcr->dir_bsock && njcr->dir_bsock->tls)?1:0;
267 ow.get_output(OT_UTIME, "DirectorConnected", njcr->start_time,
268 OT_INT, "DirTLS", val,
271 ow.get_output(OT_INT32, "JobId", njcr->JobId,
272 OT_STRING, "Job", njcr->Job,
273 OT_JOBLEVEL,"Level", njcr->getJobLevel(),
274 OT_JOBTYPE, "Type", njcr->getJobType(),
275 OT_JOBSTATUS, "Status", njcr->getJobStatus(),
276 OT_UTIME, "StartTime", njcr->start_time,
280 sendit(p, strlen(p), sp);
281 if (njcr->JobId == 0) {
284 sec = time(NULL) - njcr->start_time;
288 bps = (int)(njcr->JobBytes / sec);
289 ow.get_output(OT_CLEAR,
290 OT_INT32, "JobFiles", njcr->JobFiles,
291 OT_SIZE, "JobBytes", njcr->JobBytes,
292 OT_INT, "Bytes/sec", bps,
293 OT_INT, "Errors", njcr->JobErrors,
294 OT_INT64, "Bwlimit", njcr->max_bandwidth,
295 OT_SIZE, "ReadBytes", njcr->ReadBytes,
298 ow.get_output(OT_INT32, "Files Examined", njcr->num_files_examined, OT_END);
300 if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
301 ow.get_output(OT_INT32, "Expected Files", njcr->ExpectedFiles,
302 OT_INT32, "Percent Complete", 100*(njcr->num_files_examined/njcr->ExpectedFiles),
306 sendit(p, strlen(p), sp);
307 ow.get_output(OT_CLEAR, OT_END);
309 if (njcr->JobFiles > 0) {
311 ow.get_output(OT_STRING, "Processing file", njcr->last_fname, OT_END);
315 if (njcr->store_bsock) {
316 ow.get_output(OT_INT64, "SDReadSeqNo", (int64_t)njcr->store_bsock->read_seqno,
317 OT_INT, "fd", njcr->store_bsock->m_fd,
318 OT_INT, "SDtls", (njcr->store_bsock->tls)?1:0,
321 ow.get_output(OT_STRING, "SDSocket", "closed", OT_END);
323 ow.get_output(OT_END_OBJ, OT_END);
324 sendit(p, strlen(p), sp);
329 static void list_running_jobs(STATUS_PKT *sp)
332 list_running_jobs_api(sp);
334 list_running_jobs_plain(sp);
339 * Status command from Director
341 int status_cmd(JCR *jcr)
343 BSOCK *user = jcr->dir_bsock;
348 sp.api = false; /* no API output */
351 user->signal(BNET_EOD);
356 * .status command from Director
358 int qstatus_cmd(JCR *jcr)
360 BSOCK *dir = jcr->dir_bsock;
367 cmd = get_memory(dir->msglen+1);
369 if (sscanf(dir->msg, qstatus2, cmd, &sp.api, sp.api_opts) != 3) {
370 if (sscanf(dir->msg, qstatus1, cmd) != 1) {
371 pm_strcpy(&jcr->errmsg, dir->msg);
372 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
373 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
374 dir->signal(BNET_EOD);
381 if (strcasecmp(cmd, "current") == 0) {
382 dir->fsend(OKqstatus, cmd);
384 if (njcr->JobId != 0) {
385 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
389 } else if (strcasecmp(cmd, "last") == 0) {
390 dir->fsend(OKqstatus, cmd);
391 if ((last_jobs) && (last_jobs->size() > 0)) {
392 job = (s_last_job*)last_jobs->last();
393 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
395 } else if (strcasecmp(cmd, "header") == 0) {
397 list_status_header(&sp);
398 } else if (strcasecmp(cmd, "running") == 0) {
400 list_running_jobs(&sp);
401 } else if (strcasecmp(cmd, "terminated") == 0) {
402 sp.api = MAX(sp.api, 1);
403 list_terminated_jobs(&sp); /* defined in lib/status.h */
405 pm_strcpy(&jcr->errmsg, dir->msg);
406 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
407 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
408 dir->signal(BNET_EOD);
413 dir->signal(BNET_EOD);