]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
kes First cut of converting FD .status to work with bat API.
[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
40 extern void *start_heap;
41
42 /* Forward referenced functions */
43 static void  list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg, bool api);
44 static void  list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg, bool api);
45 static void  list_status_header(void sendit(const char *msg, int len, void *sarg), void *arg, bool api);
46 static void bsock_sendit(const char *msg, int len, void *arg);
47 static const char *level_to_str(int level);
48
49 /* Static variables */
50 static char qstatus[] = ".status %s\n";
51
52 static char OKqstatus[]   = "2000 OK .status\n";
53 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
54
55 #if defined(HAVE_WIN32)
56 static int privs = 0;
57 #endif
58 #ifdef WIN32_VSS
59 #include "vss.h"
60 #define VSS " VSS"
61 extern VSSClient *g_pVSSClient;
62 #else
63 #define VSS ""
64 #endif
65
66 /*
67  * General status generator
68  */
69 void output_status(void sendit(const char *msg, int len, void *sarg), void *arg)
70 {
71    list_status_header(sendit, arg, false /*no api*/);
72    list_running_jobs(sendit, arg, false /*no api*/);
73    list_terminated_jobs(sendit, arg, false /*no api*/);
74 }
75
76 static void  list_status_header(void sendit(const char *msg, int len, void *sarg), void *arg, bool api)
77 {
78    POOL_MEM msg(PM_MESSAGE);
79    char b1[32], b2[32], b3[32], b4[32], b5[35];
80    int len;
81    char dt[MAX_TIME_LENGTH];
82
83    len = Mmsg(msg, _("%s Version: %s (%s) %s %s %s %s\n"), 
84               my_name, VERSION, BDATE, VSS, HOST_OS, DISTNAME, DISTVER);
85    sendit(msg.c_str(), len, arg);
86    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
87    len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
88         dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
89    sendit(msg.c_str(), len, arg);
90 #if defined(HAVE_WIN32)
91    if (debug_level > 0) {
92       if (!privs) {
93          privs = enable_backup_privileges(NULL, 1);
94       }
95       len = Mmsg(msg, "VSS %s, Priv 0x%x\n", g_pVSSClient?"enabled":"disabled", privs);
96       sendit(msg.c_str(), len, arg);
97       len = Mmsg(msg, "APIs=%sOPT,%sATP,%sLPV,%sCFA,%sCFW,\n",
98                  p_OpenProcessToken?"":"!",
99                  p_AdjustTokenPrivileges?"":"!",
100                  p_LookupPrivilegeValue?"":"!",
101                  p_CreateFileA?"":"!",
102                  p_CreateFileW?"":"!");
103       sendit(msg.c_str(), len, arg);
104       len = Mmsg(msg, " %sWUL,%sWMKD,%sGFAA,%sGFAW,%sGFAEA,%sGFAEW,%sSFAA,%sSFAW,%sBR,%sBW,%sSPSP,\n",
105                  p_wunlink?"":"!",
106                  p_wmkdir?"":"!",
107                  p_GetFileAttributesA?"":"!",
108                  p_GetFileAttributesW?"":"!",
109                  p_GetFileAttributesExA?"":"!",
110                  p_GetFileAttributesExW?"":"!",
111                  p_SetFileAttributesA?"":"!",
112                  p_SetFileAttributesW?"":"!",
113                  p_BackupRead?"":"!",
114                  p_BackupWrite?"":"!",
115                  p_SetProcessShutdownParameters?"":"!");
116       sendit(msg.c_str(), len, arg);
117       len = Mmsg(msg, " %sWC2MB,%sMB2WC,%sFFFA,%sFFFW,%sFNFA,%sFNFW,%sSCDA,%sSCDW,\n",
118                  p_WideCharToMultiByte?"":"!",
119                  p_MultiByteToWideChar?"":"!",
120                  p_FindFirstFileA?"":"!",
121                  p_FindFirstFileW?"":"!",
122                  p_FindNextFileA?"":"!",
123                  p_FindNextFileW?"":"!",
124                  p_SetCurrentDirectoryA?"":"!",
125                  p_SetCurrentDirectoryW?"":"!");
126       sendit(msg.c_str(), len, arg);
127       len = Mmsg(msg, " %sGCDA,%sGCDW,%sGVPNW,%sGVNFVMPW\n",  
128                  p_GetCurrentDirectoryA?"":"!",
129                  p_GetCurrentDirectoryW?"":"!",
130                  p_GetVolumePathNameW?"":"!",
131                  p_GetVolumeNameForVolumeMountPointW?"":"!");
132      sendit(msg.c_str(), len, arg);
133    }
134 #endif
135    len = Mmsg(msg, _(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
136          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
137          edit_uint64_with_commas(sm_bytes, b2),
138          edit_uint64_with_commas(sm_max_bytes, b3),
139          edit_uint64_with_commas(sm_buffers, b4),
140          edit_uint64_with_commas(sm_max_buffers, b5));
141    sendit(msg.c_str(), len, arg);
142    len = Mmsg(msg, _(" Sizeof: boffset_t=%d size_t=%d debug=%d trace=%d\n"),
143          sizeof(boffset_t), sizeof(size_t), debug_level, get_trace());
144    sendit(msg.c_str(), len, arg);
145 }
146
147 static void  list_running_jobs(void sendit(const char *msg, int len, void *sarg), void *arg, bool api)
148 {
149    int sec, bps;
150    POOL_MEM msg(PM_MESSAGE);
151    char b1[32], b2[32], b3[32];
152    int len;
153    bool found = false;
154    JCR *njcr;
155    char dt[MAX_TIME_LENGTH];
156    /*
157     * List running jobs
158     */
159    Dmsg0(1000, "Begin status jcr loop.\n");
160    if (!api) {
161       len = Mmsg(msg, _("\nRunning Jobs:\n"));
162       sendit(msg.c_str(), len, arg);
163    }
164    const char *vss = "";
165 #ifdef WIN32_VSS
166    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
167       vss = "VSS ";
168    }
169 #endif
170    foreach_jcr(njcr) {
171       bstrftime_nc(dt, sizeof(dt), njcr->start_time);
172       if (njcr->JobId == 0) {
173          len = Mmsg(msg, _("Director connected at: %s\n"), dt);
174       } else {
175          len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
176                     njcr->JobId, njcr->Job);
177          sendit(msg.c_str(), len, arg);
178          len = Mmsg(msg, _("    %s%s Job started: %s\n"),
179                     vss, job_type_to_str(njcr->JobType), dt);
180       }
181       sendit(msg.c_str(), len, arg);
182       if (njcr->JobId == 0) {
183          continue;
184       }
185       sec = time(NULL) - njcr->start_time;
186       if (sec <= 0) {
187          sec = 1;
188       }
189       bps = (int)(njcr->JobBytes / sec);
190       len = Mmsg(msg,  _("    Files=%s Bytes=%s Bytes/sec=%s Errors=%d\n"),
191            edit_uint64_with_commas(njcr->JobFiles, b1),
192            edit_uint64_with_commas(njcr->JobBytes, b2),
193            edit_uint64_with_commas(bps, b3),
194            njcr->JobErrors);
195       sendit(msg.c_str(), len, arg);
196       len = Mmsg(msg, _("    Files Examined=%s\n"),
197            edit_uint64_with_commas(njcr->num_files_examined, b1));
198       sendit(msg.c_str(), len, arg);
199       if (njcr->JobFiles > 0) {
200          njcr->lock();
201          len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
202          njcr->unlock();
203          sendit(msg.c_str(), len, arg);
204       }
205
206       found = true;
207       if (njcr->store_bsock) {
208          len = Mmsg(msg, "    SDReadSeqNo=%" lld " fd=%d\n",
209              njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
210          sendit(msg.c_str(), len, arg);
211       } else {
212          len = Mmsg(msg, _("    SDSocket closed.\n"));
213          sendit(msg.c_str(), len, arg);
214       }
215    }
216    endeach_jcr(njcr);
217
218    if (!api) {
219       if (!found) {
220          len = Mmsg(msg, _("No Jobs running.\n"));
221          sendit(msg.c_str(), len, arg);
222       }
223       sendit(_("====\n"), 5, arg);
224    }
225 }
226   
227
228 static void list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg, bool api)
229 {
230    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
231    char level[10];
232    struct s_last_job *je;
233    const char *msg;
234
235    if (!api) {
236       msg =  _("\nTerminated Jobs:\n");
237       sendit(msg, strlen(msg), arg);
238    }
239
240    if (last_jobs->size() == 0) {
241       if (!api) sendit(_("====\n"), 5, arg);
242       return;
243    }
244    lock_last_jobs_list();
245    if (!api) {
246       msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
247       sendit(msg, strlen(msg), arg);
248       msg = _("======================================================================\n");
249       sendit(msg, strlen(msg), arg);
250    }
251    foreach_dlist(je, last_jobs) {
252       char JobName[MAX_NAME_LENGTH];
253       const char *termstat;
254       char buf[1000];
255
256       bstrftime_nc(dt, sizeof(dt), je->end_time);
257       switch (je->JobType) {
258       case JT_ADMIN:
259       case JT_RESTORE:
260          bstrncpy(level, "    ", sizeof(level));
261          break;
262       default:
263          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
264          level[4] = 0;
265          break;
266       }
267       switch (je->JobStatus) {
268       case JS_Created:
269          termstat = _("Created");
270          break;
271       case JS_FatalError:
272       case JS_ErrorTerminated:
273          termstat = _("Error");
274          break;
275       case JS_Differences:
276          termstat = _("Diffs");
277          break;
278       case JS_Canceled:
279          termstat = _("Cancel");
280          break;
281       case JS_Terminated:
282          termstat = _("OK");
283          break;
284       default:
285          termstat = _("Other");
286          break;
287       }
288       bstrncpy(JobName, je->Job, sizeof(JobName));
289       /* There are three periods after the Job name */
290       char *p;
291       for (int i=0; i<3; i++) {
292          if ((p=strrchr(JobName, '.')) != NULL) {
293             *p = 0;
294          }
295       }
296       if (api) {
297          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
298             je->JobId,
299             level,
300             edit_uint64_with_commas(je->JobFiles, b1),
301             edit_uint64_with_suffix(je->JobBytes, b2),
302             termstat,
303             dt, JobName);
304       } else {
305          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
306             je->JobId,
307             level,
308             edit_uint64_with_commas(je->JobFiles, b1),
309             edit_uint64_with_suffix(je->JobBytes, b2),
310             termstat,
311             dt, JobName);
312       }
313       sendit(buf, strlen(buf), arg);
314    }
315    if (!api) sendit(_("====\n"), 5, arg);
316    unlock_last_jobs_list();
317 }
318
319
320 /*
321  * Send to bsock (Director or Console)
322  */
323 static void bsock_sendit(const char *msg, int len, void *arg)
324 {
325    BSOCK *user = (BSOCK *)arg;
326
327    user->msg = check_pool_memory_size(user->msg, len+1);
328    memcpy(user->msg, msg, len+1);
329    user->msglen = len+1;
330    user->send();
331 }
332
333 /*
334  * Status command from Director
335  */
336 int status_cmd(JCR *jcr)
337 {
338    BSOCK *user = jcr->dir_bsock;
339
340    user->fsend("\n");
341    output_status(bsock_sendit, (void *)user);
342
343    user->signal(BNET_EOD);
344    return 1;
345 }
346
347 /*
348  * .status command from Director
349  */
350 int qstatus_cmd(JCR *jcr)
351 {
352    BSOCK *dir = jcr->dir_bsock;
353    POOLMEM *cmd;
354    JCR *njcr;
355    s_last_job* job;
356
357    cmd = get_memory(dir->msglen+1);
358
359    if (sscanf(dir->msg, qstatus, cmd) != 1) {
360       pm_strcpy(&jcr->errmsg, dir->msg);
361       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
362       dir->fsend(_("2900 Bad .status command, missing argument.\n"));
363       dir->signal(BNET_EOD);
364       free_memory(cmd);
365       return 0;
366    }
367    unbash_spaces(cmd);
368
369    if (strcmp(cmd, "current") == 0) {
370       dir->fsend(OKqstatus, cmd);
371       foreach_jcr(njcr) {
372          if (njcr->JobId != 0) {
373             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
374          }
375       }
376       endeach_jcr(njcr);
377    } else if (strcmp(cmd, "last") == 0) {
378       dir->fsend(OKqstatus, cmd);
379       if ((last_jobs) && (last_jobs->size() > 0)) {
380          job = (s_last_job*)last_jobs->last();
381          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
382       }
383    } else if (strcasecmp(cmd, "header") == 0) {
384        list_status_header(bsock_sendit, (void *)dir, true/*api*/);
385    } else if (strcasecmp(cmd, "running") == 0) {
386        list_running_jobs(bsock_sendit, (void *)dir, true/*api*/);
387    } else if (strcasecmp(cmd, "terminated") == 0) {
388        list_terminated_jobs(bsock_sendit, (void *)dir, true/*api*/);
389    } else {
390       pm_strcpy(&jcr->errmsg, dir->msg);
391       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
392       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
393       bnet_sig(dir, BNET_EOD);
394       free_memory(cmd);
395       return 0;
396    }
397
398    bnet_sig(dir, BNET_EOD);
399    free_memory(cmd);
400    return 1;
401 }
402
403 /*
404  * Convert Job Level into a string
405  */
406 static const char *level_to_str(int level)
407 {
408    const char *str;
409
410    switch (level) {
411    case L_BASE:
412       str = _("Base");
413    case L_FULL:
414       str = _("Full");
415       break;
416    case L_INCREMENTAL:
417       str = _("Incremental");
418       break;
419    case L_DIFFERENTIAL:
420       str = _("Differential");
421       break;
422    case L_SINCE:
423       str = _("Since");
424       break;
425    case L_VERIFY_CATALOG:
426       str = _("Verify Catalog");
427       break;
428    case L_VERIFY_INIT:
429       str = _("Init Catalog");
430       break;
431    case L_VERIFY_VOLUME_TO_CATALOG:
432       str = _("Volume to Catalog");
433       break;
434    case L_VERIFY_DISK_TO_CATALOG:
435       str = _("Disk to Catalog");
436       break;
437    case L_VERIFY_DATA:
438       str = _("Data");
439       break;
440    case L_NONE:
441       str = " ";
442       break;
443    default:
444       str = _("Unknown Job Level");
445       break;
446    }
447    return str;
448 }
449
450
451 #if defined(HAVE_WIN32)
452 int bacstat = 0;
453
454 /*
455  * Put message in Window List Box
456  */
457 char *bac_status(char *buf, int buf_len)
458 {
459    JCR *njcr;
460    const char *termstat = _("Bacula Client: Idle");
461    struct s_last_job *job;
462    int stat = 0;                      /* Idle */
463
464    if (!last_jobs) {
465       goto done;
466    }
467    Dmsg0(1000, "Begin bac_status jcr loop.\n");
468    foreach_jcr(njcr) {
469       if (njcr->JobId != 0) {
470          stat = JS_Running;
471          termstat = _("Bacula Client: Running");
472          break;
473       }
474    }
475    endeach_jcr(njcr);
476
477    if (stat != 0) {
478       goto done;
479    }
480    if (last_jobs->size() > 0) {
481       job = (struct s_last_job *)last_jobs->last();
482       stat = job->JobStatus;
483       switch (job->JobStatus) {
484       case JS_Canceled:
485          termstat = _("Bacula Client: Last Job Canceled");
486          break;
487       case JS_ErrorTerminated:
488       case JS_FatalError:
489          termstat = _("Bacula Client: Last Job Failed");
490          break;
491       default:
492          if (job->Errors) {
493             termstat = _("Bacula Client: Last Job had Warnings");
494          }
495          break;
496       }
497    }
498    Dmsg0(1000, "End bac_status jcr loop.\n");
499 done:
500    bacstat = stat;
501    if (buf) {
502       bstrncpy(buf, termstat, buf_len);
503    }
504    return buf;
505 }
506
507 #endif /* HAVE_WIN32 */