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=%lldkb/s\n"), sizeof(boffset_t), sizeof(size_t),
151 debug_level, get_trace(), me->max_bandwidth_per_job/1024);
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];
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"),
213 edit_uint64_with_commas(njcr->JobFiles, b1),
214 edit_uint64_with_commas(njcr->JobBytes, b2),
215 edit_uint64_with_commas(bps, b3),
217 sendit(msg.c_str(), len, sp);
218 len = Mmsg(msg, _(" Files Examined=%s\n"),
219 edit_uint64_with_commas(njcr->num_files_examined, b1));
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\n",
231 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
232 sendit(msg.c_str(), len, sp);
234 len = Mmsg(msg, _(" SDSocket closed.\n"));
235 sendit(msg.c_str(), len, sp);
241 len = Mmsg(msg, _("No Jobs running.\n"));
242 sendit(msg.c_str(), len, sp);
244 sendit(_("====\n"), 5, sp);
247 static void list_running_jobs_api(STATUS_PKT *sp)
250 POOL_MEM msg(PM_MESSAGE);
251 char b1[32], b2[32], b3[32];
255 char dt[MAX_TIME_LENGTH];
257 * List running jobs for Bat/Bweb (simple to parse)
261 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
266 bstrutime(dt, sizeof(dt), njcr->start_time);
267 if (njcr->JobId == 0) {
268 len = Mmsg(msg, "DirectorConnected=%s\n", dt);
270 len = Mmsg(msg, "JobId=%d\n Job=%s\n",
271 njcr->JobId, njcr->Job);
272 sendit(msg.c_str(), len, sp);
273 len = Mmsg(msg," VSS=%d\n Level=%c\n JobType=%c\n JobStarted=%s\n",
274 vss, njcr->getJobLevel(),
275 njcr->getJobType(), dt);
277 sendit(msg.c_str(), len, sp);
278 if (njcr->JobId == 0) {
281 sec = time(NULL) - njcr->start_time;
285 bps = (int)(njcr->JobBytes / sec);
286 len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
288 edit_uint64(njcr->JobFiles, b1),
289 edit_uint64(njcr->JobBytes, b2),
290 edit_uint64(bps, b3),
291 njcr->JobErrors, njcr->max_bandwidth);
292 sendit(msg.c_str(), len, sp);
293 len = Mmsg(msg, " Files Examined=%s\n",
294 edit_uint64(njcr->num_files_examined, b1));
295 sendit(msg.c_str(), len, sp);
296 if (njcr->JobFiles > 0) {
298 len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
300 sendit(msg.c_str(), len, sp);
304 if (njcr->store_bsock) {
305 len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
306 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
307 sendit(msg.c_str(), len, sp);
309 len = Mmsg(msg, _(" SDSocket=closed\n"));
310 sendit(msg.c_str(), len, sp);
316 static void list_running_jobs(STATUS_PKT *sp)
319 list_running_jobs_api(sp);
321 list_running_jobs_plain(sp);
325 static void list_terminated_jobs(STATUS_PKT *sp)
327 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
329 struct s_last_job *je;
333 msg = _("\nTerminated Jobs:\n");
334 sendit(msg, strlen(msg), sp);
337 if (last_jobs->size() == 0) {
338 if (!sp->api) sendit(_("====\n"), 5, sp);
341 lock_last_jobs_list();
343 msg = _(" JobId Level Files Bytes Status Finished Name \n");
344 sendit(msg, strlen(msg), sp);
345 msg = _("======================================================================\n");
346 sendit(msg, strlen(msg), sp);
348 foreach_dlist(je, last_jobs) {
349 char JobName[MAX_NAME_LENGTH];
350 const char *termstat;
353 bstrftime_nc(dt, sizeof(dt), je->end_time);
354 switch (je->JobType) {
357 bstrncpy(level, " ", sizeof(level));
360 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
364 switch (je->JobStatus) {
366 termstat = _("Created");
369 case JS_ErrorTerminated:
370 termstat = _("Error");
373 termstat = _("Diffs");
376 termstat = _("Cancel");
382 termstat = _("Other");
385 bstrncpy(JobName, je->Job, sizeof(JobName));
386 /* There are three periods after the Job name */
388 for (int i=0; i<3; i++) {
389 if ((p=strrchr(JobName, '.')) != NULL) {
394 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
397 edit_uint64_with_commas(je->JobFiles, b1),
398 edit_uint64_with_suffix(je->JobBytes, b2),
402 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
405 edit_uint64_with_commas(je->JobFiles, b1),
406 edit_uint64_with_suffix(je->JobBytes, b2),
410 sendit(buf, strlen(buf), sp);
412 if (!sp->api) sendit(_("====\n"), 5, sp);
413 unlock_last_jobs_list();
418 * Send to bsock (Director or Console)
420 static void sendit(const char *msg, int len, STATUS_PKT *sp)
423 BSOCK *user = sp->bs;
424 user->msg = check_pool_memory_size(user->msg, len+1);
425 memcpy(user->msg, msg, len+1);
426 user->msglen = len+1;
429 sp->callback(msg, len, sp->context);
434 * Status command from Director
436 int status_cmd(JCR *jcr)
438 BSOCK *user = jcr->dir_bsock;
443 sp.api = false; /* no API output */
446 user->signal(BNET_EOD);
451 * .status command from Director
453 int qstatus_cmd(JCR *jcr)
455 BSOCK *dir = jcr->dir_bsock;
462 cmd = get_memory(dir->msglen+1);
464 if (sscanf(dir->msg, qstatus, cmd) != 1) {
465 pm_strcpy(&jcr->errmsg, dir->msg);
466 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
467 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
468 dir->signal(BNET_EOD);
474 if (strcmp(cmd, "current") == 0) {
475 dir->fsend(OKqstatus, cmd);
477 if (njcr->JobId != 0) {
478 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
482 } else if (strcmp(cmd, "last") == 0) {
483 dir->fsend(OKqstatus, cmd);
484 if ((last_jobs) && (last_jobs->size() > 0)) {
485 job = (s_last_job*)last_jobs->last();
486 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
488 } else if (strcasecmp(cmd, "header") == 0) {
490 list_status_header(&sp);
491 } else if (strcasecmp(cmd, "running") == 0) {
493 list_running_jobs(&sp);
494 } else if (strcasecmp(cmd, "terminated") == 0) {
496 list_terminated_jobs(&sp);
498 pm_strcpy(&jcr->errmsg, dir->msg);
499 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
500 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
501 dir->signal(BNET_EOD);
506 dir->signal(BNET_EOD);
512 * Convert Job Level into a string
514 static const char *level_to_str(int level)
525 str = _("Incremental");
528 str = _("Differential");
533 case L_VERIFY_CATALOG:
534 str = _("Verify Catalog");
537 str = _("Init Catalog");
539 case L_VERIFY_VOLUME_TO_CATALOG:
540 str = _("Volume to Catalog");
542 case L_VERIFY_DISK_TO_CATALOG:
543 str = _("Disk to Catalog");
552 str = _("Unknown Job Level");
559 #if defined(HAVE_WIN32)
563 * Put message in Window List Box
565 char *bac_status(char *buf, int buf_len)
568 const char *termstat = _("Bacula Client: Idle");
569 struct s_last_job *job;
570 int stat = 0; /* Idle */
575 Dmsg0(1000, "Begin bac_status jcr loop.\n");
577 if (njcr->JobId != 0) {
579 termstat = _("Bacula Client: Running");
588 if (last_jobs->size() > 0) {
589 job = (struct s_last_job *)last_jobs->last();
590 stat = job->JobStatus;
591 switch (job->JobStatus) {
593 termstat = _("Bacula Client: Last Job Canceled");
595 case JS_ErrorTerminated:
597 termstat = _("Bacula Client: Last Job Failed");
601 termstat = _("Bacula Client: Last Job had Warnings");
606 Dmsg0(1000, "End bac_status jcr loop.\n");
610 bstrncpy(buf, termstat, buf_len);
615 #endif /* HAVE_WIN32 */