]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
Fix kb/s to kB/s in FD output
[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];
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            edit_uint64_with_commas(njcr->JobFiles, b1),
214            edit_uint64_with_commas(njcr->JobBytes, b2),
215            edit_uint64_with_commas(bps, b3),
216            njcr->JobErrors);
217       sendit(msg.c_str(), len, sp);
218       len = Mmsg(msg, _("    Files Examined=%s\n"),
219            edit_uint64_with_commas(njcr->num_files_examined, b1));
220       sendit(msg.c_str(), len, sp);
221       if (njcr->JobFiles > 0) {
222          njcr->lock();
223          len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
224          njcr->unlock();
225          sendit(msg.c_str(), len, sp);
226       }
227
228       found = true;
229       if (njcr->store_bsock) {
230          len = Mmsg(msg, "    SDReadSeqNo=%" lld " fd=%d\n",
231              njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
232          sendit(msg.c_str(), len, sp);
233       } else {
234          len = Mmsg(msg, _("    SDSocket closed.\n"));
235          sendit(msg.c_str(), len, sp);
236       }
237    }
238    endeach_jcr(njcr);
239
240    if (!found) {
241       len = Mmsg(msg, _("No Jobs running.\n"));
242       sendit(msg.c_str(), len, sp);
243    }
244    sendit(_("====\n"), 5, sp);
245 }
246
247 static void  list_running_jobs_api(STATUS_PKT *sp)
248 {
249    int sec, bps;
250    POOL_MEM msg(PM_MESSAGE);
251    char b1[32], b2[32], b3[32];
252    int len;
253    bool found = false;
254    JCR *njcr;
255    char dt[MAX_TIME_LENGTH];
256    /*
257     * List running jobs for Bat/Bweb (simple to parse)
258     */
259    int vss = 0;
260 #ifdef WIN32_VSS
261    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
262       vss = 1;
263    }
264 #endif
265    foreach_jcr(njcr) {
266       bstrutime(dt, sizeof(dt), njcr->start_time);
267       if (njcr->JobId == 0) {
268          len = Mmsg(msg, "DirectorConnected=%s\n", dt);
269       } else {
270          len = Mmsg(msg, "JobId=%d\n Job=%s\n",
271                     njcr->JobId, njcr->Job);
272          sendit(msg.c_str(), len, sp);
273          len = Mmsg(msg," VSS=%d\n Level=%c\n JobType=%c\n JobStarted=%s\n",
274                     vss, njcr->getJobLevel(), 
275                     njcr->getJobType(), dt);
276       }
277       sendit(msg.c_str(), len, sp);
278       if (njcr->JobId == 0) {
279          continue;
280       }
281       sec = time(NULL) - njcr->start_time;
282       if (sec <= 0) {
283          sec = 1;
284       }
285       bps = (int)(njcr->JobBytes / sec);
286       len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
287                  " Bwlimit=%d\n",
288                  edit_uint64(njcr->JobFiles, b1),
289                  edit_uint64(njcr->JobBytes, b2),
290                  edit_uint64(bps, b3),
291                  njcr->JobErrors, njcr->max_bandwidth);
292       sendit(msg.c_str(), len, sp);
293       len = Mmsg(msg, " Files Examined=%s\n",
294            edit_uint64(njcr->num_files_examined, b1));
295       sendit(msg.c_str(), len, sp);
296       if (njcr->JobFiles > 0) {
297          njcr->lock();
298          len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
299          njcr->unlock();
300          sendit(msg.c_str(), len, sp);
301       }
302
303       found = true;
304       if (njcr->store_bsock) {
305          len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
306              njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
307          sendit(msg.c_str(), len, sp);
308       } else {
309          len = Mmsg(msg, _(" SDSocket=closed\n"));
310          sendit(msg.c_str(), len, sp);
311       }
312    }
313    endeach_jcr(njcr);
314 }
315
316 static void  list_running_jobs(STATUS_PKT *sp)
317 {
318    if (sp->api) {
319       list_running_jobs_api(sp);
320    } else {
321       list_running_jobs_plain(sp);
322    }
323 }  
324
325 static void list_terminated_jobs(STATUS_PKT *sp)
326 {
327    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
328    char level[10];
329    struct s_last_job *je;
330    const char *msg;
331
332    if (!sp->api) {
333       msg =  _("\nTerminated Jobs:\n");
334       sendit(msg, strlen(msg), sp);
335    }
336
337    if (last_jobs->size() == 0) {
338       if (!sp->api) sendit(_("====\n"), 5, sp);
339       return;
340    }
341    lock_last_jobs_list();
342    if (!sp->api) {
343       msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
344       sendit(msg, strlen(msg), sp);
345       msg = _("======================================================================\n");
346       sendit(msg, strlen(msg), sp);
347    }
348    foreach_dlist(je, last_jobs) {
349       char JobName[MAX_NAME_LENGTH];
350       const char *termstat;
351       char buf[1000];
352
353       bstrftime_nc(dt, sizeof(dt), je->end_time);
354       switch (je->JobType) {
355       case JT_ADMIN:
356       case JT_RESTORE:
357          bstrncpy(level, "    ", sizeof(level));
358          break;
359       default:
360          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
361          level[4] = 0;
362          break;
363       }
364       switch (je->JobStatus) {
365       case JS_Created:
366          termstat = _("Created");
367          break;
368       case JS_FatalError:
369       case JS_ErrorTerminated:
370          termstat = _("Error");
371          break;
372       case JS_Differences:
373          termstat = _("Diffs");
374          break;
375       case JS_Canceled:
376          termstat = _("Cancel");
377          break;
378       case JS_Terminated:
379          termstat = _("OK");
380          break;
381       default:
382          termstat = _("Other");
383          break;
384       }
385       bstrncpy(JobName, je->Job, sizeof(JobName));
386       /* There are three periods after the Job name */
387       char *p;
388       for (int i=0; i<3; i++) {
389          if ((p=strrchr(JobName, '.')) != NULL) {
390             *p = 0;
391          }
392       }
393       if (sp->api) {
394          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
395             je->JobId,
396             level,
397             edit_uint64_with_commas(je->JobFiles, b1),
398             edit_uint64_with_suffix(je->JobBytes, b2),
399             termstat,
400             dt, JobName);
401       } else {
402          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
403             je->JobId,
404             level,
405             edit_uint64_with_commas(je->JobFiles, b1),
406             edit_uint64_with_suffix(je->JobBytes, b2),
407             termstat,
408             dt, JobName);
409       }
410       sendit(buf, strlen(buf), sp);
411    }
412    if (!sp->api) sendit(_("====\n"), 5, sp);
413    unlock_last_jobs_list();
414 }
415
416
417 /*
418  * Send to bsock (Director or Console)
419  */
420 static void sendit(const char *msg, int len, STATUS_PKT *sp)          
421 {
422    if (sp->bs) {
423       BSOCK *user = sp->bs;
424       user->msg = check_pool_memory_size(user->msg, len+1);
425       memcpy(user->msg, msg, len+1);
426       user->msglen = len+1;
427       user->send();
428    } else {
429       sp->callback(msg, len, sp->context);
430    }
431 }
432
433 /*
434  * Status command from Director
435  */
436 int status_cmd(JCR *jcr)
437 {
438    BSOCK *user = jcr->dir_bsock;
439    STATUS_PKT sp;
440
441    user->fsend("\n");
442    sp.bs = user;
443    sp.api = false;                         /* no API output */
444    output_status(&sp);
445
446    user->signal(BNET_EOD);
447    return 1;
448 }
449
450 /*
451  * .status command from Director
452  */
453 int qstatus_cmd(JCR *jcr)
454 {
455    BSOCK *dir = jcr->dir_bsock;
456    POOLMEM *cmd;
457    JCR *njcr;
458    s_last_job* job;
459    STATUS_PKT sp;
460
461    sp.bs = dir;
462    cmd = get_memory(dir->msglen+1);
463
464    if (sscanf(dir->msg, qstatus, cmd) != 1) {
465       pm_strcpy(&jcr->errmsg, dir->msg);
466       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
467       dir->fsend(_("2900 Bad .status command, missing argument.\n"));
468       dir->signal(BNET_EOD);
469       free_memory(cmd);
470       return 0;
471    }
472    unbash_spaces(cmd);
473
474    if (strcmp(cmd, "current") == 0) {
475       dir->fsend(OKqstatus, cmd);
476       foreach_jcr(njcr) {
477          if (njcr->JobId != 0) {
478             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
479          }
480       }
481       endeach_jcr(njcr);
482    } else if (strcmp(cmd, "last") == 0) {
483       dir->fsend(OKqstatus, cmd);
484       if ((last_jobs) && (last_jobs->size() > 0)) {
485          job = (s_last_job*)last_jobs->last();
486          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
487       }
488    } else if (strcasecmp(cmd, "header") == 0) {
489        sp.api = true;
490        list_status_header(&sp);
491    } else if (strcasecmp(cmd, "running") == 0) {
492        sp.api = true;
493        list_running_jobs(&sp);
494    } else if (strcasecmp(cmd, "terminated") == 0) {
495        sp.api = true;
496        list_terminated_jobs(&sp);
497    } else {
498       pm_strcpy(&jcr->errmsg, dir->msg);
499       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
500       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
501       dir->signal(BNET_EOD);
502       free_memory(cmd);
503       return 0;
504    }
505
506    dir->signal(BNET_EOD);
507    free_memory(cmd);
508    return 1;
509 }
510
511 /*
512  * Convert Job Level into a string
513  */
514 static const char *level_to_str(int level)
515 {
516    const char *str;
517
518    switch (level) {
519    case L_BASE:
520       str = _("Base");
521    case L_FULL:
522       str = _("Full");
523       break;
524    case L_INCREMENTAL:
525       str = _("Incremental");
526       break;
527    case L_DIFFERENTIAL:
528       str = _("Differential");
529       break;
530    case L_SINCE:
531       str = _("Since");
532       break;
533    case L_VERIFY_CATALOG:
534       str = _("Verify Catalog");
535       break;
536    case L_VERIFY_INIT:
537       str = _("Init Catalog");
538       break;
539    case L_VERIFY_VOLUME_TO_CATALOG:
540       str = _("Volume to Catalog");
541       break;
542    case L_VERIFY_DISK_TO_CATALOG:
543       str = _("Disk to Catalog");
544       break;
545    case L_VERIFY_DATA:
546       str = _("Data");
547       break;
548    case L_NONE:
549       str = " ";
550       break;
551    default:
552       str = _("Unknown Job Level");
553       break;
554    }
555    return str;
556 }
557
558
559 #if defined(HAVE_WIN32)
560 int bacstat = 0;
561
562 /*
563  * Put message in Window List Box
564  */
565 char *bac_status(char *buf, int buf_len)
566 {
567    JCR *njcr;
568    const char *termstat = _("Bacula Client: Idle");
569    struct s_last_job *job;
570    int stat = 0;                      /* Idle */
571
572    if (!last_jobs) {
573       goto done;
574    }
575    Dmsg0(1000, "Begin bac_status jcr loop.\n");
576    foreach_jcr(njcr) {
577       if (njcr->JobId != 0) {
578          stat = JS_Running;
579          termstat = _("Bacula Client: Running");
580          break;
581       }
582    }
583    endeach_jcr(njcr);
584
585    if (stat != 0) {
586       goto done;
587    }
588    if (last_jobs->size() > 0) {
589       job = (struct s_last_job *)last_jobs->last();
590       stat = job->JobStatus;
591       switch (job->JobStatus) {
592       case JS_Canceled:
593          termstat = _("Bacula Client: Last Job Canceled");
594          break;
595       case JS_ErrorTerminated:
596       case JS_FatalError:
597          termstat = _("Bacula Client: Last Job Failed");
598          break;
599       default:
600          if (job->Errors) {
601             termstat = _("Bacula Client: Last Job had Warnings");
602          }
603          break;
604       }
605    }
606    Dmsg0(1000, "End bac_status jcr loop.\n");
607 done:
608    bacstat = stat;
609    if (buf) {
610       bstrncpy(buf, termstat, buf_len);
611    }
612    return buf;
613 }
614
615 #endif /* HAVE_WIN32 */