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