]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
Tweak updates
[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 && bplugin_list->size() > 0) {
153       Plugin *plugin;
154       int len;
155       pm_strcpy(msg, "Plugin: ");
156       foreach_alist(plugin, bplugin_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    JCR *njcr;
253    char dt[MAX_TIME_LENGTH];
254    /*
255     * List running jobs for Bat/Bweb (simple to parse)
256     */
257    int vss = 0;
258 #ifdef WIN32_VSS
259    if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
260       vss = 1;
261    }
262 #endif
263    foreach_jcr(njcr) {
264       bstrutime(dt, sizeof(dt), njcr->start_time);
265       if (njcr->JobId == 0) {
266          len = Mmsg(msg, "DirectorConnected=%s\n", dt);
267       } else {
268          len = Mmsg(msg, "JobId=%d\n Job=%s\n",
269                     njcr->JobId, njcr->Job);
270          sendit(msg.c_str(), len, sp);
271          len = Mmsg(msg," VSS=%d\n Level=%c\n JobType=%c\n JobStarted=%s\n",
272                     vss, njcr->getJobLevel(), 
273                     njcr->getJobType(), dt);
274       }
275       sendit(msg.c_str(), len, sp);
276       if (njcr->JobId == 0) {
277          continue;
278       }
279       sec = time(NULL) - njcr->start_time;
280       if (sec <= 0) {
281          sec = 1;
282       }
283       bps = (int)(njcr->JobBytes / sec);
284       len = Mmsg(msg, " Files=%s\n Bytes=%s\n Bytes/sec=%s\n Errors=%d\n",
285                  edit_uint64(njcr->JobFiles, b1),
286                  edit_uint64(njcr->JobBytes, b2),
287                  edit_uint64(bps, b3),
288                  njcr->JobErrors);
289       sendit(msg.c_str(), len, sp);
290       len = Mmsg(msg, " Files Examined=%s\n",
291            edit_uint64(njcr->num_files_examined, b1));
292       sendit(msg.c_str(), len, sp);
293       if (njcr->JobFiles > 0) {
294          njcr->lock();
295          len = Mmsg(msg, " Processing file=%s\n", njcr->last_fname);
296          njcr->unlock();
297          sendit(msg.c_str(), len, sp);
298       }
299
300       if (njcr->store_bsock) {
301          len = Mmsg(msg, " SDReadSeqNo=%" lld "\n fd=%d\n",
302              njcr->store_bsock->read_seqno, njcr->store_bsock->m_fd);
303          sendit(msg.c_str(), len, sp);
304       } else {
305          len = Mmsg(msg, _(" SDSocket=closed\n"));
306          sendit(msg.c_str(), len, sp);
307       }
308    }
309    endeach_jcr(njcr);
310 }
311
312 static void  list_running_jobs(STATUS_PKT *sp)
313 {
314    if (sp->api) {
315       list_running_jobs_api(sp);
316    } else {
317       list_running_jobs_plain(sp);
318    }
319 }  
320
321 static void list_terminated_jobs(STATUS_PKT *sp)
322 {
323    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
324    char level[10];
325    struct s_last_job *je;
326    const char *msg;
327
328    if (!sp->api) {
329       msg =  _("\nTerminated Jobs:\n");
330       sendit(msg, strlen(msg), sp);
331    }
332
333    if (last_jobs->size() == 0) {
334       if (!sp->api) sendit(_("====\n"), 5, sp);
335       return;
336    }
337    lock_last_jobs_list();
338    if (!sp->api) {
339       msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
340       sendit(msg, strlen(msg), sp);
341       msg = _("======================================================================\n");
342       sendit(msg, strlen(msg), sp);
343    }
344    foreach_dlist(je, last_jobs) {
345       char JobName[MAX_NAME_LENGTH];
346       const char *termstat;
347       char buf[1000];
348
349       bstrftime_nc(dt, sizeof(dt), je->end_time);
350       switch (je->JobType) {
351       case JT_ADMIN:
352       case JT_RESTORE:
353          bstrncpy(level, "    ", sizeof(level));
354          break;
355       default:
356          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
357          level[4] = 0;
358          break;
359       }
360       switch (je->JobStatus) {
361       case JS_Created:
362          termstat = _("Created");
363          break;
364       case JS_FatalError:
365       case JS_ErrorTerminated:
366          termstat = _("Error");
367          break;
368       case JS_Differences:
369          termstat = _("Diffs");
370          break;
371       case JS_Canceled:
372          termstat = _("Cancel");
373          break;
374       case JS_Terminated:
375          termstat = _("OK");
376          break;
377       default:
378          termstat = _("Other");
379          break;
380       }
381       bstrncpy(JobName, je->Job, sizeof(JobName));
382       /* There are three periods after the Job name */
383       char *p;
384       for (int i=0; i<3; i++) {
385          if ((p=strrchr(JobName, '.')) != NULL) {
386             *p = 0;
387          }
388       }
389       if (sp->api) {
390          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
391             je->JobId,
392             level,
393             edit_uint64_with_commas(je->JobFiles, b1),
394             edit_uint64_with_suffix(je->JobBytes, b2),
395             termstat,
396             dt, JobName);
397       } else {
398          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
399             je->JobId,
400             level,
401             edit_uint64_with_commas(je->JobFiles, b1),
402             edit_uint64_with_suffix(je->JobBytes, b2),
403             termstat,
404             dt, JobName);
405       }
406       sendit(buf, strlen(buf), sp);
407    }
408    if (!sp->api) sendit(_("====\n"), 5, sp);
409    unlock_last_jobs_list();
410 }
411
412
413 /*
414  * Send to bsock (Director or Console)
415  */
416 static void sendit(const char *msg, int len, STATUS_PKT *sp)          
417 {
418    if (sp->bs) {
419       BSOCK *user = sp->bs;
420       user->msg = check_pool_memory_size(user->msg, len+1);
421       memcpy(user->msg, msg, len+1);
422       user->msglen = len+1;
423       user->send();
424    } else {
425       sp->callback(msg, len, sp->context);
426    }
427 }
428
429 /*
430  * Status command from Director
431  */
432 int status_cmd(JCR *jcr)
433 {
434    BSOCK *user = jcr->dir_bsock;
435    STATUS_PKT sp;
436
437    user->fsend("\n");
438    sp.bs = user;
439    sp.api = false;                         /* no API output */
440    output_status(&sp);
441
442    user->signal(BNET_EOD);
443    return 1;
444 }
445
446 /*
447  * .status command from Director
448  */
449 int qstatus_cmd(JCR *jcr)
450 {
451    BSOCK *dir = jcr->dir_bsock;
452    POOLMEM *cmd;
453    JCR *njcr;
454    s_last_job* job;
455    STATUS_PKT sp;
456
457    sp.bs = dir;
458    cmd = get_memory(dir->msglen+1);
459
460    if (sscanf(dir->msg, qstatus, cmd) != 1) {
461       pm_strcpy(&jcr->errmsg, dir->msg);
462       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
463       dir->fsend(_("2900 Bad .status command, missing argument.\n"));
464       dir->signal(BNET_EOD);
465       free_memory(cmd);
466       return 0;
467    }
468    unbash_spaces(cmd);
469
470    if (strcmp(cmd, "current") == 0) {
471       dir->fsend(OKqstatus, cmd);
472       foreach_jcr(njcr) {
473          if (njcr->JobId != 0) {
474             dir->fsend(DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
475          }
476       }
477       endeach_jcr(njcr);
478    } else if (strcmp(cmd, "last") == 0) {
479       dir->fsend(OKqstatus, cmd);
480       if ((last_jobs) && (last_jobs->size() > 0)) {
481          job = (s_last_job*)last_jobs->last();
482          dir->fsend(DotStatusJob, job->JobId, job->JobStatus, job->Errors);
483       }
484    } else if (strcasecmp(cmd, "header") == 0) {
485        sp.api = true;
486        list_status_header(&sp);
487    } else if (strcasecmp(cmd, "running") == 0) {
488        sp.api = true;
489        list_running_jobs(&sp);
490    } else if (strcasecmp(cmd, "terminated") == 0) {
491        sp.api = true;
492        list_terminated_jobs(&sp);
493    } else {
494       pm_strcpy(&jcr->errmsg, dir->msg);
495       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
496       dir->fsend(_("2900 Bad .status command, wrong argument.\n"));
497       dir->signal(BNET_EOD);
498       free_memory(cmd);
499       return 0;
500    }
501
502    dir->signal(BNET_EOD);
503    free_memory(cmd);
504    return 1;
505 }
506
507 /*
508  * Convert Job Level into a string
509  */
510 static const char *level_to_str(int level)
511 {
512    const char *str;
513
514    switch (level) {
515    case L_BASE:
516       str = _("Base");
517    case L_FULL:
518       str = _("Full");
519       break;
520    case L_INCREMENTAL:
521       str = _("Incremental");
522       break;
523    case L_DIFFERENTIAL:
524       str = _("Differential");
525       break;
526    case L_SINCE:
527       str = _("Since");
528       break;
529    case L_VERIFY_CATALOG:
530       str = _("Verify Catalog");
531       break;
532    case L_VERIFY_INIT:
533       str = _("Init Catalog");
534       break;
535    case L_VERIFY_VOLUME_TO_CATALOG:
536       str = _("Volume to Catalog");
537       break;
538    case L_VERIFY_DISK_TO_CATALOG:
539       str = _("Disk to Catalog");
540       break;
541    case L_VERIFY_DATA:
542       str = _("Data");
543       break;
544    case L_NONE:
545       str = " ";
546       break;
547    default:
548       str = _("Unknown Job Level");
549       break;
550    }
551    return str;
552 }
553
554
555 #if defined(HAVE_WIN32)
556 int bacstat = 0;
557
558 /*
559  * Put message in Window List Box
560  */
561 char *bac_status(char *buf, int buf_len)
562 {
563    JCR *njcr;
564    const char *termstat = _("Bacula Client: Idle");
565    struct s_last_job *job;
566    int stat = 0;                      /* Idle */
567
568    if (!last_jobs) {
569       goto done;
570    }
571    Dmsg0(1000, "Begin bac_status jcr loop.\n");
572    foreach_jcr(njcr) {
573       if (njcr->JobId != 0) {
574          stat = JS_Running;
575          termstat = _("Bacula Client: Running");
576          break;
577       }
578    }
579    endeach_jcr(njcr);
580
581    if (stat != 0) {
582       goto done;
583    }
584    if (last_jobs->size() > 0) {
585       job = (struct s_last_job *)last_jobs->last();
586       stat = job->JobStatus;
587       switch (job->JobStatus) {
588       case JS_Canceled:
589          termstat = _("Bacula Client: Last Job Canceled");
590          break;
591       case JS_ErrorTerminated:
592       case JS_FatalError:
593          termstat = _("Bacula Client: Last Job Failed");
594          break;
595       default:
596          if (job->Errors) {
597             termstat = _("Bacula Client: Last Job had Warnings");
598          }
599          break;
600       }
601    }
602    Dmsg0(1000, "End bac_status jcr loop.\n");
603 done:
604    bacstat = stat;
605    if (buf) {
606       bstrncpy(buf, termstat, buf_len);
607    }
608    return buf;
609 }
610
611 #endif /* HAVE_WIN32 */