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