]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
Use %s when displaying bandwidth limits in FD status
[bacula/bacula] / bacula / src / filed / status.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2011 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 three of the GNU Affero 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 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
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  */
34
35 #include "bacula.h"
36 #include "filed.h"
37 #include "lib/status.h"
38
39 extern void *start_heap;
40
41 extern bool GetWindowsVersionString(char *buf, int maxsiz);
42
43
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);
50
51 /* Static variables */
52 static char qstatus[] = ".status %s\n";
53
54 static char OKqstatus[]   = "2000 OK .status\n";
55 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
56
57 #if defined(HAVE_WIN32)
58 static int privs = 0;
59 #endif
60 #ifdef WIN32_VSS
61 #include "vss.h"
62 #define VSS " VSS"
63 extern VSSClient *g_pVSSClient;
64 #else
65 #define VSS ""
66 #endif
67
68 /*
69  * General status generator
70  */
71 void output_status(STATUS_PKT *sp)
72 {
73    list_status_header(sp);
74    list_running_jobs(sp);
75    list_terminated_jobs(sp);
76 }
77
78 static void  list_status_header(STATUS_PKT *sp)
79 {
80    POOL_MEM msg(PM_MESSAGE);
81    char b1[32], b2[32], b3[32], b4[32], b5[35];
82    int len;
83    char dt[MAX_TIME_LENGTH];
84
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)
93    char buf[300];
94    if (GetWindowsVersionString(buf, sizeof(buf))) {
95       len = Mmsg(msg, "%s\n", buf);
96       sendit(msg.c_str(), len, sp);
97    }
98    if (debug_level > 0) {
99       if (!privs) {
100          privs = enable_backup_privileges(NULL, 1);
101       }
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",
112                  p_wunlink?"":"!",
113                  p_wmkdir?"":"!",
114                  p_GetFileAttributesA?"":"!",
115                  p_GetFileAttributesW?"":"!",
116                  p_GetFileAttributesExA?"":"!",
117                  p_GetFileAttributesExW?"":"!",
118                  p_SetFileAttributesA?"":"!",
119                  p_SetFileAttributesW?"":"!",
120                  p_BackupRead?"":"!",
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);
140    }
141 #endif
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 "
150                      "bwlimit=%lldkB/s\n"), sizeof(boffset_t), sizeof(size_t),
151               debug_level, get_trace(), me->max_bandwidth_per_job/1024);
152    sendit(msg.c_str(), len, sp);
153    if (debug_level > 0 && plugin_list->size() > 0) {
154       Plugin *plugin;
155       int len;
156       pm_strcpy(msg, "Plugin: ");
157       foreach_alist(plugin, plugin_list) {
158          len = pm_strcat(msg, plugin->file);
159          if (len > 80) {
160             pm_strcat(msg, "\n   ");
161          } else {
162             pm_strcat(msg, " ");
163          }
164       }
165       len = pm_strcat(msg, "\n");
166       sendit(msg.c_str(), len, sp);
167    }
168 }
169
170 static void  list_running_jobs_plain(STATUS_PKT *sp)
171 {
172    int sec, bps;
173    POOL_MEM msg(PM_MESSAGE);
174    char b1[32], b2[32], b3[32], b4[32];
175    int len;
176    bool found = false;
177    JCR *njcr;
178    char dt[MAX_TIME_LENGTH];
179    /*
180     * List running jobs
181     */
182    Dmsg0(1000, "Begin status jcr loop.\n");
183    len = Mmsg(msg, _("\nRunning Jobs:\n"));
184    sendit(msg.c_str(), len, sp);
185    const char *vss = "";
186 #ifdef WIN32_VSS
187    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
188       vss = "VSS ";
189    }
190 #endif
191    foreach_jcr(njcr) {
192       bstrftime_nc(dt, sizeof(dt), njcr->start_time);
193       if (njcr->JobId == 0) {
194          len = Mmsg(msg, _("Director connected at: %s\n"), dt);
195       } else {
196          len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
197                     njcr->JobId, njcr->Job);
198          sendit(msg.c_str(), len, sp);
199          len = Mmsg(msg, _("    %s%s %s Job started: %s\n"),
200                     vss, level_to_str(njcr->getJobLevel()), 
201                     job_type_to_str(njcr->getJobType()), dt);
202       }
203       sendit(msg.c_str(), len, sp);
204       if (njcr->JobId == 0) {
205          continue;
206       }
207       sec = time(NULL) - njcr->start_time;
208       if (sec <= 0) {
209          sec = 1;
210       }
211       bps = (int)(njcr->JobBytes / sec);
212       len = Mmsg(msg,  _("    Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"
213                          "    Bwlimit=%s\n"),
214            edit_uint64_with_commas(njcr->JobFiles, b1),
215            edit_uint64_with_commas(njcr->JobBytes, b2),
216            edit_uint64_with_commas(bps, b3),
217            njcr->JobErrors, edit_int64(njcr->max_bandwidth, b4));
218       sendit(msg.c_str(), len, sp);
219       len = Mmsg(msg, _("    Files Examined=%s\n"),
220            edit_uint64_with_commas(njcr->num_files_examined, b1));
221       sendit(msg.c_str(), len, sp);
222       if (njcr->JobFiles > 0) {
223          njcr->lock();
224          len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
225          njcr->unlock();
226          sendit(msg.c_str(), len, sp);
227       }
228
229       found = true;
230       if (njcr->store_bsock) {
231          len = Mmsg(msg, "    SDReadSeqNo=%" lld " fd=%d\n",
232              njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
233          sendit(msg.c_str(), len, sp);
234       } else {
235          len = Mmsg(msg, _("    SDSocket closed.\n"));
236          sendit(msg.c_str(), len, sp);
237       }
238    }
239    endeach_jcr(njcr);
240
241    if (!found) {
242       len = Mmsg(msg, _("No Jobs running.\n"));
243       sendit(msg.c_str(), len, sp);
244    }
245    sendit(_("====\n"), 5, sp);
246 }
247
248 static void  list_running_jobs_api(STATUS_PKT *sp)
249 {
250    int sec, bps;
251    POOL_MEM msg(PM_MESSAGE);
252    char b1[32], b2[32], b3[32], b4[32];
253    int len;
254    bool found = false;
255    JCR *njcr;
256    char dt[MAX_TIME_LENGTH];
257    /*
258     * List running jobs for Bat/Bweb (simple to parse)
259     */
260    int vss = 0;
261 #ifdef WIN32_VSS
262    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
263       vss = 1;
264    }
265 #endif
266    foreach_jcr(njcr) {
267       bstrutime(dt, sizeof(dt), njcr->start_time);
268       if (njcr->JobId == 0) {
269          len = Mmsg(msg, "DirectorConnected=%s\n", dt);
270       } else {
271          len = Mmsg(msg, "JobId=%d\n Job=%s\n",
272                     njcr->JobId, njcr->Job);
273          sendit(msg.c_str(), len, sp);
274          len = Mmsg(msg," VSS=%d\n Level=%c\n JobType=%c\n JobStarted=%s\n",
275                     vss, njcr->getJobLevel(), 
276                     njcr->getJobType(), dt);
277       }
278       sendit(msg.c_str(), len, sp);
279       if (njcr->JobId == 0) {
280          continue;
281       }
282       sec = time(NULL) - njcr->start_time;
283       if (sec <= 0) {
284          sec = 1;
285       }
286       bps = (int)(njcr->JobBytes / sec);
287       len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
288                       " Bwlimit=%s\n",
289                  edit_uint64(njcr->JobFiles, b1),
290                  edit_uint64(njcr->JobBytes, b2),
291                  edit_uint64(bps, b3),
292                  njcr->JobErrors, edit_int64(njcr->max_bandwidth, b4));
293       sendit(msg.c_str(), len, sp);
294       len = Mmsg(msg, " Files Examined=%s\n",
295            edit_uint64(njcr->num_files_examined, b1));
296       sendit(msg.c_str(), len, sp);
297       if (njcr->JobFiles > 0) {
298          njcr->lock();
299          len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
300          njcr->unlock();
301          sendit(msg.c_str(), len, sp);
302       }
303
304       found = true;
305       if (njcr->store_bsock) {
306          len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
307              njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
308          sendit(msg.c_str(), len, sp);
309       } else {
310          len = Mmsg(msg, _(" SDSocket=closed\n"));
311          sendit(msg.c_str(), len, sp);
312       }
313    }
314    endeach_jcr(njcr);
315 }
316
317 static void  list_running_jobs(STATUS_PKT *sp)
318 {
319    if (sp->api) {
320       list_running_jobs_api(sp);
321    } else {
322       list_running_jobs_plain(sp);
323    }
324 }  
325
326 static void list_terminated_jobs(STATUS_PKT *sp)
327 {
328    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
329    char level[10];
330    struct s_last_job *je;
331    const char *msg;
332
333    if (!sp->api) {
334       msg =  _("\nTerminated Jobs:\n");
335       sendit(msg, strlen(msg), sp);
336    }
337
338    if (last_jobs->size() == 0) {
339       if (!sp->api) sendit(_("====\n"), 5, sp);
340       return;
341    }
342    lock_last_jobs_list();
343    if (!sp->api) {
344       msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
345       sendit(msg, strlen(msg), sp);
346       msg = _("======================================================================\n");
347       sendit(msg, strlen(msg), sp);
348    }
349    foreach_dlist(je, last_jobs) {
350       char JobName[MAX_NAME_LENGTH];
351       const char *termstat;
352       char buf[1000];
353
354       bstrftime_nc(dt, sizeof(dt), je->end_time);
355       switch (je->JobType) {
356       case JT_ADMIN:
357       case JT_RESTORE:
358          bstrncpy(level, "    ", sizeof(level));
359          break;
360       default:
361          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
362          level[4] = 0;
363          break;
364       }
365       switch (je->JobStatus) {
366       case JS_Created:
367          termstat = _("Created");
368          break;
369       case JS_FatalError:
370       case JS_ErrorTerminated:
371          termstat = _("Error");
372          break;
373       case JS_Differences:
374          termstat = _("Diffs");
375          break;
376       case JS_Canceled:
377          termstat = _("Cancel");
378          break;
379       case JS_Terminated:
380          termstat = _("OK");
381          break;
382       default:
383          termstat = _("Other");
384          break;
385       }
386       bstrncpy(JobName, je->Job, sizeof(JobName));
387       /* There are three periods after the Job name */
388       char *p;
389       for (int i=0; i<3; i++) {
390          if ((p=strrchr(JobName, '.')) != NULL) {
391             *p = 0;
392          }
393       }
394       if (sp->api) {
395          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
396             je->JobId,
397             level,
398             edit_uint64_with_commas(je->JobFiles, b1),
399             edit_uint64_with_suffix(je->JobBytes, b2),
400             termstat,
401             dt, JobName);
402       } else {
403          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
404             je->JobId,
405             level,
406             edit_uint64_with_commas(je->JobFiles, b1),
407             edit_uint64_with_suffix(je->JobBytes, b2),
408             termstat,
409             dt, JobName);
410       }
411       sendit(buf, strlen(buf), sp);
412    }
413    if (!sp->api) sendit(_("====\n"), 5, sp);
414    unlock_last_jobs_list();
415 }
416
417
418 /*
419  * Send to bsock (Director or Console)
420  */
421 static void sendit(const char *msg, int len, STATUS_PKT *sp)          
422 {
423    if (sp->bs) {
424       BSOCK *user = sp->bs;
425       user->msg = check_pool_memory_size(user->msg, len+1);
426       memcpy(user->msg, msg, len+1);
427       user->msglen = len+1;
428       user->send();
429    } else {
430       sp->callback(msg, len, sp->context);
431    }
432 }
433
434 /*
435  * Status command from Director
436  */
437 int status_cmd(JCR *jcr)
438 {
439    BSOCK *user = jcr->dir_bsock;
440    STATUS_PKT sp;
441
442    user->fsend("\n");
443    sp.bs = user;
444    sp.api = false;                         /* no API output */
445    output_status(&sp);
446
447    user->signal(BNET_EOD);
448    return 1;
449 }
450
451 /*
452  * .status command from Director
453  */
454 int qstatus_cmd(JCR *jcr)
455 {
456    BSOCK *dir = jcr->dir_bsock;
457    POOLMEM *cmd;
458    JCR *njcr;
459    s_last_job* job;
460    STATUS_PKT sp;
461
462    sp.bs = dir;
463    cmd = get_memory(dir->msglen+1);
464
465    if (sscanf(dir->msg, qstatus, cmd) != 1) {
466       pm_strcpy(&jcr->errmsg, dir->msg);
467       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
468       dir->fsend(_("2900 Bad .status command, missing argument.\n"));
469       dir->signal(BNET_EOD);
470       free_memory(cmd);
471       return 0;
472    }
473    unbash_spaces(cmd);
474
475    if (strcmp(cmd, "current") == 0) {
476       dir->fsend(OKqstatus, cmd);
477       foreach_jcr(njcr) {
478          if (njcr->JobId != 0) {
479             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
480          }
481       }
482       endeach_jcr(njcr);
483    } else if (strcmp(cmd, "last") == 0) {
484       dir->fsend(OKqstatus, cmd);
485       if ((last_jobs) && (last_jobs->size() > 0)) {
486          job = (s_last_job*)last_jobs->last();
487          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
488       }
489    } else if (strcasecmp(cmd, "header") == 0) {
490        sp.api = true;
491        list_status_header(&sp);
492    } else if (strcasecmp(cmd, "running") == 0) {
493        sp.api = true;
494        list_running_jobs(&sp);
495    } else if (strcasecmp(cmd, "terminated") == 0) {
496        sp.api = true;
497        list_terminated_jobs(&sp);
498    } else {
499       pm_strcpy(&jcr->errmsg, dir->msg);
500       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
501       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
502       dir->signal(BNET_EOD);
503       free_memory(cmd);
504       return 0;
505    }
506
507    dir->signal(BNET_EOD);
508    free_memory(cmd);
509    return 1;
510 }
511
512 /*
513  * Convert Job Level into a string
514  */
515 static const char *level_to_str(int level)
516 {
517    const char *str;
518
519    switch (level) {
520    case L_BASE:
521       str = _("Base");
522    case L_FULL:
523       str = _("Full");
524       break;
525    case L_INCREMENTAL:
526       str = _("Incremental");
527       break;
528    case L_DIFFERENTIAL:
529       str = _("Differential");
530       break;
531    case L_SINCE:
532       str = _("Since");
533       break;
534    case L_VERIFY_CATALOG:
535       str = _("Verify Catalog");
536       break;
537    case L_VERIFY_INIT:
538       str = _("Init Catalog");
539       break;
540    case L_VERIFY_VOLUME_TO_CATALOG:
541       str = _("Volume to Catalog");
542       break;
543    case L_VERIFY_DISK_TO_CATALOG:
544       str = _("Disk to Catalog");
545       break;
546    case L_VERIFY_DATA:
547       str = _("Data");
548       break;
549    case L_NONE:
550       str = " ";
551       break;
552    default:
553       str = _("Unknown Job Level");
554       break;
555    }
556    return str;
557 }
558
559
560 #if defined(HAVE_WIN32)
561 int bacstat = 0;
562
563 /*
564  * Put message in Window List Box
565  */
566 char *bac_status(char *buf, int buf_len)
567 {
568    JCR *njcr;
569    const char *termstat = _("Bacula Client: Idle");
570    struct s_last_job *job;
571    int stat = 0;                      /* Idle */
572
573    if (!last_jobs) {
574       goto done;
575    }
576    Dmsg0(1000, "Begin bac_status jcr loop.\n");
577    foreach_jcr(njcr) {
578       if (njcr->JobId != 0) {
579          stat = JS_Running;
580          termstat = _("Bacula Client: Running");
581          break;
582       }
583    }
584    endeach_jcr(njcr);
585
586    if (stat != 0) {
587       goto done;
588    }
589    if (last_jobs->size() > 0) {
590       job = (struct s_last_job *)last_jobs->last();
591       stat = job->JobStatus;
592       switch (job->JobStatus) {
593       case JS_Canceled:
594          termstat = _("Bacula Client: Last Job Canceled");
595          break;
596       case JS_ErrorTerminated:
597       case JS_FatalError:
598          termstat = _("Bacula Client: Last Job Failed");
599          break;
600       default:
601          if (job->Errors) {
602             termstat = _("Bacula Client: Last Job had Warnings");
603          }
604          break;
605       }
606    }
607    Dmsg0(1000, "End bac_status jcr loop.\n");
608 done:
609    bacstat = stat;
610    if (buf) {
611       bstrncpy(buf, termstat, buf_len);
612    }
613    return buf;
614 }
615
616 #endif /* HAVE_WIN32 */