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 && plugin_list->size() > 0) {
155 pm_strcpy(msg, "Plugin: ");
156 foreach_alist(plugin, plugin_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);
285 len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
287 edit_uint64(njcr->JobFiles, b1),
288 edit_uint64(njcr->JobBytes, b2),
289 edit_uint64(bps, b3),
290 njcr->JobErrors, njcr->max_bandwidth);
292 len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n",
293 edit_uint64(njcr->JobFiles, b1),
294 edit_uint64(njcr->JobBytes, b2),
295 edit_uint64(bps, b3),
297 >>>>>>> caaa5db... Implement RestoreObject for sqlite + cleanups
298 sendit(msg.c_str(), len, sp);
299 len = Mmsg(msg, " Files Examined=%s\n",
300 edit_uint64(njcr->num_files_examined, b1));
301 sendit(msg.c_str(), len, sp);
302 if (njcr->JobFiles > 0) {
304 len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
306 sendit(msg.c_str(), len, sp);
309 if (njcr->store_bsock) {
310 len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
311 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
312 sendit(msg.c_str(), len, sp);
314 len = Mmsg(msg, _(" SDSocket=closed\n"));
315 sendit(msg.c_str(), len, sp);
321 static void list_running_jobs(STATUS_PKT *sp)
324 list_running_jobs_api(sp);
326 list_running_jobs_plain(sp);
330 static void list_terminated_jobs(STATUS_PKT *sp)
332 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
334 struct s_last_job *je;
338 msg = _("\nTerminated Jobs:\n");
339 sendit(msg, strlen(msg), sp);
342 if (last_jobs->size() == 0) {
343 if (!sp->api) sendit(_("====\n"), 5, sp);
346 lock_last_jobs_list();
348 msg = _(" JobId Level Files Bytes Status Finished Name \n");
349 sendit(msg, strlen(msg), sp);
350 msg = _("======================================================================\n");
351 sendit(msg, strlen(msg), sp);
353 foreach_dlist(je, last_jobs) {
354 char JobName[MAX_NAME_LENGTH];
355 const char *termstat;
358 bstrftime_nc(dt, sizeof(dt), je->end_time);
359 switch (je->JobType) {
362 bstrncpy(level, " ", sizeof(level));
365 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
369 switch (je->JobStatus) {
371 termstat = _("Created");
374 case JS_ErrorTerminated:
375 termstat = _("Error");
378 termstat = _("Diffs");
381 termstat = _("Cancel");
387 termstat = _("Other");
390 bstrncpy(JobName, je->Job, sizeof(JobName));
391 /* There are three periods after the Job name */
393 for (int i=0; i<3; i++) {
394 if ((p=strrchr(JobName, '.')) != NULL) {
399 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
402 edit_uint64_with_commas(je->JobFiles, b1),
403 edit_uint64_with_suffix(je->JobBytes, b2),
407 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
410 edit_uint64_with_commas(je->JobFiles, b1),
411 edit_uint64_with_suffix(je->JobBytes, b2),
415 sendit(buf, strlen(buf), sp);
417 if (!sp->api) sendit(_("====\n"), 5, sp);
418 unlock_last_jobs_list();
423 * Send to bsock (Director or Console)
425 static void sendit(const char *msg, int len, STATUS_PKT *sp)
428 BSOCK *user = sp->bs;
429 user->msg = check_pool_memory_size(user->msg, len+1);
430 memcpy(user->msg, msg, len+1);
431 user->msglen = len+1;
434 sp->callback(msg, len, sp->context);
439 * Status command from Director
441 int status_cmd(JCR *jcr)
443 BSOCK *user = jcr->dir_bsock;
448 sp.api = false; /* no API output */
451 user->signal(BNET_EOD);
456 * .status command from Director
458 int qstatus_cmd(JCR *jcr)
460 BSOCK *dir = jcr->dir_bsock;
467 cmd = get_memory(dir->msglen+1);
469 if (sscanf(dir->msg, qstatus, cmd) != 1) {
470 pm_strcpy(&jcr->errmsg, dir->msg);
471 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
472 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
473 dir->signal(BNET_EOD);
479 if (strcmp(cmd, "current") == 0) {
480 dir->fsend(OKqstatus, cmd);
482 if (njcr->JobId != 0) {
483 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
487 } else if (strcmp(cmd, "last") == 0) {
488 dir->fsend(OKqstatus, cmd);
489 if ((last_jobs) && (last_jobs->size() > 0)) {
490 job = (s_last_job*)last_jobs->last();
491 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
493 } else if (strcasecmp(cmd, "header") == 0) {
495 list_status_header(&sp);
496 } else if (strcasecmp(cmd, "running") == 0) {
498 list_running_jobs(&sp);
499 } else if (strcasecmp(cmd, "terminated") == 0) {
501 list_terminated_jobs(&sp);
503 pm_strcpy(&jcr->errmsg, dir->msg);
504 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
505 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
506 dir->signal(BNET_EOD);
511 dir->signal(BNET_EOD);
517 * Convert Job Level into a string
519 static const char *level_to_str(int level)
530 str = _("Incremental");
533 str = _("Differential");
538 case L_VERIFY_CATALOG:
539 str = _("Verify Catalog");
542 str = _("Init Catalog");
544 case L_VERIFY_VOLUME_TO_CATALOG:
545 str = _("Volume to Catalog");
547 case L_VERIFY_DISK_TO_CATALOG:
548 str = _("Disk to Catalog");
557 str = _("Unknown Job Level");
564 #if defined(HAVE_WIN32)
568 * Put message in Window List Box
570 char *bac_status(char *buf, int buf_len)
573 const char *termstat = _("Bacula Client: Idle");
574 struct s_last_job *job;
575 int stat = 0; /* Idle */
580 Dmsg0(1000, "Begin bac_status jcr loop.\n");
582 if (njcr->JobId != 0) {
584 termstat = _("Bacula Client: Running");
593 if (last_jobs->size() > 0) {
594 job = (struct s_last_job *)last_jobs->last();
595 stat = job->JobStatus;
596 switch (job->JobStatus) {
598 termstat = _("Bacula Client: Last Job Canceled");
600 case JS_ErrorTerminated:
602 termstat = _("Bacula Client: Last Job Failed");
606 termstat = _("Bacula Client: Last Job had Warnings");
611 Dmsg0(1000, "End bac_status jcr loop.\n");
615 bstrncpy(buf, termstat, buf_len);
620 #endif /* HAVE_WIN32 */