]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/status.c
Final changes
[bacula/bacula] / bacula / src / filed / status.c
1 /*
2  *  Bacula File Daemon Status routines
3  *
4  *    Kern Sibbald, August MMI
5  *
6  *   Version $Id$
7  *
8  */
9 /*
10    Copyright (C) 2000-2004 Kern Sibbald and John Walker
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "filed.h"
31
32 extern char my_name[];
33 extern int num_jobs_run;
34 extern time_t daemon_start_time;
35 extern bool get_trace(void);
36
37 /* Forward referenced functions */
38 static void  list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg);
39 static void bsock_sendit(const char *msg, int len, void *arg);
40 static const char *level_to_str(int level);
41
42 /* Static variables */
43 static char qstatus[] = ".status %s\n";
44
45 static char OKqstatus[]   = "2000 OK .status\n";
46 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
47
48 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
49 static int privs = 0;
50 #endif
51
52 /*
53  * General status generator
54  */
55 static void do_status(void sendit(const char *msg, int len, void *sarg), void *arg)
56 {
57    int sec, bps;
58    char *msg, b1[32], b2[32], b3[32], b4[32];
59    int found, len;
60    JCR *njcr;
61    char dt[MAX_TIME_LENGTH];
62
63    msg = (char *)get_pool_memory(PM_MESSAGE);
64    found = 0;
65    len = Mmsg(msg, "%s Version: " VERSION " (" BDATE ") %s %s %s\n", my_name,
66               HOST_OS, DISTNAME, DISTVER);
67    sendit(msg, len, arg);
68    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
69    len = Mmsg(msg, _("Daemon started %s, %d Job%s run since started.\n"),
70         dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
71    sendit(msg, len, arg);
72 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
73    if (/*debug_level > 0*/ true) {
74       if (!privs) {
75          privs = enable_backup_privileges(NULL, 1);
76       }
77       len = Mmsg(msg,
78      _(" Priv 0x%x\n APIs=%sOPT,%sATP,%sLPV,%sCFA,%sCFW,\n"
79      " %sWUL,%sWMKD,%sWOP,%sGFAA,%sGFAW,%sGFAEA,%sGFAEW,%sSFAA,%sSFAW,%sBR,%sBW,%sSPSP,\n" 
80      " %sWC2MB,%sMB2WC,%sFFFA,%sFFFW,%sFNFA,%sFNFW,%sSCDA,%sSCDW,\n"
81      " %sGCDA,%sGCDW\n"), 
82      privs,
83          p_OpenProcessToken?"":"!",
84          p_AdjustTokenPrivileges?"":"!",
85          p_LookupPrivilegeValue?"":"!",
86
87     p_CreateFileA?"":"!",
88          p_CreateFileW?"":"!",
89
90     p_wunlink?"":"!",
91     p_wmkdir?"":"!",
92     p_wopen?"":"!",
93
94     p_GetFileAttributesA?"":"!",
95     p_GetFileAttributesW?"":"!",
96
97     p_GetFileAttributesExA?"":"!",
98          p_GetFileAttributesExW?"":"!",
99
100     p_SetFileAttributesA?"":"!",
101          p_SetFileAttributesW?"":"!",
102          p_BackupRead?"":"!",
103          p_BackupWrite?"":"!",
104          p_SetProcessShutdownParameters?"":"!",
105
106     p_WideCharToMultiByte?"":"!",
107     p_MultiByteToWideChar?"":"!",
108
109     p_FindFirstFileA?"":"!",
110     p_FindFirstFileW?"":"!",
111
112     p_FindNextFileA?"":"!",
113     p_FindNextFileW?"":"!",
114
115     p_SetCurrentDirectoryA?"":"!",
116     p_SetCurrentDirectoryW?"":"!",
117
118     p_GetCurrentDirectoryA?"":"!",
119     p_GetCurrentDirectoryW?"":"!");
120       sendit(msg, len, arg);
121    }
122 #endif
123    if (debug_level > 0) {
124       len = Mmsg(msg, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
125             edit_uint64_with_commas(sm_bytes, b1),
126             edit_uint64_with_commas(sm_max_bytes, b2),
127             edit_uint64_with_commas(sm_buffers, b3),
128             edit_uint64_with_commas(sm_max_buffers, b4));
129       sendit(msg, len, arg);
130       len = Mmsg(msg, _(" Sizeof: off_t=%d size_t=%d debug=%d trace=%d\n"),
131             sizeof(off_t), sizeof(size_t), debug_level, get_trace());
132       sendit(msg, len, arg);
133    }
134
135    list_terminated_jobs(sendit, arg);
136
137    /*
138     * List running jobs
139     */
140    Dmsg0(1000, "Begin status jcr loop.\n");
141    len = Mmsg(msg, _("Running Jobs:\n"));
142    sendit(msg, len, arg);
143    foreach_jcr(njcr) {
144       bstrftime_nc(dt, sizeof(dt), njcr->start_time);
145       if (njcr->JobId == 0) {
146          len = Mmsg(msg, _("Director connected at: %s\n"), dt);
147       } else {
148          len = Mmsg(msg, _("JobId %d Job %s is running.\n"),
149                     njcr->JobId, njcr->Job);
150          sendit(msg, len, arg);
151          len = Mmsg(msg, _("    %s Job started: %s\n"),
152                     job_type_to_str(njcr->JobType), dt);
153       }
154       sendit(msg, len, arg);
155       if (njcr->JobId == 0) {
156          free_jcr(njcr);
157          continue;
158       }
159       sec = time(NULL) - njcr->start_time;
160       if (sec <= 0) {
161          sec = 1;
162       }
163       bps = (int)(njcr->JobBytes / sec);
164       len = Mmsg(msg,  _("    Files=%s Bytes=%s Bytes/sec=%s\n"),
165            edit_uint64_with_commas(njcr->JobFiles, b1),
166            edit_uint64_with_commas(njcr->JobBytes, b2),
167            edit_uint64_with_commas(bps, b3));
168       sendit(msg, len, arg);
169       len = Mmsg(msg, _("    Files Examined=%s\n"),
170            edit_uint64_with_commas(njcr->num_files_examined, b1));
171       sendit(msg, len, arg);
172       if (njcr->JobFiles > 0) {
173          P(njcr->mutex);
174          len = Mmsg(msg, _("    Processing file: %s\n"), njcr->last_fname);
175          V(njcr->mutex);
176          sendit(msg, len, arg);
177       }
178
179       found = 1;
180       if (njcr->store_bsock) {
181          len = Mmsg(msg, "    SDReadSeqNo=%" lld " fd=%d\n",
182              njcr->store_bsock->read_seqno, njcr->store_bsock->fd);
183          sendit(msg, len, arg);
184       } else {
185          len = Mmsg(msg, _("    SDSocket closed.\n"));
186          sendit(msg, len, arg);
187       }
188       free_jcr(njcr);
189    }
190    Dmsg0(1000, "Begin status jcr loop.\n");
191    if (!found) {
192       len = Mmsg(msg, _("No Jobs running.\n"));
193       sendit(msg, len, arg);
194    }
195    len = Mmsg(msg, _("====\n"));
196    sendit(msg, len, arg);
197    free_pool_memory(msg);
198 }
199
200 static void  list_terminated_jobs(void sendit(const char *msg, int len, void *sarg), void *arg)
201 {
202    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
203    char level[10];
204    struct s_last_job *je;
205    const char *msg;
206
207    if (last_jobs->size() == 0) {
208       msg = _("No Terminated Jobs.\n");
209       sendit(msg, strlen(msg), arg);
210       return;
211    }
212    lock_last_jobs_list();
213    sendit("\n", 1, arg);               /* send separately */
214    msg =  _("Terminated Jobs:\n");
215    sendit(msg, strlen(msg), arg);
216    msg =  _(" JobId  Level     Files         Bytes  Status   Finished        Name \n");
217    sendit(msg, strlen(msg), arg);
218    msg = _("======================================================================\n");
219    sendit(msg, strlen(msg), arg);
220    foreach_dlist(je, last_jobs) {
221       char JobName[MAX_NAME_LENGTH];
222       const char *termstat;
223       char buf[1000];
224
225       bstrftime_nc(dt, sizeof(dt), je->end_time);
226       switch (je->JobType) {
227       case JT_ADMIN:
228       case JT_RESTORE:
229          bstrncpy(level, "    ", sizeof(level));
230          break;
231       default:
232          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
233          level[4] = 0;
234          break;
235       }
236       switch (je->JobStatus) {
237       case JS_Created:
238          termstat = "Created";
239          break;
240       case JS_FatalError:
241       case JS_ErrorTerminated:
242          termstat = "Error";
243          break;
244       case JS_Differences:
245          termstat = "Diffs";
246          break;
247       case JS_Canceled:
248          termstat = "Cancel";
249          break;
250       case JS_Terminated:
251          termstat = "OK";
252          break;
253       default:
254          termstat = "Other";
255          break;
256       }
257       bstrncpy(JobName, je->Job, sizeof(JobName));
258       /* There are three periods after the Job name */
259       char *p;
260       for (int i=0; i<3; i++) {
261          if ((p=strrchr(JobName, '.')) != NULL) {
262             *p = 0;
263          }
264       }
265       bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"),
266          je->JobId,
267          level,
268          edit_uint64_with_commas(je->JobFiles, b1),
269          edit_uint64_with_commas(je->JobBytes, b2),
270          termstat,
271          dt, JobName);
272       sendit(buf, strlen(buf), arg);
273    }
274    sendit("====\n", 5, arg);
275    unlock_last_jobs_list();
276 }
277
278
279 /*
280  * Send to bsock (Director or Console)
281  */
282 static void bsock_sendit(const char *msg, int len, void *arg)
283 {
284    BSOCK *user = (BSOCK *)arg;
285
286    user->msg = check_pool_memory_size(user->msg, len+1);
287    memcpy(user->msg, msg, len+1);
288    user->msglen = len+1;
289    bnet_send(user);
290 }
291
292 /*
293  * Status command from Director
294  */
295 int status_cmd(JCR *jcr)
296 {
297    BSOCK *user = jcr->dir_bsock;
298
299    bnet_fsend(user, "\n");
300    do_status(bsock_sendit, (void *)user);
301
302    bnet_sig(user, BNET_EOD);
303    return 1;
304 }
305
306 /*
307  * .status command from Director
308  */
309 int qstatus_cmd(JCR *jcr)
310 {
311    BSOCK *dir = jcr->dir_bsock;
312    POOLMEM *time;
313    JCR *njcr;
314    s_last_job* job;
315
316    time = get_memory(dir->msglen+1);
317
318    if (sscanf(dir->msg, qstatus, time) != 1) {
319       pm_strcpy(&jcr->errmsg, dir->msg);
320       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
321       bnet_fsend(dir, "2900 Bad .status command, missing argument.\n");
322       bnet_sig(dir, BNET_EOD);
323       free_memory(time);
324       return 0;
325    }
326    unbash_spaces(time);
327
328    if (strcmp(time, "current") == 0) {
329       bnet_fsend(dir, OKqstatus, time);
330       foreach_jcr(njcr) {
331          if (njcr->JobId != 0) {
332             bnet_fsend(dir, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
333          }
334          free_jcr(njcr);
335       }
336    }
337    else if (strcmp(time, "last") == 0) {
338       bnet_fsend(dir, OKqstatus, time);
339       if ((last_jobs) && (last_jobs->size() > 0)) {
340          job = (s_last_job*)last_jobs->last();
341          bnet_fsend(dir, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
342       }
343    }
344    else {
345       pm_strcpy(&jcr->errmsg, dir->msg);
346       Jmsg1(jcr, M_FATAL, 0, _("Bad .status command: %s\n"), jcr->errmsg);
347       bnet_fsend(dir, "2900 Bad .status command, wrong argument.\n");
348       bnet_sig(dir, BNET_EOD);
349       free_memory(time);
350       return 0;
351    }
352
353    bnet_sig(dir, BNET_EOD);
354    free_memory(time);
355    return 1;
356 }
357
358 /*
359  * Convert Job Level into a string
360  */
361 static const char *level_to_str(int level)
362 {
363    const char *str;
364
365    switch (level) {
366    case L_BASE:
367       str = _("Base");
368    case L_FULL:
369       str = _("Full");
370       break;
371    case L_INCREMENTAL:
372       str = _("Incremental");
373       break;
374    case L_DIFFERENTIAL:
375       str = _("Differential");
376       break;
377    case L_SINCE:
378       str = _("Since");
379       break;
380    case L_VERIFY_CATALOG:
381       str = _("Verify Catalog");
382       break;
383    case L_VERIFY_INIT:
384       str = _("Init Catalog");
385       break;
386    case L_VERIFY_VOLUME_TO_CATALOG:
387       str = _("Volume to Catalog");
388       break;
389    case L_VERIFY_DISK_TO_CATALOG:
390       str = _("Disk to Catalog");
391       break;
392    case L_VERIFY_DATA:
393       str = _("Data");
394       break;
395    case L_NONE:
396       str = " ";
397       break;
398    default:
399       str = _("Unknown Job Level");
400       break;
401    }
402    return str;
403 }
404
405
406 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
407 #include <windows.h>
408
409 int bacstat = 0;
410
411 struct s_win32_arg {
412    HWND hwnd;
413    int idlist;
414 };
415
416 /*
417  * Put message in Window List Box
418  */
419 static void win32_sendit(const char *msg, int len, void *marg)
420 {
421    struct s_win32_arg *arg = (struct s_win32_arg *)marg;
422
423    if (len > 0 && msg[len-1] == '\n') {
424        // when compiling with visual studio some strings are read-only
425        // and cause access violations.  So we creat a tmp copy.
426        char *_msg = (char *)alloca(len);
427        bstrncpy(_msg, msg, len);
428        msg = _msg;
429    }
430    SendDlgItemMessage(arg->hwnd, arg->idlist, LB_ADDSTRING, 0, (LONG)msg);
431
432 }
433
434 void FillStatusBox(HWND hwnd, int idlist)
435 {
436    struct s_win32_arg arg;
437
438    arg.hwnd = hwnd;
439    arg.idlist = idlist;
440
441    /* Empty box */
442    for ( ; SendDlgItemMessage(hwnd, idlist, LB_DELETESTRING, 0, (LONG)0) > 0; )
443       { }
444    do_status(win32_sendit, (void *)&arg);
445 }
446
447 char *bac_status(char *buf, int buf_len)
448 {
449    JCR *njcr;
450    const char *termstat = _("Bacula Idle");
451    struct s_last_job *job;
452    int stat = 0;                      /* Idle */
453
454    if (!last_jobs) {
455       goto done;
456    }
457    Dmsg0(1000, "Begin bac_status jcr loop.\n");
458    foreach_jcr(njcr) {
459       if (njcr->JobId != 0) {
460          stat = JS_Running;
461          termstat = _("Bacula Running");
462          free_jcr(njcr);
463          break;
464       }
465       free_jcr(njcr);
466    }
467    if (stat != 0) {
468       goto done;
469    }
470    if (last_jobs->size() > 0) {
471       job = (struct s_last_job *)last_jobs->last();
472       stat = job->JobStatus;
473       switch (job->JobStatus) {
474       case JS_Canceled:
475          termstat = _("Last Job Canceled");
476          break;
477       case JS_ErrorTerminated:
478       case JS_FatalError:
479          termstat = _("Last Job Failed");
480          break;
481       default:
482          if (job->Errors) {
483             termstat = _("Last Job had Warnings");
484          }
485          break;
486       }
487    }
488    Dmsg0(1000, "End bac_status jcr loop.\n");
489 done:
490    bacstat = stat;
491    if (buf) {
492       bstrncpy(buf, termstat, buf_len);
493    }
494    return buf;
495 }
496
497 #endif /* HAVE_CYGWIN */