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