]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
kes Refactor FD and SD status commands to permit new API code in
[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, bool api);
45 static void  list_running_jobs(STATUS_PKT *sp, bool api);
46 static void  list_status_header(STATUS_PKT *sp, bool api);
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, false /*no api*/);
73    list_running_jobs(sp, false /*no api*/);
74    list_terminated_jobs(sp, false /*no api*/);
75 }
76
77 static void  list_status_header(STATUS_PKT *sp, bool api)
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, bool api)
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 (!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 (!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, bool api)
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 (!api) {
237       msg =  _("\nTerminated Jobs:\n");
238       sendit(msg, strlen(msg), sp);
239    }
240
241    if (last_jobs->size() == 0) {
242       if (!api) sendit(_("====\n"), 5, sp);
243       return;
244    }
245    lock_last_jobs_list();
246    if (!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 (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 (!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    output_status(&sp);
348
349    user->signal(BNET_EOD);
350    return 1;
351 }
352
353 /*
354  * .status command from Director
355  */
356 int qstatus_cmd(JCR *jcr)
357 {
358    BSOCK *dir = jcr->dir_bsock;
359    POOLMEM *cmd;
360    JCR *njcr;
361    s_last_job* job;
362    STATUS_PKT sp;
363
364    sp.bs = dir;
365    cmd = get_memory(dir->msglen+1);
366
367    if (sscanf(dir->msg, qstatus, cmd) != 1) {
368       pm_strcpy(&jcr->errmsg, dir->msg);
369       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
370       dir->fsend(_("2900 Bad .status command, missing argument.\n"));
371       dir->signal(BNET_EOD);
372       free_memory(cmd);
373       return 0;
374    }
375    unbash_spaces(cmd);
376
377    if (strcmp(cmd, "current") == 0) {
378       dir->fsend(OKqstatus, cmd);
379       foreach_jcr(njcr) {
380          if (njcr->JobId != 0) {
381             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
382          }
383       }
384       endeach_jcr(njcr);
385    } else if (strcmp(cmd, "last") == 0) {
386       dir->fsend(OKqstatus, cmd);
387       if ((last_jobs) && (last_jobs->size() > 0)) {
388          job = (s_last_job*)last_jobs->last();
389          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
390       }
391    } else if (strcasecmp(cmd, "header") == 0) {
392        list_status_header(&sp, true/*api*/);
393    } else if (strcasecmp(cmd, "running") == 0) {
394        list_running_jobs(&sp, true/*api*/);
395    } else if (strcasecmp(cmd, "terminated") == 0) {
396        list_terminated_jobs(&sp, true/*api*/);
397    } else {
398       pm_strcpy(&jcr->errmsg, dir->msg);
399       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
400       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
401       dir->signal(BNET_EOD);
402       free_memory(cmd);
403       return 0;
404    }
405
406    dir->signal(BNET_EOD);
407    free_memory(cmd);
408    return 1;
409 }
410
411 /*
412  * Convert Job Level into a string
413  */
414 static const char *level_to_str(int level)
415 {
416    const char *str;
417
418    switch (level) {
419    case L_BASE:
420       str = _("Base");
421    case L_FULL:
422       str = _("Full");
423       break;
424    case L_INCREMENTAL:
425       str = _("Incremental");
426       break;
427    case L_DIFFERENTIAL:
428       str = _("Differential");
429       break;
430    case L_SINCE:
431       str = _("Since");
432       break;
433    case L_VERIFY_CATALOG:
434       str = _("Verify Catalog");
435       break;
436    case L_VERIFY_INIT:
437       str = _("Init Catalog");
438       break;
439    case L_VERIFY_VOLUME_TO_CATALOG:
440       str = _("Volume to Catalog");
441       break;
442    case L_VERIFY_DISK_TO_CATALOG:
443       str = _("Disk to Catalog");
444       break;
445    case L_VERIFY_DATA:
446       str = _("Data");
447       break;
448    case L_NONE:
449       str = " ";
450       break;
451    default:
452       str = _("Unknown Job Level");
453       break;
454    }
455    return str;
456 }
457
458
459 #if defined(HAVE_WIN32)
460 int bacstat = 0;
461
462 /*
463  * Put message in Window List Box
464  */
465 char *bac_status(char *buf, int buf_len)
466 {
467    JCR *njcr;
468    const char *termstat = _("Bacula Client: Idle");
469    struct s_last_job *job;
470    int stat = 0;                      /* Idle */
471
472    if (!last_jobs) {
473       goto done;
474    }
475    Dmsg0(1000, "Begin bac_status jcr loop.\n");
476    foreach_jcr(njcr) {
477       if (njcr->JobId != 0) {
478          stat = JS_Running;
479          termstat = _("Bacula Client: Running");
480          break;
481       }
482    }
483    endeach_jcr(njcr);
484
485    if (stat != 0) {
486       goto done;
487    }
488    if (last_jobs->size() > 0) {
489       job = (struct s_last_job *)last_jobs->last();
490       stat = job->JobStatus;
491       switch (job->JobStatus) {
492       case JS_Canceled:
493          termstat = _("Bacula Client: Last Job Canceled");
494          break;
495       case JS_ErrorTerminated:
496       case JS_FatalError:
497          termstat = _("Bacula Client: Last Job Failed");
498          break;
499       default:
500          if (job->Errors) {
501             termstat = _("Bacula Client: Last Job had Warnings");
502          }
503          break;
504       }
505    }
506    Dmsg0(1000, "End bac_status jcr loop.\n");
507 done:
508    bacstat = stat;
509    if (buf) {
510       bstrncpy(buf, termstat, buf_len);
511    }
512    return buf;
513 }
514
515 #endif /* HAVE_WIN32 */