]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
kes Attempt to correct problems with restores with autochangers and
[bacula/bacula] / bacula / src / filed / status.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2008 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 John Walker.
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 }
147
148 static void  list_running_jobs(STATUS_PKT *sp)
149 {
150    int sec, bps;
151    POOL_MEM msg(PM_MESSAGE);
152    char b1[32], b2[32], b3[32];
153    int len;
154    bool found = false;
155    JCR *njcr;
156    char dt[MAX_TIME_LENGTH];
157    /*
158     * List running jobs
159     */
160    Dmsg0(1000, "Begin status jcr loop.\n");
161    if (!sp->api) {
162       len = Mmsg(msg, _("\nRunning Jobs:\n"));
163       sendit(msg.c_str(), len, sp);
164    }
165    const char *vss = "";
166 #ifdef WIN32_VSS
167    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
168       vss = "VSS ";
169    }
170 #endif
171    foreach_jcr(njcr) {
172       bstrftime_nc(dt, sizeof(dt), njcr->start_time);
173       if (njcr->JobId == 0) {
174          len = Mmsg(msg, _("Director connected at: %s\n"), dt);
175       } else {
176          len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
177                     njcr->JobId, njcr->Job);
178          sendit(msg.c_str(), len, sp);
179          len = Mmsg(msg, _("    %s%s Job started: %s\n"),
180                     vss, job_type_to_str(njcr->JobType), dt);
181       }
182       sendit(msg.c_str(), len, sp);
183       if (njcr->JobId == 0) {
184          continue;
185       }
186       sec = time(NULL) - njcr->start_time;
187       if (sec <= 0) {
188          sec = 1;
189       }
190       bps = (int)(njcr->JobBytes / sec);
191       len = Mmsg(msg,  _("    Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"),
192            edit_uint64_with_commas(njcr->JobFiles, b1),
193            edit_uint64_with_commas(njcr->JobBytes, b2),
194            edit_uint64_with_commas(bps, b3),
195            njcr->JobErrors);
196       sendit(msg.c_str(), len, sp);
197       len = Mmsg(msg, _("    Files Examined=%s\n"),
198            edit_uint64_with_commas(njcr->num_files_examined, b1));
199       sendit(msg.c_str(), len, sp);
200       if (njcr->JobFiles > 0) {
201          njcr->lock();
202          len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
203          njcr->unlock();
204          sendit(msg.c_str(), len, sp);
205       }
206
207       found = true;
208       if (njcr->store_bsock) {
209          len = Mmsg(msg, "    SDReadSeqNo=%" lld " fd=%d\n",
210              njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
211          sendit(msg.c_str(), len, sp);
212       } else {
213          len = Mmsg(msg, _("    SDSocket closed.\n"));
214          sendit(msg.c_str(), len, sp);
215       }
216    }
217    endeach_jcr(njcr);
218
219    if (!sp->api) {
220       if (!found) {
221          len = Mmsg(msg, _("No Jobs running.\n"));
222          sendit(msg.c_str(), len, sp);
223       }
224       sendit(_("====\n"), 5, sp);
225    }
226 }
227   
228
229 static void list_terminated_jobs(STATUS_PKT *sp)
230 {
231    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
232    char level[10];
233    struct s_last_job *je;
234    const char *msg;
235
236    if (!sp->api) {
237       msg =  _("\nTerminated Jobs:\n");
238       sendit(msg, strlen(msg), sp);
239    }
240
241    if (last_jobs->size() == 0) {
242       if (!sp->api) sendit(_("====\n"), 5, sp);
243       return;
244    }
245    lock_last_jobs_list();
246    if (!sp->api) {
247       msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
248       sendit(msg, strlen(msg), sp);
249       msg = _("======================================================================\n");
250       sendit(msg, strlen(msg), sp);
251    }
252    foreach_dlist(je, last_jobs) {
253       char JobName[MAX_NAME_LENGTH];
254       const char *termstat;
255       char buf[1000];
256
257       bstrftime_nc(dt, sizeof(dt), je->end_time);
258       switch (je->JobType) {
259       case JT_ADMIN:
260       case JT_RESTORE:
261          bstrncpy(level, "    ", sizeof(level));
262          break;
263       default:
264          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
265          level[4] = 0;
266          break;
267       }
268       switch (je->JobStatus) {
269       case JS_Created:
270          termstat = _("Created");
271          break;
272       case JS_FatalError:
273       case JS_ErrorTerminated:
274          termstat = _("Error");
275          break;
276       case JS_Differences:
277          termstat = _("Diffs");
278          break;
279       case JS_Canceled:
280          termstat = _("Cancel");
281          break;
282       case JS_Terminated:
283          termstat = _("OK");
284          break;
285       default:
286          termstat = _("Other");
287          break;
288       }
289       bstrncpy(JobName, je->Job, sizeof(JobName));
290       /* There are three periods after the Job name */
291       char *p;
292       for (int i=0; i<3; i++) {
293          if ((p=strrchr(JobName, '.')) != NULL) {
294             *p = 0;
295          }
296       }
297       if (sp->api) {
298          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
299             je->JobId,
300             level,
301             edit_uint64_with_commas(je->JobFiles, b1),
302             edit_uint64_with_suffix(je->JobBytes, b2),
303             termstat,
304             dt, JobName);
305       } else {
306          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
307             je->JobId,
308             level,
309             edit_uint64_with_commas(je->JobFiles, b1),
310             edit_uint64_with_suffix(je->JobBytes, b2),
311             termstat,
312             dt, JobName);
313       }
314       sendit(buf, strlen(buf), sp);
315    }
316    if (!sp->api) sendit(_("====\n"), 5, sp);
317    unlock_last_jobs_list();
318 }
319
320
321 /*
322  * Send to bsock (Director or Console)
323  */
324 static void sendit(const char *msg, int len, STATUS_PKT *sp)          
325 {
326    if (sp->bs) {
327       BSOCK *user = sp->bs;
328       user->msg = check_pool_memory_size(user->msg, len+1);
329       memcpy(user->msg, msg, len+1);
330       user->msglen = len+1;
331       user->send();
332    } else {
333       sp->callback(msg, len, sp->context);
334    }
335 }
336
337 /*
338  * Status command from Director
339  */
340 int status_cmd(JCR *jcr)
341 {
342    BSOCK *user = jcr->dir_bsock;
343    STATUS_PKT sp;
344
345    user->fsend("\n");
346    sp.bs = user;
347    sp.api = false;                         /* no API output */
348    output_status(&sp);
349
350    user->signal(BNET_EOD);
351    return 1;
352 }
353
354 /*
355  * .status command from Director
356  */
357 int qstatus_cmd(JCR *jcr)
358 {
359    BSOCK *dir = jcr->dir_bsock;
360    POOLMEM *cmd;
361    JCR *njcr;
362    s_last_job* job;
363    STATUS_PKT sp;
364
365    sp.bs = dir;
366    cmd = get_memory(dir->msglen+1);
367
368    if (sscanf(dir->msg, qstatus, cmd) != 1) {
369       pm_strcpy(&jcr->errmsg, dir->msg);
370       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
371       dir->fsend(_("2900 Bad .status command, missing argument.\n"));
372       dir->signal(BNET_EOD);
373       free_memory(cmd);
374       return 0;
375    }
376    unbash_spaces(cmd);
377
378    if (strcmp(cmd, "current") == 0) {
379       dir->fsend(OKqstatus, cmd);
380       foreach_jcr(njcr) {
381          if (njcr->JobId != 0) {
382             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
383          }
384       }
385       endeach_jcr(njcr);
386    } else if (strcmp(cmd, "last") == 0) {
387       dir->fsend(OKqstatus, cmd);
388       if ((last_jobs) && (last_jobs->size() > 0)) {
389          job = (s_last_job*)last_jobs->last();
390          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
391       }
392    } else if (strcasecmp(cmd, "header") == 0) {
393        sp.api = true;
394        list_status_header(&sp);
395    } else if (strcasecmp(cmd, "running") == 0) {
396        sp.api = true;
397        list_running_jobs(&sp);
398    } else if (strcasecmp(cmd, "terminated") == 0) {
399        sp.api = true;
400        list_terminated_jobs(&sp);
401    } else {
402       pm_strcpy(&jcr->errmsg, dir->msg);
403       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
404       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
405       dir->signal(BNET_EOD);
406       free_memory(cmd);
407       return 0;
408    }
409
410    dir->signal(BNET_EOD);
411    free_memory(cmd);
412    return 1;
413 }
414
415 /*
416  * Convert Job Level into a string
417  */
418 static const char *level_to_str(int level)
419 {
420    const char *str;
421
422    switch (level) {
423    case L_BASE:
424       str = _("Base");
425    case L_FULL:
426       str = _("Full");
427       break;
428    case L_INCREMENTAL:
429       str = _("Incremental");
430       break;
431    case L_DIFFERENTIAL:
432       str = _("Differential");
433       break;
434    case L_SINCE:
435       str = _("Since");
436       break;
437    case L_VERIFY_CATALOG:
438       str = _("Verify Catalog");
439       break;
440    case L_VERIFY_INIT:
441       str = _("Init Catalog");
442       break;
443    case L_VERIFY_VOLUME_TO_CATALOG:
444       str = _("Volume to Catalog");
445       break;
446    case L_VERIFY_DISK_TO_CATALOG:
447       str = _("Disk to Catalog");
448       break;
449    case L_VERIFY_DATA:
450       str = _("Data");
451       break;
452    case L_NONE:
453       str = " ";
454       break;
455    default:
456       str = _("Unknown Job Level");
457       break;
458    }
459    return str;
460 }
461
462
463 #if defined(HAVE_WIN32)
464 int bacstat = 0;
465
466 /*
467  * Put message in Window List Box
468  */
469 char *bac_status(char *buf, int buf_len)
470 {
471    JCR *njcr;
472    const char *termstat = _("Bacula Client: Idle");
473    struct s_last_job *job;
474    int stat = 0;                      /* Idle */
475
476    if (!last_jobs) {
477       goto done;
478    }
479    Dmsg0(1000, "Begin bac_status jcr loop.\n");
480    foreach_jcr(njcr) {
481       if (njcr->JobId != 0) {
482          stat = JS_Running;
483          termstat = _("Bacula Client: Running");
484          break;
485       }
486    }
487    endeach_jcr(njcr);
488
489    if (stat != 0) {
490       goto done;
491    }
492    if (last_jobs->size() > 0) {
493       job = (struct s_last_job *)last_jobs->last();
494       stat = job->JobStatus;
495       switch (job->JobStatus) {
496       case JS_Canceled:
497          termstat = _("Bacula Client: Last Job Canceled");
498          break;
499       case JS_ErrorTerminated:
500       case JS_FatalError:
501          termstat = _("Bacula Client: Last Job Failed");
502          break;
503       default:
504          if (job->Errors) {
505             termstat = _("Bacula Client: Last Job had Warnings");
506          }
507          break;
508       }
509    }
510    Dmsg0(1000, "End bac_status jcr loop.\n");
511 done:
512    bacstat = stat;
513    if (buf) {
514       bstrncpy(buf, termstat, buf_len);
515    }
516    return buf;
517 }
518
519 #endif /* HAVE_WIN32 */