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