2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2018 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
28 #include "lib/status.h"
30 extern void *start_heap;
32 extern bool GetWindowsVersionString(char *buf, int maxsiz);
35 /* Forward referenced functions */
36 static void list_running_jobs(STATUS_PKT *sp);
37 static void list_status_header(STATUS_PKT *sp);
39 /* Static variables */
40 static char qstatus1[] = ".status %127s\n";
41 static char qstatus2[] = ".status %127s api=%d api_opts=%127s";
43 static char OKqstatus[] = "2000 OK .status\n";
44 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
46 #if defined(HAVE_WIN32)
57 * General status generator
59 void output_status(STATUS_PKT *sp)
61 list_status_header(sp);
62 list_running_jobs(sp);
63 list_terminated_jobs(sp); /* defined in lib/status.h */
67 static const bool have_lzo = true;
69 static const bool have_lzo = false;
73 static void api_list_status_header(STATUS_PKT *sp)
77 OutputWriter wt(sp->api_opts);
80 #if defined(HAVE_WIN32)
81 if (!GetWindowsVersionString(buf, sizeof(buf))) {
86 wt.start_group("header");
88 OT_STRING, "name", my_name,
89 OT_STRING, "version", VERSION " (" BDATE ")",
90 OT_STRING, "uname", HOST_OS " " DISTNAME " " DISTVER,
91 OT_UTIME, "started", daemon_start_time,
92 OT_INT, "jobs_run", num_jobs_run,
93 OT_INT, "jobs_running",job_count(),
94 OT_STRING, "winver", buf,
95 OT_INT64, "debug", debug_level,
96 OT_INT, "trace", get_trace(),
97 OT_INT64, "bwlimit", me->max_bandwidth_per_job,
98 OT_PLUGINS, "plugins", b_plugin_list,
101 sendit(p, strlen(p), sp);
104 static void list_status_header(STATUS_PKT *sp)
106 POOL_MEM msg(PM_MESSAGE);
107 char b1[32], b2[32], b3[32], b4[32], b5[35];
108 int64_t memused = (char *)sbrk(0)-(char *)start_heap;
110 char dt[MAX_TIME_LENGTH];
113 api_list_status_header(sp);
117 len = Mmsg(msg, _("%s %sVersion: %s (%s) %s %s %s %s\n"),
118 my_name, BDEMO, VERSION, BDATE, VSS, HOST_OS,
120 sendit(msg.c_str(), len, sp);
121 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
122 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d running=%d.\n"),
123 dt, num_jobs_run, job_count());
124 sendit(msg.c_str(), len, sp);
125 #if defined(HAVE_WIN32)
127 if (GetWindowsVersionString(buf, sizeof(buf))) {
128 len = Mmsg(msg, "%s\n", buf);
129 sendit(msg.c_str(), len, sp);
131 memused = get_memory_info(buf, sizeof(buf));
132 if (debug_level > 0) {
134 privs = enable_backup_privileges(NULL, 1);
136 len = Mmsg(msg, "Priv 0x%x\n", privs);
137 sendit(msg.c_str(), len, sp);
139 /* Display detailed information that we got from get_memory_info() */
140 len = Mmsg(msg, "Memory: %s\n", buf);
141 sendit(msg.c_str(), len, sp);
143 len = Mmsg(msg, "APIs=%sOPT,%sATP,%sLPV,%sCFA,%sCFW,\n",
144 p_OpenProcessToken?"":"!",
145 p_AdjustTokenPrivileges?"":"!",
146 p_LookupPrivilegeValue?"":"!",
147 p_CreateFileA?"":"!",
148 p_CreateFileW?"":"!");
149 sendit(msg.c_str(), len, sp);
150 len = Mmsg(msg, " %sWUL,%sWMKD,%sGFAA,%sGFAW,%sGFAEA,%sGFAEW,%sSFAA,%sSFAW,%sBR,%sBW,%sSPSP,\n",
153 p_GetFileAttributesA?"":"!",
154 p_GetFileAttributesW?"":"!",
155 p_GetFileAttributesExA?"":"!",
156 p_GetFileAttributesExW?"":"!",
157 p_SetFileAttributesA?"":"!",
158 p_SetFileAttributesW?"":"!",
160 p_BackupWrite?"":"!",
161 p_SetProcessShutdownParameters?"":"!");
162 sendit(msg.c_str(), len, sp);
163 len = Mmsg(msg, " %sWC2MB,%sMB2WC,%sFFFA,%sFFFW,%sFNFA,%sFNFW,%sSCDA,%sSCDW,\n",
164 p_WideCharToMultiByte?"":"!",
165 p_MultiByteToWideChar?"":"!",
166 p_FindFirstFileA?"":"!",
167 p_FindFirstFileW?"":"!",
168 p_FindNextFileA?"":"!",
169 p_FindNextFileW?"":"!",
170 p_SetCurrentDirectoryA?"":"!",
171 p_SetCurrentDirectoryW?"":"!");
172 sendit(msg.c_str(), len, sp);
173 len = Mmsg(msg, " %sGCDA,%sGCDW,%sGVPNW,%sGVNFVMPW,%sLZO,%sEFS\n",
174 p_GetCurrentDirectoryA?"":"!",
175 p_GetCurrentDirectoryW?"":"!",
176 p_GetVolumePathNameW?"":"!",
177 p_GetVolumeNameForVolumeMountPointW?"":"!",
180 sendit(msg.c_str(), len, sp);
183 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
184 edit_uint64_with_commas(memused, b1),
185 edit_uint64_with_commas(sm_bytes, b2),
186 edit_uint64_with_commas(sm_max_bytes, b3),
187 edit_uint64_with_commas(sm_buffers, b4),
188 edit_uint64_with_commas(sm_max_buffers, b5));
189 sendit(msg.c_str(), len, sp);
190 len = Mmsg(msg, _(" Sizes: boffset_t=%d size_t=%d debug=%s trace=%d "
191 "mode=%d,%d bwlimit=%skB/s\n"),
192 sizeof(boffset_t), sizeof(size_t),
193 edit_uint64(debug_level, b2), get_trace(), (int)DEVELOPER_MODE, 0,
194 edit_uint64_with_commas(me->max_bandwidth_per_job/1024, b1));
195 sendit(msg.c_str(), len, sp);
196 if (b_plugin_list && b_plugin_list->size() > 0) {
199 pm_strcpy(msg, " Plugin: ");
200 foreach_alist(plugin, b_plugin_list) {
201 len = pm_strcat(msg, plugin->file);
202 /* Print plugin version when debug activated */
203 if (debug_level > 0 && plugin->pinfo) {
204 pInfo *info = (pInfo *)plugin->pinfo;
206 pm_strcat(msg, NPRT(info->plugin_version));
207 len = pm_strcat(msg, ")");
210 pm_strcat(msg, "\n ");
215 len = pm_strcat(msg, "\n");
216 sendit(msg.c_str(), len, sp);
221 * List running jobs in for humans.
223 static void list_running_jobs_plain(STATUS_PKT *sp)
225 int total_sec, inst_sec;
226 uint64_t total_bps, inst_bps;
227 POOL_MEM msg(PM_MESSAGE);
228 char b1[50], b2[50], b3[50], b4[50], b5[50], b6[50];
232 time_t now = time(NULL);
233 char dt[MAX_TIME_LENGTH];
235 Dmsg0(1000, "Begin status jcr loop.\n");
236 len = Mmsg(msg, _("\nRunning Jobs:\n"));
237 sendit(msg.c_str(), len, sp);
239 const char *vss = "";
241 if (njcr->pVSSClient && njcr->pVSSClient->IsInitialized()) {
245 bstrftime_nc(dt, sizeof(dt), njcr->start_time);
246 if (njcr->JobId == 0) {
247 len = Mmsg(msg, _("Director connected %sat: %s\n"),
248 (njcr->dir_bsock && njcr->dir_bsock->tls)?_("using TLS "):"",
251 len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
252 njcr->JobId, njcr->Job);
253 sendit(msg.c_str(), len, sp);
254 len = Mmsg(msg, _(" %s%s %s Job started: %s\n"),
255 vss, job_level_to_str(njcr->getJobLevel()),
256 job_type_to_str(njcr->getJobType()), dt);
258 sendit(msg.c_str(), len, sp);
259 if (njcr->JobId == 0) {
262 if (njcr->last_time == 0) {
263 njcr->last_time = njcr->start_time;
265 total_sec = now - njcr->start_time;
266 inst_sec = now - njcr->last_time;
267 if (total_sec <= 0) {
273 /* Instanteous bps not smoothed */
274 inst_bps = (njcr->JobBytes - njcr->LastJobBytes) / inst_sec;
275 if (njcr->LastRate <= 0) {
276 njcr->LastRate = inst_bps;
278 /* Smooth the instantaneous bps a bit */
279 inst_bps = (2 * njcr->LastRate + inst_bps) / 3;
280 /* total bps (AveBytes/sec) since start of job */
281 total_bps = njcr->JobBytes / total_sec;
282 len = Mmsg(msg, _(" Files=%s Bytes=%s AveBytes/sec=%s LastBytes/sec=%s Errors=%d\n"
283 " Bwlimit=%s ReadBytes=%s\n"),
284 edit_uint64_with_commas(njcr->JobFiles, b1),
285 edit_uint64_with_commas(njcr->JobBytes, b2),
286 edit_uint64_with_commas(total_bps, b3),
287 edit_uint64_with_commas(inst_bps, b4),
288 njcr->JobErrors, edit_uint64_with_commas(njcr->max_bandwidth, b5),
289 edit_uint64_with_commas(njcr->ReadBytes, b6));
290 sendit(msg.c_str(), len, sp);
292 if (njcr->is_JobType(JT_RESTORE)) {
293 if (njcr->ExpectedFiles > 0) {
294 len = Mmsg(msg, _(" Files: Restored=%s Expected=%s Completed=%d%%\n"),
295 edit_uint64_with_commas(njcr->num_files_examined, b1),
296 edit_uint64_with_commas(njcr->ExpectedFiles, b2),
297 (100*njcr->num_files_examined)/njcr->ExpectedFiles);
300 len = Mmsg(msg, _(" Files: Restored=%s\n"),
301 edit_uint64_with_commas(njcr->num_files_examined, b1));
304 len = Mmsg(msg, _(" Files: Examined=%s Backed up=%s\n"),
305 edit_uint64_with_commas(njcr->num_files_examined, b1),
306 edit_uint64_with_commas(njcr->JobFiles, b2));
308 /* Update only every 10 seconds */
309 if (now - njcr->last_time > 10) {
310 njcr->LastRate = inst_bps;
311 njcr->LastJobBytes = njcr->JobBytes;
312 njcr->last_time = now;
314 sendit(msg.c_str(), len, sp);
315 if (njcr->JobFiles > 0) {
317 len = Mmsg(msg, _(" Processing file: %s\n"), njcr->last_fname);
319 sendit(msg.c_str(), len, sp);
323 if (njcr->store_bsock) {
324 len = Mmsg(msg, " SDReadSeqNo=%" lld " fd=%d SDtls=%d\n",
325 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd,
326 (njcr->store_bsock->tls)?1:0);
327 sendit(msg.c_str(), len, sp);
329 len = Mmsg(msg, _(" SDSocket closed.\n"));
330 sendit(msg.c_str(), len, sp);
336 len = Mmsg(msg, _("No Jobs running.\n"));
337 sendit(msg.c_str(), len, sp);
339 sendit(_("====\n"), 5, sp);
343 * List running jobs for Bat or Bweb in a format
344 * simpler to parse. Be careful when changing this
347 static void list_running_jobs_api(STATUS_PKT *sp)
349 OutputWriter ow(sp->api_opts);
354 /* API v1, edit with comma, space before the name, sometime ' ' as separator */
359 if (njcr->pVSSClient && njcr->pVSSClient->IsInitialized()) {
363 p = ow.get_output(OT_CLEAR, OT_START_OBJ, OT_END);
365 if (njcr->JobId == 0) {
366 int val = (njcr->dir_bsock && njcr->dir_bsock->tls)?1:0;
367 ow.get_output(OT_UTIME, "DirectorConnected", njcr->start_time,
368 OT_INT, "DirTLS", val,
371 ow.get_output(OT_INT32, "JobId", njcr->JobId,
372 OT_STRING, "Job", njcr->Job,
374 OT_JOBLEVEL,"Level", njcr->getJobLevel(),
375 OT_JOBTYPE, "Type", njcr->getJobType(),
376 OT_JOBSTATUS, "Status", njcr->getJobStatus(),
377 OT_UTIME, "StartTime", njcr->start_time,
381 sendit(p, strlen(p), sp);
382 if (njcr->JobId == 0) {
385 sec = time(NULL) - njcr->start_time;
389 bps = (int)(njcr->JobBytes / sec);
390 ow.get_output(OT_CLEAR,
391 OT_INT32, "JobFiles", njcr->JobFiles,
392 OT_SIZE, "JobBytes", njcr->JobBytes,
393 OT_INT, "Bytes/sec", bps,
394 OT_INT, "Errors", njcr->JobErrors,
395 OT_INT64, "Bwlimit", njcr->max_bandwidth,
396 OT_SIZE, "ReadBytes", njcr->ReadBytes,
399 ow.get_output(OT_INT32, "Files Examined", njcr->num_files_examined, OT_END);
401 if (njcr->is_JobType(JT_RESTORE) && njcr->ExpectedFiles > 0) {
402 ow.get_output(OT_INT32, "Expected Files", njcr->ExpectedFiles,
403 OT_INT32, "Percent Complete", 100*(njcr->num_files_examined/njcr->ExpectedFiles),
407 sendit(p, strlen(p), sp);
408 ow.get_output(OT_CLEAR, OT_END);
410 if (njcr->JobFiles > 0) {
412 ow.get_output(OT_STRING, "Processing file", njcr->last_fname, OT_END);
416 if (njcr->store_bsock) {
417 int val = (njcr->store_bsock->tls)?1:0;
418 ow.get_output(OT_INT64, "SDReadSeqNo", (int64_t)njcr->store_bsock->read_seqno,
419 OT_INT, "fd", njcr->store_bsock->m_fd,
420 OT_INT, "SDtls", val,
423 ow.get_output(OT_STRING, "SDSocket", "closed", OT_END);
425 ow.get_output(OT_END_OBJ, OT_END);
426 sendit(p, strlen(p), sp);
431 static void list_running_jobs(STATUS_PKT *sp)
434 list_running_jobs_api(sp);
436 list_running_jobs_plain(sp);
441 * Status command from Director
443 int status_cmd(JCR *jcr)
445 BSOCK *user = jcr->dir_bsock;
450 sp.api = false; /* no API output */
453 user->signal(BNET_EOD);
458 * .status command from Director
460 int qstatus_cmd(JCR *jcr)
462 BSOCK *dir = jcr->dir_bsock;
469 cmd = get_memory(dir->msglen+1);
471 if (sscanf(dir->msg, qstatus2, cmd, &sp.api, sp.api_opts) != 3) {
472 if (sscanf(dir->msg, qstatus1, cmd) != 1) {
473 pm_strcpy(&jcr->errmsg, dir->msg);
474 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
475 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
476 dir->signal(BNET_EOD);
483 if (strcasecmp(cmd, "current") == 0) {
484 dir->fsend(OKqstatus, cmd);
486 if (njcr->JobId != 0) {
487 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
491 } else if (strcasecmp(cmd, "last") == 0) {
492 dir->fsend(OKqstatus, cmd);
493 if ((last_jobs) && (last_jobs->size() > 0)) {
494 job = (s_last_job*)last_jobs->last();
495 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
497 } else if (strcasecmp(cmd, "header") == 0) {
499 list_status_header(&sp);
500 } else if (strcasecmp(cmd, "running") == 0) {
502 list_running_jobs(&sp);
503 } else if (strcasecmp(cmd, "terminated") == 0) {
504 sp.api = MAX(sp.api, 1);
505 list_terminated_jobs(&sp); /* defined in lib/status.h */
507 pm_strcpy(&jcr->errmsg, dir->msg);
508 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
509 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
510 dir->signal(BNET_EOD);
515 dir->signal(BNET_EOD);