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);
284 len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
286 edit_uint64(njcr->JobFiles, b1),
287 edit_uint64(njcr->JobBytes, b2),
288 edit_uint64(bps, b3),
289 njcr->JobErrors, njcr->max_bandwidth);
290 sendit(msg.c_str(), len, sp);
291 len = Mmsg(msg, " Files Examined=%s\n",
292 edit_uint64(njcr->num_files_examined, b1));
293 sendit(msg.c_str(), len, sp);
294 if (njcr->JobFiles > 0) {
296 len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
298 sendit(msg.c_str(), len, sp);
301 if (njcr->store_bsock) {
302 len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
303 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
304 sendit(msg.c_str(), len, sp);
306 len = Mmsg(msg, _(" SDSocket=closed\n"));
307 sendit(msg.c_str(), len, sp);
313 static void list_running_jobs(STATUS_PKT *sp)
316 list_running_jobs_api(sp);
318 list_running_jobs_plain(sp);
322 static void list_terminated_jobs(STATUS_PKT *sp)
324 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
326 struct s_last_job *je;
330 msg = _("\nTerminated Jobs:\n");
331 sendit(msg, strlen(msg), sp);
334 if (last_jobs->size() == 0) {
335 if (!sp->api) sendit(_("====\n"), 5, sp);
338 lock_last_jobs_list();
340 msg = _(" JobId Level Files Bytes Status Finished Name \n");
341 sendit(msg, strlen(msg), sp);
342 msg = _("======================================================================\n");
343 sendit(msg, strlen(msg), sp);
345 foreach_dlist(je, last_jobs) {
346 char JobName[MAX_NAME_LENGTH];
347 const char *termstat;
350 bstrftime_nc(dt, sizeof(dt), je->end_time);
351 switch (je->JobType) {
354 bstrncpy(level, " ", sizeof(level));
357 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
361 switch (je->JobStatus) {
363 termstat = _("Created");
366 case JS_ErrorTerminated:
367 termstat = _("Error");
370 termstat = _("Diffs");
373 termstat = _("Cancel");
379 termstat = _("Other");
382 bstrncpy(JobName, je->Job, sizeof(JobName));
383 /* There are three periods after the Job name */
385 for (int i=0; i<3; i++) {
386 if ((p=strrchr(JobName, '.')) != NULL) {
391 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
394 edit_uint64_with_commas(je->JobFiles, b1),
395 edit_uint64_with_suffix(je->JobBytes, b2),
399 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
402 edit_uint64_with_commas(je->JobFiles, b1),
403 edit_uint64_with_suffix(je->JobBytes, b2),
407 sendit(buf, strlen(buf), sp);
409 if (!sp->api) sendit(_("====\n"), 5, sp);
410 unlock_last_jobs_list();
415 * Send to bsock (Director or Console)
417 static void sendit(const char *msg, int len, STATUS_PKT *sp)
420 BSOCK *user = sp->bs;
421 user->msg = check_pool_memory_size(user->msg, len+1);
422 memcpy(user->msg, msg, len+1);
423 user->msglen = len+1;
426 sp->callback(msg, len, sp->context);
431 * Status command from Director
433 int status_cmd(JCR *jcr)
435 BSOCK *user = jcr->dir_bsock;
440 sp.api = false; /* no API output */
443 user->signal(BNET_EOD);
448 * .status command from Director
450 int qstatus_cmd(JCR *jcr)
452 BSOCK *dir = jcr->dir_bsock;
459 cmd = get_memory(dir->msglen+1);
461 if (sscanf(dir->msg, qstatus, cmd) != 1) {
462 pm_strcpy(&jcr->errmsg, dir->msg);
463 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
464 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
465 dir->signal(BNET_EOD);
471 if (strcmp(cmd, "current") == 0) {
472 dir->fsend(OKqstatus, cmd);
474 if (njcr->JobId != 0) {
475 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
479 } else if (strcmp(cmd, "last") == 0) {
480 dir->fsend(OKqstatus, cmd);
481 if ((last_jobs) && (last_jobs->size() > 0)) {
482 job = (s_last_job*)last_jobs->last();
483 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
485 } else if (strcasecmp(cmd, "header") == 0) {
487 list_status_header(&sp);
488 } else if (strcasecmp(cmd, "running") == 0) {
490 list_running_jobs(&sp);
491 } else if (strcasecmp(cmd, "terminated") == 0) {
493 list_terminated_jobs(&sp);
495 pm_strcpy(&jcr->errmsg, dir->msg);
496 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
497 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
498 dir->signal(BNET_EOD);
503 dir->signal(BNET_EOD);
509 * Convert Job Level into a string
511 static const char *level_to_str(int level)
522 str = _("Incremental");
525 str = _("Differential");
530 case L_VERIFY_CATALOG:
531 str = _("Verify Catalog");
534 str = _("Init Catalog");
536 case L_VERIFY_VOLUME_TO_CATALOG:
537 str = _("Volume to Catalog");
539 case L_VERIFY_DISK_TO_CATALOG:
540 str = _("Disk to Catalog");
549 str = _("Unknown Job Level");
556 #if defined(HAVE_WIN32)
560 * Put message in Window List Box
562 char *bac_status(char *buf, int buf_len)
565 const char *termstat = _("Bacula Client: Idle");
566 struct s_last_job *job;
567 int stat = 0; /* Idle */
572 Dmsg0(1000, "Begin bac_status jcr loop.\n");
574 if (njcr->JobId != 0) {
576 termstat = _("Bacula Client: Running");
585 if (last_jobs->size() > 0) {
586 job = (struct s_last_job *)last_jobs->last();
587 stat = job->JobStatus;
588 switch (job->JobStatus) {
590 termstat = _("Bacula Client: Last Job Canceled");
592 case JS_ErrorTerminated:
594 termstat = _("Bacula Client: Last Job Failed");
598 termstat = _("Bacula Client: Last Job had Warnings");
603 Dmsg0(1000, "End bac_status jcr loop.\n");
607 bstrncpy(buf, termstat, buf_len);
612 #endif /* HAVE_WIN32 */