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 "
142 "bwlimit=%lldkb/s\n"), sizeof(boffset_t), sizeof(size_t),
143 debug_level, get_trace(), me->max_bandwidth_per_job/1024);
144 sendit(msg.c_str(), len, sp);
145 if (debug_level > 0 && plugin_list->size() > 0) {
148 pm_strcpy(msg, "Plugin: ");
149 foreach_alist(plugin, plugin_list) {
150 len = pm_strcat(msg, plugin->file);
152 pm_strcat(msg, "\n ");
157 len = pm_strcat(msg, "\n");
158 sendit(msg.c_str(), len, sp);
162 static void list_running_jobs_plain(STATUS_PKT *sp)
165 POOL_MEM msg(PM_MESSAGE);
166 char b1[32], b2[32], b3[32];
170 char dt[MAX_TIME_LENGTH];
174 Dmsg0(1000, "Begin status jcr loop.\n");
175 len = Mmsg(msg, _("\nRunning Jobs:\n"));
176 sendit(msg.c_str(), len, sp);
177 const char *vss = "";
179 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
184 bstrftime_nc(dt, sizeof(dt), njcr->start_time);
185 if (njcr->JobId == 0) {
186 len = Mmsg(msg, _("Director connected at: %s\n"), dt);
188 len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
189 njcr->JobId, njcr->Job);
190 sendit(msg.c_str(), len, sp);
191 len = Mmsg(msg, _(" %s%s %s Job started: %s\n"),
192 vss, level_to_str(njcr->getJobLevel()),
193 job_type_to_str(njcr->getJobType()), dt);
195 sendit(msg.c_str(), len, sp);
196 if (njcr->JobId == 0) {
199 sec = time(NULL) - njcr->start_time;
203 bps = (int)(njcr->JobBytes / sec);
204 len = Mmsg(msg, _(" Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"),
205 edit_uint64_with_commas(njcr->JobFiles, b1),
206 edit_uint64_with_commas(njcr->JobBytes, b2),
207 edit_uint64_with_commas(bps, b3),
209 sendit(msg.c_str(), len, sp);
210 len = Mmsg(msg, _(" Files Examined=%s\n"),
211 edit_uint64_with_commas(njcr->num_files_examined, b1));
212 sendit(msg.c_str(), len, sp);
213 if (njcr->JobFiles > 0) {
215 len = Mmsg(msg, _(" Processing file: %s\n"), njcr->last_fname);
217 sendit(msg.c_str(), len, sp);
221 if (njcr->store_bsock) {
222 len = Mmsg(msg, " SDReadSeqNo=%" lld " fd=%d\n",
223 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
224 sendit(msg.c_str(), len, sp);
226 len = Mmsg(msg, _(" SDSocket closed.\n"));
227 sendit(msg.c_str(), len, sp);
233 len = Mmsg(msg, _("No Jobs running.\n"));
234 sendit(msg.c_str(), len, sp);
236 sendit(_("====\n"), 5, sp);
239 static void list_running_jobs_api(STATUS_PKT *sp)
242 POOL_MEM msg(PM_MESSAGE);
243 char b1[32], b2[32], b3[32];
247 char dt[MAX_TIME_LENGTH];
249 * List running jobs for Bat/Bweb (simple to parse)
253 if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
258 bstrutime(dt, sizeof(dt), njcr->start_time);
259 if (njcr->JobId == 0) {
260 len = Mmsg(msg, "DirectorConnected=%s\n", dt);
262 len = Mmsg(msg, "JobId=%d\n Job=%s\n",
263 njcr->JobId, njcr->Job);
264 sendit(msg.c_str(), len, sp);
265 len = Mmsg(msg," VSS=%d\n Level=%c\n JobType=%c\n JobStarted=%s\n",
266 vss, njcr->getJobLevel(),
267 njcr->getJobType(), dt);
269 sendit(msg.c_str(), len, sp);
270 if (njcr->JobId == 0) {
273 sec = time(NULL) - njcr->start_time;
277 bps = (int)(njcr->JobBytes / sec);
278 len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
280 edit_uint64(njcr->JobFiles, b1),
281 edit_uint64(njcr->JobBytes, b2),
282 edit_uint64(bps, b3),
283 njcr->JobErrors, njcr->max_bandwidth);
284 sendit(msg.c_str(), len, sp);
285 len = Mmsg(msg, " Files Examined=%s\n",
286 edit_uint64(njcr->num_files_examined, b1));
287 sendit(msg.c_str(), len, sp);
288 if (njcr->JobFiles > 0) {
290 len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
292 sendit(msg.c_str(), len, sp);
296 if (njcr->store_bsock) {
297 len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
298 njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
299 sendit(msg.c_str(), len, sp);
301 len = Mmsg(msg, _(" SDSocket=closed\n"));
302 sendit(msg.c_str(), len, sp);
308 static void list_running_jobs(STATUS_PKT *sp)
311 list_running_jobs_api(sp);
313 list_running_jobs_plain(sp);
317 static void list_terminated_jobs(STATUS_PKT *sp)
319 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
321 struct s_last_job *je;
325 msg = _("\nTerminated Jobs:\n");
326 sendit(msg, strlen(msg), sp);
329 if (last_jobs->size() == 0) {
330 if (!sp->api) sendit(_("====\n"), 5, sp);
333 lock_last_jobs_list();
335 msg = _(" JobId Level Files Bytes Status Finished Name \n");
336 sendit(msg, strlen(msg), sp);
337 msg = _("======================================================================\n");
338 sendit(msg, strlen(msg), sp);
340 foreach_dlist(je, last_jobs) {
341 char JobName[MAX_NAME_LENGTH];
342 const char *termstat;
345 bstrftime_nc(dt, sizeof(dt), je->end_time);
346 switch (je->JobType) {
349 bstrncpy(level, " ", sizeof(level));
352 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
356 switch (je->JobStatus) {
358 termstat = _("Created");
361 case JS_ErrorTerminated:
362 termstat = _("Error");
365 termstat = _("Diffs");
368 termstat = _("Cancel");
374 termstat = _("Other");
377 bstrncpy(JobName, je->Job, sizeof(JobName));
378 /* There are three periods after the Job name */
380 for (int i=0; i<3; i++) {
381 if ((p=strrchr(JobName, '.')) != NULL) {
386 bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
389 edit_uint64_with_commas(je->JobFiles, b1),
390 edit_uint64_with_suffix(je->JobBytes, b2),
394 bsnprintf(buf, sizeof(buf), _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
397 edit_uint64_with_commas(je->JobFiles, b1),
398 edit_uint64_with_suffix(je->JobBytes, b2),
402 sendit(buf, strlen(buf), sp);
404 if (!sp->api) sendit(_("====\n"), 5, sp);
405 unlock_last_jobs_list();
410 * Send to bsock (Director or Console)
412 static void sendit(const char *msg, int len, STATUS_PKT *sp)
415 BSOCK *user = sp->bs;
416 user->msg = check_pool_memory_size(user->msg, len+1);
417 memcpy(user->msg, msg, len+1);
418 user->msglen = len+1;
421 sp->callback(msg, len, sp->context);
426 * Status command from Director
428 int status_cmd(JCR *jcr)
430 BSOCK *user = jcr->dir_bsock;
435 sp.api = false; /* no API output */
438 user->signal(BNET_EOD);
443 * .status command from Director
445 int qstatus_cmd(JCR *jcr)
447 BSOCK *dir = jcr->dir_bsock;
454 cmd = get_memory(dir->msglen+1);
456 if (sscanf(dir->msg, qstatus, cmd) != 1) {
457 pm_strcpy(&jcr->errmsg, dir->msg);
458 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
459 dir->fsend(_("2900 Bad .status command, missing argument.\n"));
460 dir->signal(BNET_EOD);
466 if (strcmp(cmd, "current") == 0) {
467 dir->fsend(OKqstatus, cmd);
469 if (njcr->JobId != 0) {
470 dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
474 } else if (strcmp(cmd, "last") == 0) {
475 dir->fsend(OKqstatus, cmd);
476 if ((last_jobs) && (last_jobs->size() > 0)) {
477 job = (s_last_job*)last_jobs->last();
478 dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
480 } else if (strcasecmp(cmd, "header") == 0) {
482 list_status_header(&sp);
483 } else if (strcasecmp(cmd, "running") == 0) {
485 list_running_jobs(&sp);
486 } else if (strcasecmp(cmd, "terminated") == 0) {
488 list_terminated_jobs(&sp);
490 pm_strcpy(&jcr->errmsg, dir->msg);
491 Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
492 dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
493 dir->signal(BNET_EOD);
498 dir->signal(BNET_EOD);
504 * Convert Job Level into a string
506 static const char *level_to_str(int level)
517 str = _("Incremental");
520 str = _("Differential");
525 case L_VERIFY_CATALOG:
526 str = _("Verify Catalog");
529 str = _("Init Catalog");
531 case L_VERIFY_VOLUME_TO_CATALOG:
532 str = _("Volume to Catalog");
534 case L_VERIFY_DISK_TO_CATALOG:
535 str = _("Disk to Catalog");
544 str = _("Unknown Job Level");
551 #if defined(HAVE_WIN32)
555 * Put message in Window List Box
557 char *bac_status(char *buf, int buf_len)
560 const char *termstat = _("Bacula Client: Idle");
561 struct s_last_job *job;
562 int stat = 0; /* Idle */
567 Dmsg0(1000, "Begin bac_status jcr loop.\n");
569 if (njcr->JobId != 0) {
571 termstat = _("Bacula Client: Running");
580 if (last_jobs->size() > 0) {
581 job = (struct s_last_job *)last_jobs->last();
582 stat = job->JobStatus;
583 switch (job->JobStatus) {
585 termstat = _("Bacula Client: Last Job Canceled");
587 case JS_ErrorTerminated:
589 termstat = _("Bacula Client: Last Job Failed");
593 termstat = _("Bacula Client: Last Job had Warnings");
598 Dmsg0(1000, "End bac_status jcr loop.\n");
602 bstrncpy(buf, termstat, buf_len);
607 #endif /* HAVE_WIN32 */