]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
Enhance FD status to have level
[bacula/bacula] / bacula / src / filed / status.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2009 Free Software Foundation Europe e.V.
5
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 two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
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.
17
18    You should have received a copy of the GNU 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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *  Bacula File Daemon Status routines
30  *
31  *    Kern Sibbald, August MMI
32  *
33  *   Version $Id$
34  *
35  */
36
37 #include "bacula.h"
38 #include "filed.h"
39 #include "lib/status.h"
40
41 extern void *start_heap;
42
43 /* Forward referenced functions */
44 static void  list_terminated_jobs(STATUS_PKT *sp);
45 static void  list_running_jobs(STATUS_PKT *sp);
46 static void  list_status_header(STATUS_PKT *sp);
47 static void sendit(const char *msg, int len, STATUS_PKT *sp);
48 static const char *level_to_str(int level);
49
50 /* Static variables */
51 static char qstatus[] = ".status %s\n";
52
53 static char OKqstatus[]   = "2000 OK .status\n";
54 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
55
56 #if defined(HAVE_WIN32)
57 static int privs = 0;
58 #endif
59 #ifdef WIN32_VSS
60 #include "vss.h"
61 #define VSS " VSS"
62 extern VSSClient *g_pVSSClient;
63 #else
64 #define VSS ""
65 #endif
66
67 /*
68  * General status generator
69  */
70 void output_status(STATUS_PKT *sp)
71 {
72    list_status_header(sp);
73    list_running_jobs(sp);
74    list_terminated_jobs(sp);
75 }
76
77 static void  list_status_header(STATUS_PKT *sp)
78 {
79    POOL_MEM msg(PM_MESSAGE);
80    char b1[32], b2[32], b3[32], b4[32], b5[35];
81    int len;
82    char dt[MAX_TIME_LENGTH];
83
84    len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s %s\n"), 
85               my_name, VERSION, BDATE, VSS, HOST_OS, DISTNAME, DISTVER);
86    sendit(msg.c_str(), len, sp);
87    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
88    len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
89         dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
90    sendit(msg.c_str(), len, sp);
91 #if defined(HAVE_WIN32)
92    if (debug_level > 0) {
93       if (!privs) {
94          privs = enable_backup_privileges(NULL, 1);
95       }
96       len = Mmsg(msg, "VSS %s, Priv 0x%x\n", g_pVSSClient?"enabled":"disabled", privs);
97       sendit(msg.c_str(), len, sp);
98       len = Mmsg(msg, "APIs=%sOPT,%sATP,%sLPV,%sCFA,%sCFW,\n",
99                  p_OpenProcessToken?"":"!",
100                  p_AdjustTokenPrivileges?"":"!",
101                  p_LookupPrivilegeValue?"":"!",
102                  p_CreateFileA?"":"!",
103                  p_CreateFileW?"":"!");
104       sendit(msg.c_str(), len, sp);
105       len = Mmsg(msg, " %sWUL,%sWMKD,%sGFAA,%sGFAW,%sGFAEA,%sGFAEW,%sSFAA,%sSFAW,%sBR,%sBW,%sSPSP,\n",
106                  p_wunlink?"":"!",
107                  p_wmkdir?"":"!",
108                  p_GetFileAttributesA?"":"!",
109                  p_GetFileAttributesW?"":"!",
110                  p_GetFileAttributesExA?"":"!",
111                  p_GetFileAttributesExW?"":"!",
112                  p_SetFileAttributesA?"":"!",
113                  p_SetFileAttributesW?"":"!",
114                  p_BackupRead?"":"!",
115                  p_BackupWrite?"":"!",
116                  p_SetProcessShutdownParameters?"":"!");
117       sendit(msg.c_str(), len, sp);
118       len = Mmsg(msg, " %sWC2MB,%sMB2WC,%sFFFA,%sFFFW,%sFNFA,%sFNFW,%sSCDA,%sSCDW,\n",
119                  p_WideCharToMultiByte?"":"!",
120                  p_MultiByteToWideChar?"":"!",
121                  p_FindFirstFileA?"":"!",
122                  p_FindFirstFileW?"":"!",
123                  p_FindNextFileA?"":"!",
124                  p_FindNextFileW?"":"!",
125                  p_SetCurrentDirectoryA?"":"!",
126                  p_SetCurrentDirectoryW?"":"!");
127       sendit(msg.c_str(), len, sp);
128       len = Mmsg(msg, " %sGCDA,%sGCDW,%sGVPNW,%sGVNFVMPW\n",  
129                  p_GetCurrentDirectoryA?"":"!",
130                  p_GetCurrentDirectoryW?"":"!",
131                  p_GetVolumePathNameW?"":"!",
132                  p_GetVolumeNameForVolumeMountPointW?"":"!");
133      sendit(msg.c_str(), len, sp);
134    }
135 #endif
136    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
137          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
138          edit_uint64_with_commas(sm_bytes, b2),
139          edit_uint64_with_commas(sm_max_bytes, b3),
140          edit_uint64_with_commas(sm_buffers, b4),
141          edit_uint64_with_commas(sm_max_buffers, b5));
142    sendit(msg.c_str(), len, sp);
143    len = Mmsg(msg, _(" Sizeof: boffset_t=%d size_t=%d debug=%d trace=%d\n"),
144          sizeof(boffset_t), sizeof(size_t), debug_level, get_trace());
145    sendit(msg.c_str(), len, sp);
146    if (debug_level > 0 && plugin_list->size() > 0) {
147       Plugin *plugin;
148       int len;
149       pm_strcpy(msg, "Plugin: ");
150       foreach_alist(plugin, plugin_list) {
151          len = pm_strcat(msg, plugin->file);
152          if (len > 80) {
153             pm_strcat(msg, "\n   ");
154          } else {
155             pm_strcat(msg, " ");
156          }
157       }
158       len = pm_strcat(msg, "\n");
159       sendit(msg.c_str(), len, sp);
160    }
161 }
162
163 static void  list_running_jobs(STATUS_PKT *sp)
164 {
165    int sec, bps;
166    POOL_MEM msg(PM_MESSAGE);
167    char b1[32], b2[32], b3[32];
168    int len;
169    bool found = false;
170    JCR *njcr;
171    char dt[MAX_TIME_LENGTH];
172    /*
173     * List running jobs
174     */
175    Dmsg0(1000, "Begin status jcr loop.\n");
176    if (!sp->api) {
177       len = Mmsg(msg, _("\nRunning Jobs:\n"));
178       sendit(msg.c_str(), len, sp);
179    }
180    const char *vss = "";
181 #ifdef WIN32_VSS
182    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
183       vss = "VSS ";
184    }
185 #endif
186    foreach_jcr(njcr) {
187       bstrftime_nc(dt, sizeof(dt), njcr->start_time);
188       if (njcr->JobId == 0) {
189          len = Mmsg(msg, _("Director connected at: %s\n"), dt);
190       } else {
191          len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
192                     njcr->JobId, njcr->Job);
193          sendit(msg.c_str(), len, sp);
194          len = Mmsg(msg, _("    %s%s %s Job started: %s\n"),
195                     vss, level_to_str(njcr->getJobLevel()), 
196                     job_type_to_str(njcr->getJobType()), dt);
197       }
198       sendit(msg.c_str(), len, sp);
199       if (njcr->JobId == 0) {
200          continue;
201       }
202       sec = time(NULL) - njcr->start_time;
203       if (sec <= 0) {
204          sec = 1;
205       }
206       bps = (int)(njcr->JobBytes / sec);
207       len = Mmsg(msg,  _("    Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"),
208            edit_uint64_with_commas(njcr->JobFiles, b1),
209            edit_uint64_with_commas(njcr->JobBytes, b2),
210            edit_uint64_with_commas(bps, b3),
211            njcr->JobErrors);
212       sendit(msg.c_str(), len, sp);
213       len = Mmsg(msg, _("    Files Examined=%s\n"),
214            edit_uint64_with_commas(njcr->num_files_examined, b1));
215       sendit(msg.c_str(), len, sp);
216       if (njcr->JobFiles > 0) {
217          njcr->lock();
218          len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
219          njcr->unlock();
220          sendit(msg.c_str(), len, sp);
221       }
222
223       found = true;
224       if (njcr->store_bsock) {
225          len = Mmsg(msg, "    SDReadSeqNo=%" lld " fd=%d\n",
226              njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
227          sendit(msg.c_str(), len, sp);
228       } else {
229          len = Mmsg(msg, _("    SDSocket closed.\n"));
230          sendit(msg.c_str(), len, sp);
231       }
232    }
233    endeach_jcr(njcr);
234
235    if (!sp->api) {
236       if (!found) {
237          len = Mmsg(msg, _("No Jobs running.\n"));
238          sendit(msg.c_str(), len, sp);
239       }
240       sendit(_("====\n"), 5, sp);
241    }
242 }
243   
244
245 static void list_terminated_jobs(STATUS_PKT *sp)
246 {
247    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
248    char level[10];
249    struct s_last_job *je;
250    const char *msg;
251
252    if (!sp->api) {
253       msg =  _("\nTerminated Jobs:\n");
254       sendit(msg, strlen(msg), sp);
255    }
256
257    if (last_jobs->size() == 0) {
258       if (!sp->api) sendit(_("====\n"), 5, sp);
259       return;
260    }
261    lock_last_jobs_list();
262    if (!sp->api) {
263       msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
264       sendit(msg, strlen(msg), sp);
265       msg = _("======================================================================\n");
266       sendit(msg, strlen(msg), sp);
267    }
268    foreach_dlist(je, last_jobs) {
269       char JobName[MAX_NAME_LENGTH];
270       const char *termstat;
271       char buf[1000];
272
273       bstrftime_nc(dt, sizeof(dt), je->end_time);
274       switch (je->JobType) {
275       case JT_ADMIN:
276       case JT_RESTORE:
277          bstrncpy(level, "    ", sizeof(level));
278          break;
279       default:
280          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
281          level[4] = 0;
282          break;
283       }
284       switch (je->JobStatus) {
285       case JS_Created:
286          termstat = _("Created");
287          break;
288       case JS_FatalError:
289       case JS_ErrorTerminated:
290          termstat = _("Error");
291          break;
292       case JS_Differences:
293          termstat = _("Diffs");
294          break;
295       case JS_Canceled:
296          termstat = _("Cancel");
297          break;
298       case JS_Terminated:
299          termstat = _("OK");
300          break;
301       default:
302          termstat = _("Other");
303          break;
304       }
305       bstrncpy(JobName, je->Job, sizeof(JobName));
306       /* There are three periods after the Job name */
307       char *p;
308       for (int i=0; i<3; i++) {
309          if ((p=strrchr(JobName, '.')) != NULL) {
310             *p = 0;
311          }
312       }
313       if (sp->api) {
314          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
315             je->JobId,
316             level,
317             edit_uint64_with_commas(je->JobFiles, b1),
318             edit_uint64_with_suffix(je->JobBytes, b2),
319             termstat,
320             dt, JobName);
321       } else {
322          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
323             je->JobId,
324             level,
325             edit_uint64_with_commas(je->JobFiles, b1),
326             edit_uint64_with_suffix(je->JobBytes, b2),
327             termstat,
328             dt, JobName);
329       }
330       sendit(buf, strlen(buf), sp);
331    }
332    if (!sp->api) sendit(_("====\n"), 5, sp);
333    unlock_last_jobs_list();
334 }
335
336
337 /*
338  * Send to bsock (Director or Console)
339  */
340 static void sendit(const char *msg, int len, STATUS_PKT *sp)          
341 {
342    if (sp->bs) {
343       BSOCK *user = sp->bs;
344       user->msg = check_pool_memory_size(user->msg, len+1);
345       memcpy(user->msg, msg, len+1);
346       user->msglen = len+1;
347       user->send();
348    } else {
349       sp->callback(msg, len, sp->context);
350    }
351 }
352
353 /*
354  * Status command from Director
355  */
356 int status_cmd(JCR *jcr)
357 {
358    BSOCK *user = jcr->dir_bsock;
359    STATUS_PKT sp;
360
361    user->fsend("\n");
362    sp.bs = user;
363    sp.api = false;                         /* no API output */
364    output_status(&sp);
365
366    user->signal(BNET_EOD);
367    return 1;
368 }
369
370 /*
371  * .status command from Director
372  */
373 int qstatus_cmd(JCR *jcr)
374 {
375    BSOCK *dir = jcr->dir_bsock;
376    POOLMEM *cmd;
377    JCR *njcr;
378    s_last_job* job;
379    STATUS_PKT sp;
380
381    sp.bs = dir;
382    cmd = get_memory(dir->msglen+1);
383
384    if (sscanf(dir->msg, qstatus, cmd) != 1) {
385       pm_strcpy(&jcr->errmsg, dir->msg);
386       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
387       dir->fsend(_("2900 Bad .status command, missing argument.\n"));
388       dir->signal(BNET_EOD);
389       free_memory(cmd);
390       return 0;
391    }
392    unbash_spaces(cmd);
393
394    if (strcmp(cmd, "current") == 0) {
395       dir->fsend(OKqstatus, cmd);
396       foreach_jcr(njcr) {
397          if (njcr->JobId != 0) {
398             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
399          }
400       }
401       endeach_jcr(njcr);
402    } else if (strcmp(cmd, "last") == 0) {
403       dir->fsend(OKqstatus, cmd);
404       if ((last_jobs) && (last_jobs->size() > 0)) {
405          job = (s_last_job*)last_jobs->last();
406          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
407       }
408    } else if (strcasecmp(cmd, "header") == 0) {
409        sp.api = true;
410        list_status_header(&sp);
411    } else if (strcasecmp(cmd, "running") == 0) {
412        sp.api = true;
413        list_running_jobs(&sp);
414    } else if (strcasecmp(cmd, "terminated") == 0) {
415        sp.api = true;
416        list_terminated_jobs(&sp);
417    } else {
418       pm_strcpy(&jcr->errmsg, dir->msg);
419       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
420       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
421       dir->signal(BNET_EOD);
422       free_memory(cmd);
423       return 0;
424    }
425
426    dir->signal(BNET_EOD);
427    free_memory(cmd);
428    return 1;
429 }
430
431 /*
432  * Convert Job Level into a string
433  */
434 static const char *level_to_str(int level)
435 {
436    const char *str;
437
438    switch (level) {
439    case L_BASE:
440       str = _("Base");
441    case L_FULL:
442       str = _("Full");
443       break;
444    case L_INCREMENTAL:
445       str = _("Incremental");
446       break;
447    case L_DIFFERENTIAL:
448       str = _("Differential");
449       break;
450    case L_SINCE:
451       str = _("Since");
452       break;
453    case L_VERIFY_CATALOG:
454       str = _("Verify Catalog");
455       break;
456    case L_VERIFY_INIT:
457       str = _("Init Catalog");
458       break;
459    case L_VERIFY_VOLUME_TO_CATALOG:
460       str = _("Volume to Catalog");
461       break;
462    case L_VERIFY_DISK_TO_CATALOG:
463       str = _("Disk to Catalog");
464       break;
465    case L_VERIFY_DATA:
466       str = _("Data");
467       break;
468    case L_NONE:
469       str = " ";
470       break;
471    default:
472       str = _("Unknown Job Level");
473       break;
474    }
475    return str;
476 }
477
478
479 #if defined(HAVE_WIN32)
480 int bacstat = 0;
481
482 /*
483  * Put message in Window List Box
484  */
485 char *bac_status(char *buf, int buf_len)
486 {
487    JCR *njcr;
488    const char *termstat = _("Bacula Client: Idle");
489    struct s_last_job *job;
490    int stat = 0;                      /* Idle */
491
492    if (!last_jobs) {
493       goto done;
494    }
495    Dmsg0(1000, "Begin bac_status jcr loop.\n");
496    foreach_jcr(njcr) {
497       if (njcr->JobId != 0) {
498          stat = JS_Running;
499          termstat = _("Bacula Client: Running");
500          break;
501       }
502    }
503    endeach_jcr(njcr);
504
505    if (stat != 0) {
506       goto done;
507    }
508    if (last_jobs->size() > 0) {
509       job = (struct s_last_job *)last_jobs->last();
510       stat = job->JobStatus;
511       switch (job->JobStatus) {
512       case JS_Canceled:
513          termstat = _("Bacula Client: Last Job Canceled");
514          break;
515       case JS_ErrorTerminated:
516       case JS_FatalError:
517          termstat = _("Bacula Client: Last Job Failed");
518          break;
519       default:
520          if (job->Errors) {
521             termstat = _("Bacula Client: Last Job had Warnings");
522          }
523          break;
524       }
525    }
526    Dmsg0(1000, "End bac_status jcr loop.\n");
527 done:
528    bacstat = stat;
529    if (buf) {
530       bstrncpy(buf, termstat, buf_len);
531    }
532    return buf;
533 }
534
535 #endif /* HAVE_WIN32 */