]> 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 "
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) {
146       Plugin *plugin;
147       int len;
148       pm_strcpy(msg, "Plugin: ");
149       foreach_alist(plugin, plugin_list) {
150          len = pm_strcat(msg, plugin->file);
151          if (len > 80) {
152             pm_strcat(msg, "\n   ");
153          } else {
154             pm_strcat(msg, " ");
155          }
156       }
157       len = pm_strcat(msg, "\n");
158       sendit(msg.c_str(), len, sp);
159    }
160 }
161
162 static void  list_running_jobs_plain(STATUS_PKT *sp)
163 {
164    int sec, bps;
165    POOL_MEM msg(PM_MESSAGE);
166    char b1[32], b2[32], b3[32];
167    int len;
168    bool found = false;
169    JCR *njcr;
170    char dt[MAX_TIME_LENGTH];
171    /*
172     * List running jobs
173     */
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 = "";
178 #ifdef WIN32_VSS
179    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
180       vss = "VSS ";
181    }
182 #endif
183    foreach_jcr(njcr) {
184       bstrftime_nc(dt, sizeof(dt), njcr->start_time);
185       if (njcr->JobId == 0) {
186          len = Mmsg(msg, _("Director connected at: %s\n"), dt);
187       } else {
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);
194       }
195       sendit(msg.c_str(), len, sp);
196       if (njcr->JobId == 0) {
197          continue;
198       }
199       sec = time(NULL) - njcr->start_time;
200       if (sec <= 0) {
201          sec = 1;
202       }
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),
208            njcr->JobErrors);
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) {
214          njcr->lock();
215          len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
216          njcr->unlock();
217          sendit(msg.c_str(), len, sp);
218       }
219
220       found = true;
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);
225       } else {
226          len = Mmsg(msg, _("    SDSocket closed.\n"));
227          sendit(msg.c_str(), len, sp);
228       }
229    }
230    endeach_jcr(njcr);
231
232    if (!found) {
233       len = Mmsg(msg, _("No Jobs running.\n"));
234       sendit(msg.c_str(), len, sp);
235    }
236    sendit(_("====\n"), 5, sp);
237 }
238
239 static void  list_running_jobs_api(STATUS_PKT *sp)
240 {
241    int sec, bps;
242    POOL_MEM msg(PM_MESSAGE);
243    char b1[32], b2[32], b3[32];
244    int len;
245    bool found = false;
246    JCR *njcr;
247    char dt[MAX_TIME_LENGTH];
248    /*
249     * List running jobs for Bat/Bweb (simple to parse)
250     */
251    int vss = 0;
252 #ifdef WIN32_VSS
253    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
254       vss = 1;
255    }
256 #endif
257    foreach_jcr(njcr) {
258       bstrutime(dt, sizeof(dt), njcr->start_time);
259       if (njcr->JobId == 0) {
260          len = Mmsg(msg, "DirectorConnected=%s\n", dt);
261       } else {
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);
268       }
269       sendit(msg.c_str(), len, sp);
270       if (njcr->JobId == 0) {
271          continue;
272       }
273       sec = time(NULL) - njcr->start_time;
274       if (sec <= 0) {
275          sec = 1;
276       }
277       bps = (int)(njcr->JobBytes / sec);
278       len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n"
279                  " Bwlimit=%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) {
289          njcr->lock();
290          len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
291          njcr->unlock();
292          sendit(msg.c_str(), len, sp);
293       }
294
295       found = true;
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);
300       } else {
301          len = Mmsg(msg, _(" SDSocket=closed\n"));
302          sendit(msg.c_str(), len, sp);
303       }
304    }
305    endeach_jcr(njcr);
306 }
307
308 static void  list_running_jobs(STATUS_PKT *sp)
309 {
310    if (sp->api) {
311       list_running_jobs_api(sp);
312    } else {
313       list_running_jobs_plain(sp);
314    }
315 }  
316
317 static void list_terminated_jobs(STATUS_PKT *sp)
318 {
319    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
320    char level[10];
321    struct s_last_job *je;
322    const char *msg;
323
324    if (!sp->api) {
325       msg =  _("\nTerminated Jobs:\n");
326       sendit(msg, strlen(msg), sp);
327    }
328
329    if (last_jobs->size() == 0) {
330       if (!sp->api) sendit(_("====\n"), 5, sp);
331       return;
332    }
333    lock_last_jobs_list();
334    if (!sp->api) {
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);
339    }
340    foreach_dlist(je, last_jobs) {
341       char JobName[MAX_NAME_LENGTH];
342       const char *termstat;
343       char buf[1000];
344
345       bstrftime_nc(dt, sizeof(dt), je->end_time);
346       switch (je->JobType) {
347       case JT_ADMIN:
348       case JT_RESTORE:
349          bstrncpy(level, "    ", sizeof(level));
350          break;
351       default:
352          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
353          level[4] = 0;
354          break;
355       }
356       switch (je->JobStatus) {
357       case JS_Created:
358          termstat = _("Created");
359          break;
360       case JS_FatalError:
361       case JS_ErrorTerminated:
362          termstat = _("Error");
363          break;
364       case JS_Differences:
365          termstat = _("Diffs");
366          break;
367       case JS_Canceled:
368          termstat = _("Cancel");
369          break;
370       case JS_Terminated:
371          termstat = _("OK");
372          break;
373       default:
374          termstat = _("Other");
375          break;
376       }
377       bstrncpy(JobName, je->Job, sizeof(JobName));
378       /* There are three periods after the Job name */
379       char *p;
380       for (int i=0; i<3; i++) {
381          if ((p=strrchr(JobName, '.')) != NULL) {
382             *p = 0;
383          }
384       }
385       if (sp->api) {
386          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
387             je->JobId,
388             level,
389             edit_uint64_with_commas(je->JobFiles, b1),
390             edit_uint64_with_suffix(je->JobBytes, b2),
391             termstat,
392             dt, JobName);
393       } else {
394          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %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       }
402       sendit(buf, strlen(buf), sp);
403    }
404    if (!sp->api) sendit(_("====\n"), 5, sp);
405    unlock_last_jobs_list();
406 }
407
408
409 /*
410  * Send to bsock (Director or Console)
411  */
412 static void sendit(const char *msg, int len, STATUS_PKT *sp)          
413 {
414    if (sp->bs) {
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;
419       user->send();
420    } else {
421       sp->callback(msg, len, sp->context);
422    }
423 }
424
425 /*
426  * Status command from Director
427  */
428 int status_cmd(JCR *jcr)
429 {
430    BSOCK *user = jcr->dir_bsock;
431    STATUS_PKT sp;
432
433    user->fsend("\n");
434    sp.bs = user;
435    sp.api = false;                         /* no API output */
436    output_status(&sp);
437
438    user->signal(BNET_EOD);
439    return 1;
440 }
441
442 /*
443  * .status command from Director
444  */
445 int qstatus_cmd(JCR *jcr)
446 {
447    BSOCK *dir = jcr->dir_bsock;
448    POOLMEM *cmd;
449    JCR *njcr;
450    s_last_job* job;
451    STATUS_PKT sp;
452
453    sp.bs = dir;
454    cmd = get_memory(dir->msglen+1);
455
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);
461       free_memory(cmd);
462       return 0;
463    }
464    unbash_spaces(cmd);
465
466    if (strcmp(cmd, "current") == 0) {
467       dir->fsend(OKqstatus, cmd);
468       foreach_jcr(njcr) {
469          if (njcr->JobId != 0) {
470             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
471          }
472       }
473       endeach_jcr(njcr);
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);
479       }
480    } else if (strcasecmp(cmd, "header") == 0) {
481        sp.api = true;
482        list_status_header(&sp);
483    } else if (strcasecmp(cmd, "running") == 0) {
484        sp.api = true;
485        list_running_jobs(&sp);
486    } else if (strcasecmp(cmd, "terminated") == 0) {
487        sp.api = true;
488        list_terminated_jobs(&sp);
489    } else {
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);
494       free_memory(cmd);
495       return 0;
496    }
497
498    dir->signal(BNET_EOD);
499    free_memory(cmd);
500    return 1;
501 }
502
503 /*
504  * Convert Job Level into a string
505  */
506 static const char *level_to_str(int level)
507 {
508    const char *str;
509
510    switch (level) {
511    case L_BASE:
512       str = _("Base");
513    case L_FULL:
514       str = _("Full");
515       break;
516    case L_INCREMENTAL:
517       str = _("Incremental");
518       break;
519    case L_DIFFERENTIAL:
520       str = _("Differential");
521       break;
522    case L_SINCE:
523       str = _("Since");
524       break;
525    case L_VERIFY_CATALOG:
526       str = _("Verify Catalog");
527       break;
528    case L_VERIFY_INIT:
529       str = _("Init Catalog");
530       break;
531    case L_VERIFY_VOLUME_TO_CATALOG:
532       str = _("Volume to Catalog");
533       break;
534    case L_VERIFY_DISK_TO_CATALOG:
535       str = _("Disk to Catalog");
536       break;
537    case L_VERIFY_DATA:
538       str = _("Data");
539       break;
540    case L_NONE:
541       str = " ";
542       break;
543    default:
544       str = _("Unknown Job Level");
545       break;
546    }
547    return str;
548 }
549
550
551 #if defined(HAVE_WIN32)
552 int bacstat = 0;
553
554 /*
555  * Put message in Window List Box
556  */
557 char *bac_status(char *buf, int buf_len)
558 {
559    JCR *njcr;
560    const char *termstat = _("Bacula Client: Idle");
561    struct s_last_job *job;
562    int stat = 0;                      /* Idle */
563
564    if (!last_jobs) {
565       goto done;
566    }
567    Dmsg0(1000, "Begin bac_status jcr loop.\n");
568    foreach_jcr(njcr) {
569       if (njcr->JobId != 0) {
570          stat = JS_Running;
571          termstat = _("Bacula Client: Running");
572          break;
573       }
574    }
575    endeach_jcr(njcr);
576
577    if (stat != 0) {
578       goto done;
579    }
580    if (last_jobs->size() > 0) {
581       job = (struct s_last_job *)last_jobs->last();
582       stat = job->JobStatus;
583       switch (job->JobStatus) {
584       case JS_Canceled:
585          termstat = _("Bacula Client: Last Job Canceled");
586          break;
587       case JS_ErrorTerminated:
588       case JS_FatalError:
589          termstat = _("Bacula Client: Last Job Failed");
590          break;
591       default:
592          if (job->Errors) {
593             termstat = _("Bacula Client: Last Job had Warnings");
594          }
595          break;
596       }
597    }
598    Dmsg0(1000, "End bac_status jcr loop.\n");
599 done:
600    bacstat = stat;
601    if (buf) {
602       bstrncpy(buf, termstat, buf_len);
603    }
604    return buf;
605 }
606
607 #endif /* HAVE_WIN32 */