2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2011 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Bacula File Daemon Status routines
31 * Kern Sibbald, August MMI
37 #include "lib/status.h"
39 extern void *start_heap;
41 extern bool GetWindowsVersionString(char *buf, int maxsiz);
44 /* Forward referenced functions */
45 static void list_terminated_jobs(STATUS_PKT *sp);
46 static void list_running_jobs(STATUS_PKT *sp);
47 static void list_status_header(STATUS_PKT *sp);
48 static void sendit(const char *msg, int len, STATUS_PKT *sp);
49 static const char *level_to_str(int level);
51 /* Static variables */
52 static char qstatus[] = ".status %s\n";
54 static char OKqstatus[] = "2000 OK .status\n";
55 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
57 #if defined(HAVE_WIN32)
63 extern VSSClient *g_pVSSClient;
69 * General status generator
71 void output_status(STATUS_PKT *sp)
73 list_status_header(sp);
74 list_running_jobs(sp);
75 list_terminated_jobs(sp);
78 static void list_status_header(STATUS_PKT *sp)
80 POOL_MEM msg(PM_MESSAGE);
81 char b1[32], b2[32], b3[32], b4[32], b5[35];
83 char dt[MAX_TIME_LENGTH];
85 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s %s\n"),
86 my_name, VERSION, BDATE, VSS, HOST_OS, DISTNAME, DISTVER);
87 sendit(msg.c_str(), len, sp);
88 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
89 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d running=%d.\n"),
90 dt, num_jobs_run, job_count());
91 sendit(msg.c_str(), len, sp);
92 #if defined(HAVE_WIN32)
94 if (GetWindowsVersionString(buf, sizeof(buf))) {
95 len = Mmsg(msg, "%s\n", buf);
96 sendit(msg.c_str(), len, sp);
98 if (debug_level > 0) {
100 privs = enable_backup_privileges(NULL, 1);
102 len = Mmsg(msg, "VSS %s, Priv 0x%x\n", g_pVSSClient?"enabled":"disabled", privs);
103 sendit(msg.c_str(), len, sp);
104 len = Mmsg(msg, "APIs=%sOPT,%sATP,%sLPV,%sCFA,%sCFW,\n",
105 p_OpenProcessToken?"":"!",
106 p_AdjustTokenPrivileges?"":"!",
107 p_LookupPrivilegeValue?"":"!",
108 p_CreateFileA?"":"!",
109 p_CreateFileW?"":"!");
110 sendit(msg.c_str(), len, sp);
111 len = Mmsg(msg, " %sWUL,%sWMKD,%sGFAA,%sGFAW,%sGFAEA,%sGFAEW,%sSFAA,%sSFAW,%sBR,%sBW,%sSPSP,\n",
114 p_GetFileAttributesA?"":"!",
115 p_GetFileAttributesW?"":"!",
116 p_GetFileAttributesExA?"":"!",
117 p_GetFileAttributesExW?"":"!",
118 p_SetFileAttributesA?"":"!",
119 p_SetFileAttributesW?"":"!",
121 p_BackupWrite?"":"!",
122 p_SetProcessShutdownParameters?"":"!");
123 sendit(msg.c_str(), len, sp);
124 len = Mmsg(msg, " %sWC2MB,%sMB2WC,%sFFFA,%sFFFW,%sFNFA,%sFNFW,%sSCDA,%sSCDW,\n",
125 p_WideCharToMultiByte?"":"!",
126 p_MultiByteToWideChar?"":"!",
127 p_FindFirstFileA?"":"!",
128 p_FindFirstFileW?"":"!",
129 p_FindNextFileA?"":"!",
130 p_FindNextFileW?"":"!",
131 p_SetCurrentDirectoryA?"":"!",
132 p_SetCurrentDirectoryW?"":"!");
133 sendit(msg.c_str(), len, sp);
134 len = Mmsg(msg, " %sGCDA,%sGCDW,%sGVPNW,%sGVNFVMPW\n",
135 p_GetCurrentDirectoryA?"":"!",
136 p_GetCurrentDirectoryW?"":"!",
137 p_GetVolumePathNameW?"":"!",
138 p_GetVolumeNameForVolumeMountPointW?"":"!");
139 sendit(msg.c_str(), len, sp);
142 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
143 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
144 edit_uint64_with_commas(sm_bytes, b2),
145 edit_uint64_with_commas(sm_max_bytes, b3),
146 edit_uint64_with_commas(sm_buffers, b4),
147 edit_uint64_with_commas(sm_max_buffers, b5));
148 sendit(msg.c_str(), len, sp);
149 len = Mmsg(msg, _(" Sizeof: boffset_t=%d size_t=%d debug=%d trace=%d "
150 "bwlimit=%skB/s\n"), sizeof(boffset_t), sizeof(size_t),
151 debug_level, get_trace(), edit_uint64_with_commas(me->max_bandwidth_per_job/1024, b1));
152 sendit(msg.c_str(), len, sp);
153 if (debug_level > 0 && plugin_list->size() > 0) {
156 pm_strcpy(msg, "Plugin: ");
157 foreach_alist(plugin, plugin_list) {
158 len = pm_strcat(msg, plugin->file);
160 pm_strcat(msg, "\n ");
165 len = pm_strcat(msg, "\n");
166 sendit(msg.c_str(), len, sp);
170 static void list_running_jobs_plain(STATUS_PKT *sp)
173 POOL_MEM msg(PM_MESSAGE);
174 char b1[32], b2[32], b3[32], b4[32];
178 char dt[MAX_TIME_LENGTH];
182 Dmsg0(1000, "Begin status jcr loop.\n");
183 len = Mmsg(msg, _("\nRunning Jobs:\n"));
184 sendit(msg.c_str(), len, sp);
185 const char *vss = "";
187 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
192 bstrftime_nc(dt, sizeof(dt), njcr->start_time);
193 if (njcr->JobId == 0) {
194 len = Mmsg(msg, _("Director connected at: %s\n"), dt);
196 len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
197 njcr->JobId, njcr->Job);
198 sendit(msg.c_str(), len, sp);
199 len = Mmsg(msg, _(" %s%s %s Job started: %s\n"),
200 vss, level_to_str(njcr->getJobLevel()),
201 job_type_to_str(njcr->getJobType()), dt);
203 sendit(msg.c_str(), len, sp);
204 if (njcr->JobId == 0) {
207 sec = time(NULL) - njcr->start_time;
211 bps = (int)(njcr->JobBytes / sec);
212 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"
214 edit_uint64_with_commas(njcr->JobFiles, b1),
215 edit_uint64_with_commas(njcr->JobBytes, b2),
216 edit_uint64_with_commas(bps, b3),
217 njcr->JobErrors, edit_uint64_with_commas(njcr->max_bandwidth, b4));
218 sendit(msg.c_str(), len, sp);
219 len = Mmsg(msg, _(" Files Examined=%s\n"),
220 edit_uint64_with_commas(njcr->num_files_examined, b1));
221 sendit(msg.c_str(), len, sp);
222 if (njcr->JobFiles > 0) {
224 len = Mmsg(msg, _(" Processing file: %s\n"), njcr->last_fname);
226 sendit(msg.c_str(), len, sp);
230 if (njcr->store_bsock) {
231 len = Mmsg(msg, " SDReadSeqNo=%" lld " fd=%d\n",
232 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
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);
248 static void list_running_jobs_api(STATUS_PKT *sp)
251 POOL_MEM msg(PM_MESSAGE);
252 char b1[32], b2[32], b3[32], b4[32];
256 char dt[MAX_TIME_LENGTH];
258 * List running jobs for Bat/Bweb (simple to parse)
262 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
267 bstrutime(dt, sizeof(dt), njcr->start_time);
268 if (njcr->JobId == 0) {
269 len = Mmsg(msg, "DirectorConnected=%s\n", dt);
271 len = Mmsg(msg, "JobId=%d\n Job=%s\n",
272 njcr->JobId, njcr->Job);
273 sendit(msg.c_str(), len, sp);
274 len = Mmsg(msg," VSS=%d\n Level=%c\n JobType=%c\n JobStarted=%s\n",
275 vss, njcr->getJobLevel(),
276 njcr->getJobType(), dt);
278 sendit(msg.c_str(), len, sp);
279 if (njcr->JobId == 0) {
282 sec = time(NULL) - njcr->start_time;
286 bps = (int)(njcr->JobBytes / sec);
287 len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
289 edit_uint64(njcr->JobFiles, b1),
290 edit_uint64(njcr->JobBytes, b2),
291 edit_uint64(bps, b3),
292 njcr->JobErrors, edit_int64(njcr->max_bandwidth, b4));
293 sendit(msg.c_str(), len, sp);
294 len = Mmsg(msg, " Files Examined=%s\n",
295 edit_uint64(njcr->num_files_examined, b1));
296 sendit(msg.c_str(), len, sp);
297 if (njcr->JobFiles > 0) {
299 len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
301 sendit(msg.c_str(), len, sp);
305 if (njcr->store_bsock) {
306 len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
307 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
308 sendit(msg.c_str(), len, sp);
310 len = Mmsg(msg, _(" SDSocket=closed\n"));
311 sendit(msg.c_str(), len, sp);
317 static void list_running_jobs(STATUS_PKT *sp)
320 list_running_jobs_api(sp);
322 list_running_jobs_plain(sp);
326 static void list_terminated_jobs(STATUS_PKT *sp)
328 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
330 struct s_last_job *je;
334 msg = _("\nTerminated Jobs:\n");
335 sendit(msg, strlen(msg), sp);
338 if (last_jobs->size() == 0) {
339 if (!sp->api) sendit(_("====\n"), 5, sp);
342 lock_last_jobs_list();
344 msg = _(" JobId Level Files Bytes Status Finished Name \n");
345 sendit(msg, strlen(msg), sp);
346 msg = _("======================================================================\n");
347 sendit(msg, strlen(msg), sp);
349 foreach_dlist(je, last_jobs) {
350 char JobName[MAX_NAME_LENGTH];
351 const char *termstat;
354 bstrftime_nc(dt, sizeof(dt), je->end_time);
355 switch (je->JobType) {
358 bstrncpy(level, " ", sizeof(level));
361 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
365 switch (je->JobStatus) {
367 termstat = _("Created");
370 case JS_ErrorTerminated:
371 termstat = _("Error");
374 termstat = _("Diffs");
377 termstat = _("Cancel");
383 termstat = _("Other");
386 bstrncpy(JobName, je->Job, sizeof(JobName));
387 /* There are three periods after the Job name */
389 for (int i=0; i<3; i++) {
390 if ((p=strrchr(JobName, '.')) != NULL) {
395 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
398 edit_uint64_with_commas(je->JobFiles, b1),
399 edit_uint64_with_suffix(je->JobBytes, b2),
403 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
406 edit_uint64_with_commas(je->JobFiles, b1),
407 edit_uint64_with_suffix(je->JobBytes, b2),
411 sendit(buf, strlen(buf), sp);
413 if (!sp->api) sendit(_("====\n"), 5, sp);
414 unlock_last_jobs_list();
419 * Send to bsock (Director or Console)
421 static void sendit(const char *msg, int len, STATUS_PKT *sp)
424 BSOCK *user = sp->bs;
425 user->msg = check_pool_memory_size(user->msg, len+1);
426 memcpy(user->msg, msg, len+1);
427 user->msglen = len+1;
430 sp->callback(msg, len, sp->context);
435 * Status command from Director
437 int status_cmd(JCR *jcr)
439 BSOCK *user = jcr->dir_bsock;
444 sp.api = false; /* no API output */
447 user->signal(BNET_EOD);
452 * .status command from Director
454 int qstatus_cmd(JCR *jcr)
456 BSOCK *dir = jcr->dir_bsock;
463 cmd = get_memory(dir->msglen+1);
465 if (sscanf(dir->msg, qstatus, cmd) != 1) {
466 pm_strcpy(&jcr->errmsg, dir->msg);
467 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
468 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
469 dir->signal(BNET_EOD);
475 if (strcmp(cmd, "current") == 0) {
476 dir->fsend(OKqstatus, cmd);
478 if (njcr->JobId != 0) {
479 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
483 } else if (strcmp(cmd, "last") == 0) {
484 dir->fsend(OKqstatus, cmd);
485 if ((last_jobs) && (last_jobs->size() > 0)) {
486 job = (s_last_job*)last_jobs->last();
487 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
489 } else if (strcasecmp(cmd, "header") == 0) {
491 list_status_header(&sp);
492 } else if (strcasecmp(cmd, "running") == 0) {
494 list_running_jobs(&sp);
495 } else if (strcasecmp(cmd, "terminated") == 0) {
497 list_terminated_jobs(&sp);
499 pm_strcpy(&jcr->errmsg, dir->msg);
500 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
501 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
502 dir->signal(BNET_EOD);
507 dir->signal(BNET_EOD);
513 * Convert Job Level into a string
515 static const char *level_to_str(int level)
526 str = _("Incremental");
529 str = _("Differential");
534 case L_VERIFY_CATALOG:
535 str = _("Verify Catalog");
538 str = _("Init Catalog");
540 case L_VERIFY_VOLUME_TO_CATALOG:
541 str = _("Volume to Catalog");
543 case L_VERIFY_DISK_TO_CATALOG:
544 str = _("Disk to Catalog");
553 str = _("Unknown Job Level");
560 #if defined(HAVE_WIN32)
564 * Put message in Window List Box
566 char *bac_status(char *buf, int buf_len)
569 const char *termstat = _("Bacula Client: Idle");
570 struct s_last_job *job;
571 int stat = 0; /* Idle */
576 Dmsg0(1000, "Begin bac_status jcr loop.\n");
578 if (njcr->JobId != 0) {
580 termstat = _("Bacula Client: Running");
589 if (last_jobs->size() > 0) {
590 job = (struct s_last_job *)last_jobs->last();
591 stat = job->JobStatus;
592 switch (job->JobStatus) {
594 termstat = _("Bacula Client: Last Job Canceled");
596 case JS_ErrorTerminated:
598 termstat = _("Bacula Client: Last Job Failed");
602 termstat = _("Bacula Client: Last Job had Warnings");
607 Dmsg0(1000, "End bac_status jcr loop.\n");
611 bstrncpy(buf, termstat, buf_len);
616 #endif /* HAVE_WIN32 */