2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2010 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 /* Forward referenced functions */
42 static void list_terminated_jobs(STATUS_PKT *sp);
43 static void list_running_jobs(STATUS_PKT *sp);
44 static void list_status_header(STATUS_PKT *sp);
45 static void sendit(const char *msg, int len, STATUS_PKT *sp);
46 static const char *level_to_str(int level);
48 /* Static variables */
49 static char qstatus[] = ".status %s\n";
51 static char OKqstatus[] = "2000 OK .status\n";
52 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
54 #if defined(HAVE_WIN32)
60 extern VSSClient *g_pVSSClient;
66 * General status generator
68 void output_status(STATUS_PKT *sp)
70 list_status_header(sp);
71 list_running_jobs(sp);
72 list_terminated_jobs(sp);
75 static void list_status_header(STATUS_PKT *sp)
77 POOL_MEM msg(PM_MESSAGE);
78 char b1[32], b2[32], b3[32], b4[32], b5[35];
80 char dt[MAX_TIME_LENGTH];
82 len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s %s\n"),
83 my_name, VERSION, BDATE, VSS, HOST_OS, DISTNAME, DISTVER);
84 sendit(msg.c_str(), len, sp);
85 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
86 len = Mmsg(msg, _("Daemon started %s. Jobs: run=%d running=%d.\n"),
87 dt, num_jobs_run, job_count());
88 sendit(msg.c_str(), len, sp);
89 #if defined(HAVE_WIN32)
90 if (debug_level > 0) {
92 privs = enable_backup_privileges(NULL, 1);
94 len = Mmsg(msg, "VSS %s, Priv 0x%x\n", g_pVSSClient?"enabled":"disabled", privs);
95 sendit(msg.c_str(), len, sp);
96 len = Mmsg(msg, "APIs=%sOPT,%sATP,%sLPV,%sCFA,%sCFW,\n",
97 p_OpenProcessToken?"":"!",
98 p_AdjustTokenPrivileges?"":"!",
99 p_LookupPrivilegeValue?"":"!",
100 p_CreateFileA?"":"!",
101 p_CreateFileW?"":"!");
102 sendit(msg.c_str(), len, sp);
103 len = Mmsg(msg, " %sWUL,%sWMKD,%sGFAA,%sGFAW,%sGFAEA,%sGFAEW,%sSFAA,%sSFAW,%sBR,%sBW,%sSPSP,\n",
106 p_GetFileAttributesA?"":"!",
107 p_GetFileAttributesW?"":"!",
108 p_GetFileAttributesExA?"":"!",
109 p_GetFileAttributesExW?"":"!",
110 p_SetFileAttributesA?"":"!",
111 p_SetFileAttributesW?"":"!",
113 p_BackupWrite?"":"!",
114 p_SetProcessShutdownParameters?"":"!");
115 sendit(msg.c_str(), len, sp);
116 len = Mmsg(msg, " %sWC2MB,%sMB2WC,%sFFFA,%sFFFW,%sFNFA,%sFNFW,%sSCDA,%sSCDW,\n",
117 p_WideCharToMultiByte?"":"!",
118 p_MultiByteToWideChar?"":"!",
119 p_FindFirstFileA?"":"!",
120 p_FindFirstFileW?"":"!",
121 p_FindNextFileA?"":"!",
122 p_FindNextFileW?"":"!",
123 p_SetCurrentDirectoryA?"":"!",
124 p_SetCurrentDirectoryW?"":"!");
125 sendit(msg.c_str(), len, sp);
126 len = Mmsg(msg, " %sGCDA,%sGCDW,%sGVPNW,%sGVNFVMPW\n",
127 p_GetCurrentDirectoryA?"":"!",
128 p_GetCurrentDirectoryW?"":"!",
129 p_GetVolumePathNameW?"":"!",
130 p_GetVolumeNameForVolumeMountPointW?"":"!");
131 sendit(msg.c_str(), len, sp);
134 len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
135 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
136 edit_uint64_with_commas(sm_bytes, b2),
137 edit_uint64_with_commas(sm_max_bytes, b3),
138 edit_uint64_with_commas(sm_buffers, b4),
139 edit_uint64_with_commas(sm_max_buffers, b5));
140 sendit(msg.c_str(), len, sp);
141 len = Mmsg(msg, _(" Sizeof: boffset_t=%d size_t=%d debug=%d trace=%d\n"),
142 sizeof(boffset_t), sizeof(size_t), debug_level, get_trace());
143 sendit(msg.c_str(), len, sp);
144 if (debug_level > 0 && plugin_list->size() > 0) {
147 pm_strcpy(msg, "Plugin: ");
148 foreach_alist(plugin, plugin_list) {
149 len = pm_strcat(msg, plugin->file);
151 pm_strcat(msg, "\n ");
156 len = pm_strcat(msg, "\n");
157 sendit(msg.c_str(), len, sp);
161 static void list_running_jobs_plain(STATUS_PKT *sp)
164 POOL_MEM msg(PM_MESSAGE);
165 char b1[32], b2[32], b3[32];
169 char dt[MAX_TIME_LENGTH];
173 Dmsg0(1000, "Begin status jcr loop.\n");
174 len = Mmsg(msg, _("\nRunning Jobs:\n"));
175 sendit(msg.c_str(), len, sp);
176 const char *vss = "";
178 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
183 bstrftime_nc(dt, sizeof(dt), njcr->start_time);
184 if (njcr->JobId == 0) {
185 len = Mmsg(msg, _("Director connected at: %s\n"), dt);
187 len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
188 njcr->JobId, njcr->Job);
189 sendit(msg.c_str(), len, sp);
190 len = Mmsg(msg, _(" %s%s %s Job started: %s\n"),
191 vss, level_to_str(njcr->getJobLevel()),
192 job_type_to_str(njcr->getJobType()), dt);
194 sendit(msg.c_str(), len, sp);
195 if (njcr->JobId == 0) {
198 sec = time(NULL) - njcr->start_time;
202 bps = (int)(njcr->JobBytes / sec);
203 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"),
204 edit_uint64_with_commas(njcr->JobFiles, b1),
205 edit_uint64_with_commas(njcr->JobBytes, b2),
206 edit_uint64_with_commas(bps, b3),
208 sendit(msg.c_str(), len, sp);
209 len = Mmsg(msg, _(" Files Examined=%s\n"),
210 edit_uint64_with_commas(njcr->num_files_examined, b1));
211 sendit(msg.c_str(), len, sp);
212 if (njcr->JobFiles > 0) {
214 len = Mmsg(msg, _(" Processing file: %s\n"), njcr->last_fname);
216 sendit(msg.c_str(), len, sp);
220 if (njcr->store_bsock) {
221 len = Mmsg(msg, " SDReadSeqNo=%" lld " fd=%d\n",
222 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
223 sendit(msg.c_str(), len, sp);
225 len = Mmsg(msg, _(" SDSocket closed.\n"));
226 sendit(msg.c_str(), len, sp);
232 len = Mmsg(msg, _("No Jobs running.\n"));
233 sendit(msg.c_str(), len, sp);
235 sendit(_("====\n"), 5, sp);
238 static void list_running_jobs_api(STATUS_PKT *sp)
241 POOL_MEM msg(PM_MESSAGE);
242 char b1[32], b2[32], b3[32];
246 char dt[MAX_TIME_LENGTH];
248 * List running jobs for Bat/Bweb (simple to parse)
252 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
257 bstrutime(dt, sizeof(dt), njcr->start_time);
258 if (njcr->JobId == 0) {
259 len = Mmsg(msg, "DirectorConnected=%s\n", dt);
261 len = Mmsg(msg, "JobId=%d\n Job=%s\n",
262 njcr->JobId, njcr->Job);
263 sendit(msg.c_str(), len, sp);
264 len = Mmsg(msg," VSS=%d\n Level=%c\n JobType=%c\n JobStarted=%s\n",
265 vss, njcr->getJobLevel(),
266 njcr->getJobType(), dt);
268 sendit(msg.c_str(), len, sp);
269 if (njcr->JobId == 0) {
272 sec = time(NULL) - njcr->start_time;
276 bps = (int)(njcr->JobBytes / sec);
277 len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
279 edit_uint64(njcr->JobFiles, b1),
280 edit_uint64(njcr->JobBytes, b2),
281 edit_uint64(bps, b3),
282 njcr->JobErrors, njcr->max_bandwidth);
283 sendit(msg.c_str(), len, sp);
284 len = Mmsg(msg, " FilesExamined=%s\n",
285 edit_uint64(njcr->num_files_examined, b1));
286 sendit(msg.c_str(), len, sp);
287 if (njcr->JobFiles > 0) {
289 len = Mmsg(msg, " ProcessingFile=%s\n", njcr->last_fname);
291 sendit(msg.c_str(), len, sp);
295 if (njcr->store_bsock) {
296 len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
297 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
298 sendit(msg.c_str(), len, sp);
300 len = Mmsg(msg, _(" SDSocket=closed\n"));
301 sendit(msg.c_str(), len, sp);
307 static void list_running_jobs(STATUS_PKT *sp)
310 list_running_jobs_api(sp);
312 list_running_jobs_plain(sp);
316 static void list_terminated_jobs(STATUS_PKT *sp)
318 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
320 struct s_last_job *je;
324 msg = _("\nTerminated Jobs:\n");
325 sendit(msg, strlen(msg), sp);
328 if (last_jobs->size() == 0) {
329 if (!sp->api) sendit(_("====\n"), 5, sp);
332 lock_last_jobs_list();
334 msg = _(" JobId Level Files Bytes Status Finished Name \n");
335 sendit(msg, strlen(msg), sp);
336 msg = _("======================================================================\n");
337 sendit(msg, strlen(msg), sp);
339 foreach_dlist(je, last_jobs) {
340 char JobName[MAX_NAME_LENGTH];
341 const char *termstat;
344 bstrftime_nc(dt, sizeof(dt), je->end_time);
345 switch (je->JobType) {
348 bstrncpy(level, " ", sizeof(level));
351 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
355 switch (je->JobStatus) {
357 termstat = _("Created");
360 case JS_ErrorTerminated:
361 termstat = _("Error");
364 termstat = _("Diffs");
367 termstat = _("Cancel");
373 termstat = _("Other");
376 bstrncpy(JobName, je->Job, sizeof(JobName));
377 /* There are three periods after the Job name */
379 for (int i=0; i<3; i++) {
380 if ((p=strrchr(JobName, '.')) != NULL) {
385 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
388 edit_uint64_with_commas(je->JobFiles, b1),
389 edit_uint64_with_suffix(je->JobBytes, b2),
393 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
396 edit_uint64_with_commas(je->JobFiles, b1),
397 edit_uint64_with_suffix(je->JobBytes, b2),
401 sendit(buf, strlen(buf), sp);
403 if (!sp->api) sendit(_("====\n"), 5, sp);
404 unlock_last_jobs_list();
409 * Send to bsock (Director or Console)
411 static void sendit(const char *msg, int len, STATUS_PKT *sp)
414 BSOCK *user = sp->bs;
415 user->msg = check_pool_memory_size(user->msg, len+1);
416 memcpy(user->msg, msg, len+1);
417 user->msglen = len+1;
420 sp->callback(msg, len, sp->context);
425 * Status command from Director
427 int status_cmd(JCR *jcr)
429 BSOCK *user = jcr->dir_bsock;
434 sp.api = false; /* no API output */
437 user->signal(BNET_EOD);
442 * .status command from Director
444 int qstatus_cmd(JCR *jcr)
446 BSOCK *dir = jcr->dir_bsock;
453 cmd = get_memory(dir->msglen+1);
455 if (sscanf(dir->msg, qstatus, cmd) != 1) {
456 pm_strcpy(&jcr->errmsg, dir->msg);
457 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
458 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
459 dir->signal(BNET_EOD);
465 if (strcmp(cmd, "current") == 0) {
466 dir->fsend(OKqstatus, cmd);
468 if (njcr->JobId != 0) {
469 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
473 } else if (strcmp(cmd, "last") == 0) {
474 dir->fsend(OKqstatus, cmd);
475 if ((last_jobs) && (last_jobs->size() > 0)) {
476 job = (s_last_job*)last_jobs->last();
477 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
479 } else if (strcasecmp(cmd, "header") == 0) {
481 list_status_header(&sp);
482 } else if (strcasecmp(cmd, "running") == 0) {
484 list_running_jobs(&sp);
485 } else if (strcasecmp(cmd, "terminated") == 0) {
487 list_terminated_jobs(&sp);
489 pm_strcpy(&jcr->errmsg, dir->msg);
490 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
491 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
492 dir->signal(BNET_EOD);
497 dir->signal(BNET_EOD);
503 * Convert Job Level into a string
505 static const char *level_to_str(int level)
516 str = _("Incremental");
519 str = _("Differential");
524 case L_VERIFY_CATALOG:
525 str = _("Verify Catalog");
528 str = _("Init Catalog");
530 case L_VERIFY_VOLUME_TO_CATALOG:
531 str = _("Volume to Catalog");
533 case L_VERIFY_DISK_TO_CATALOG:
534 str = _("Disk to Catalog");
543 str = _("Unknown Job Level");
550 #if defined(HAVE_WIN32)
554 * Put message in Window List Box
556 char *bac_status(char *buf, int buf_len)
559 const char *termstat = _("Bacula Client: Idle");
560 struct s_last_job *job;
561 int stat = 0; /* Idle */
566 Dmsg0(1000, "Begin bac_status jcr loop.\n");
568 if (njcr->JobId != 0) {
570 termstat = _("Bacula Client: Running");
579 if (last_jobs->size() > 0) {
580 job = (struct s_last_job *)last_jobs->last();
581 stat = job->JobStatus;
582 switch (job->JobStatus) {
584 termstat = _("Bacula Client: Last Job Canceled");
586 case JS_ErrorTerminated:
588 termstat = _("Bacula Client: Last Job Failed");
592 termstat = _("Bacula Client: Last Job had Warnings");
597 Dmsg0(1000, "End bac_status jcr loop.\n");
601 bstrncpy(buf, termstat, buf_len);
606 #endif /* HAVE_WIN32 */