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