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\n"),
150 sizeof(boffset_t), sizeof(size_t), debug_level, get_trace());
151 sendit(msg.c_str(), len, sp);
152 if (debug_level > 0 && bplugin_list->size() > 0) {
155 pm_strcpy(msg, "Plugin: ");
156 foreach_alist(plugin, bplugin_list) {
157 len = pm_strcat(msg, plugin->file);
159 pm_strcat(msg, "\n ");
164 len = pm_strcat(msg, "\n");
165 sendit(msg.c_str(), len, sp);
169 static void list_running_jobs_plain(STATUS_PKT *sp)
172 POOL_MEM msg(PM_MESSAGE);
173 char b1[32], b2[32], b3[32];
177 char dt[MAX_TIME_LENGTH];
181 Dmsg0(1000, "Begin status jcr loop.\n");
182 len = Mmsg(msg, _("\nRunning Jobs:\n"));
183 sendit(msg.c_str(), len, sp);
184 const char *vss = "";
186 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
191 bstrftime_nc(dt, sizeof(dt), njcr->start_time);
192 if (njcr->JobId == 0) {
193 len = Mmsg(msg, _("Director connected at: %s\n"), dt);
195 len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
196 njcr->JobId, njcr->Job);
197 sendit(msg.c_str(), len, sp);
198 len = Mmsg(msg, _(" %s%s %s Job started: %s\n"),
199 vss, level_to_str(njcr->getJobLevel()),
200 job_type_to_str(njcr->getJobType()), dt);
202 sendit(msg.c_str(), len, sp);
203 if (njcr->JobId == 0) {
206 sec = time(NULL) - njcr->start_time;
210 bps = (int)(njcr->JobBytes / sec);
211 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"),
212 edit_uint64_with_commas(njcr->JobFiles, b1),
213 edit_uint64_with_commas(njcr->JobBytes, b2),
214 edit_uint64_with_commas(bps, b3),
216 sendit(msg.c_str(), len, sp);
217 len = Mmsg(msg, _(" Files Examined=%s\n"),
218 edit_uint64_with_commas(njcr->num_files_examined, b1));
219 sendit(msg.c_str(), len, sp);
220 if (njcr->JobFiles > 0) {
222 len = Mmsg(msg, _(" Processing file: %s\n"), njcr->last_fname);
224 sendit(msg.c_str(), len, sp);
228 if (njcr->store_bsock) {
229 len = Mmsg(msg, " SDReadSeqNo=%" lld " fd=%d\n",
230 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
231 sendit(msg.c_str(), len, sp);
233 len = Mmsg(msg, _(" SDSocket closed.\n"));
234 sendit(msg.c_str(), len, sp);
240 len = Mmsg(msg, _("No Jobs running.\n"));
241 sendit(msg.c_str(), len, sp);
243 sendit(_("====\n"), 5, sp);
246 static void list_running_jobs_api(STATUS_PKT *sp)
249 POOL_MEM msg(PM_MESSAGE);
250 char b1[32], b2[32], b3[32];
253 char dt[MAX_TIME_LENGTH];
255 * List running jobs for Bat/Bweb (simple to parse)
259 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
264 bstrutime(dt, sizeof(dt), njcr->start_time);
265 if (njcr->JobId == 0) {
266 len = Mmsg(msg, "DirectorConnected=%s\n", dt);
268 len = Mmsg(msg, "JobId=%d\n Job=%s\n",
269 njcr->JobId, njcr->Job);
270 sendit(msg.c_str(), len, sp);
271 len = Mmsg(msg," VSS=%d\n Level=%c\n JobType=%c\n JobStarted=%s\n",
272 vss, njcr->getJobLevel(),
273 njcr->getJobType(), dt);
275 sendit(msg.c_str(), len, sp);
276 if (njcr->JobId == 0) {
279 sec = time(NULL) - njcr->start_time;
283 bps = (int)(njcr->JobBytes / sec);
284 len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n",
285 edit_uint64(njcr->JobFiles, b1),
286 edit_uint64(njcr->JobBytes, b2),
287 edit_uint64(bps, b3),
289 sendit(msg.c_str(), len, sp);
290 len = Mmsg(msg, " Files Examined=%s\n",
291 edit_uint64(njcr->num_files_examined, b1));
292 sendit(msg.c_str(), len, sp);
293 if (njcr->JobFiles > 0) {
295 len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
297 sendit(msg.c_str(), len, sp);
300 if (njcr->store_bsock) {
301 len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
302 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
303 sendit(msg.c_str(), len, sp);
305 len = Mmsg(msg, _(" SDSocket=closed\n"));
306 sendit(msg.c_str(), len, sp);
312 static void list_running_jobs(STATUS_PKT *sp)
315 list_running_jobs_api(sp);
317 list_running_jobs_plain(sp);
321 static void list_terminated_jobs(STATUS_PKT *sp)
323 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
325 struct s_last_job *je;
329 msg = _("\nTerminated Jobs:\n");
330 sendit(msg, strlen(msg), sp);
333 if (last_jobs->size() == 0) {
334 if (!sp->api) sendit(_("====\n"), 5, sp);
337 lock_last_jobs_list();
339 msg = _(" JobId Level Files Bytes Status Finished Name \n");
340 sendit(msg, strlen(msg), sp);
341 msg = _("======================================================================\n");
342 sendit(msg, strlen(msg), sp);
344 foreach_dlist(je, last_jobs) {
345 char JobName[MAX_NAME_LENGTH];
346 const char *termstat;
349 bstrftime_nc(dt, sizeof(dt), je->end_time);
350 switch (je->JobType) {
353 bstrncpy(level, " ", sizeof(level));
356 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
360 switch (je->JobStatus) {
362 termstat = _("Created");
365 case JS_ErrorTerminated:
366 termstat = _("Error");
369 termstat = _("Diffs");
372 termstat = _("Cancel");
378 termstat = _("Other");
381 bstrncpy(JobName, je->Job, sizeof(JobName));
382 /* There are three periods after the Job name */
384 for (int i=0; i<3; i++) {
385 if ((p=strrchr(JobName, '.')) != NULL) {
390 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
393 edit_uint64_with_commas(je->JobFiles, b1),
394 edit_uint64_with_suffix(je->JobBytes, b2),
398 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
401 edit_uint64_with_commas(je->JobFiles, b1),
402 edit_uint64_with_suffix(je->JobBytes, b2),
406 sendit(buf, strlen(buf), sp);
408 if (!sp->api) sendit(_("====\n"), 5, sp);
409 unlock_last_jobs_list();
414 * Send to bsock (Director or Console)
416 static void sendit(const char *msg, int len, STATUS_PKT *sp)
419 BSOCK *user = sp->bs;
420 user->msg = check_pool_memory_size(user->msg, len+1);
421 memcpy(user->msg, msg, len+1);
422 user->msglen = len+1;
425 sp->callback(msg, len, sp->context);
430 * Status command from Director
432 int status_cmd(JCR *jcr)
434 BSOCK *user = jcr->dir_bsock;
439 sp.api = false; /* no API output */
442 user->signal(BNET_EOD);
447 * .status command from Director
449 int qstatus_cmd(JCR *jcr)
451 BSOCK *dir = jcr->dir_bsock;
458 cmd = get_memory(dir->msglen+1);
460 if (sscanf(dir->msg, qstatus, cmd) != 1) {
461 pm_strcpy(&jcr->errmsg, dir->msg);
462 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
463 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
464 dir->signal(BNET_EOD);
470 if (strcmp(cmd, "current") == 0) {
471 dir->fsend(OKqstatus, cmd);
473 if (njcr->JobId != 0) {
474 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
478 } else if (strcmp(cmd, "last") == 0) {
479 dir->fsend(OKqstatus, cmd);
480 if ((last_jobs) && (last_jobs->size() > 0)) {
481 job = (s_last_job*)last_jobs->last();
482 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
484 } else if (strcasecmp(cmd, "header") == 0) {
486 list_status_header(&sp);
487 } else if (strcasecmp(cmd, "running") == 0) {
489 list_running_jobs(&sp);
490 } else if (strcasecmp(cmd, "terminated") == 0) {
492 list_terminated_jobs(&sp);
494 pm_strcpy(&jcr->errmsg, dir->msg);
495 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
496 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
497 dir->signal(BNET_EOD);
502 dir->signal(BNET_EOD);
508 * Convert Job Level into a string
510 static const char *level_to_str(int level)
521 str = _("Incremental");
524 str = _("Differential");
529 case L_VERIFY_CATALOG:
530 str = _("Verify Catalog");
533 str = _("Init Catalog");
535 case L_VERIFY_VOLUME_TO_CATALOG:
536 str = _("Volume to Catalog");
538 case L_VERIFY_DISK_TO_CATALOG:
539 str = _("Disk to Catalog");
548 str = _("Unknown Job Level");
555 #if defined(HAVE_WIN32)
559 * Put message in Window List Box
561 char *bac_status(char *buf, int buf_len)
564 const char *termstat = _("Bacula Client: Idle");
565 struct s_last_job *job;
566 int stat = 0; /* Idle */
571 Dmsg0(1000, "Begin bac_status jcr loop.\n");
573 if (njcr->JobId != 0) {
575 termstat = _("Bacula Client: Running");
584 if (last_jobs->size() > 0) {
585 job = (struct s_last_job *)last_jobs->last();
586 stat = job->JobStatus;
587 switch (job->JobStatus) {
589 termstat = _("Bacula Client: Last Job Canceled");
591 case JS_ErrorTerminated:
593 termstat = _("Bacula Client: Last Job Failed");
597 termstat = _("Bacula Client: Last Job had Warnings");
602 Dmsg0(1000, "End bac_status jcr loop.\n");
606 bstrncpy(buf, termstat, buf_len);
611 #endif /* HAVE_WIN32 */