]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
kes Turn on heap reporting in Dir with zero debug level.
[bacula/bacula] / bacula / src / dird / ua_status.c
1 /*
2  *
3  *   Bacula Director -- User Agent Status Command
4  *
5  *     Kern Sibbald, August MMI
6  *
7  *   Version $Id$
8  */
9 /*
10    Copyright (C) 2001-2006 Kern Sibbald
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
14    version 2 as amended with additional clauses defined in the
15    file LICENSE in the main source directory.
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 
20    the file LICENSE for additional details.
21
22  */
23
24
25 #include "bacula.h"
26 #include "dird.h"
27
28 static void list_scheduled_jobs(UAContext *ua);
29 static void list_running_jobs(UAContext *ua);
30 static void list_terminated_jobs(UAContext *ua);
31 static void do_storage_status(UAContext *ua, STORE *store);
32 static void do_client_status(UAContext *ua, CLIENT *client);
33 static void do_director_status(UAContext *ua);
34 static void do_all_status(UAContext *ua);
35
36 static char OKqstatus[]   = "1000 OK .status\n";
37 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
38
39 /*
40  * .status command
41  */
42 int qstatus_cmd(UAContext *ua, const char *cmd)
43 {
44    JCR* njcr = NULL;
45    s_last_job* job;
46    char ed1[50];
47
48    if (!open_db(ua)) {
49       return 1;
50    }
51    Dmsg1(20, "status:%s:\n", cmd);
52
53    if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
54       bsendmsg(ua, "1900 Bad .status command, missing arguments.\n");
55       return 1;
56    }
57
58    if (strcasecmp(ua->argk[2], "current") == 0) {
59       bsendmsg(ua, OKqstatus, ua->argk[2]);
60       foreach_jcr(njcr) {
61          if (njcr->JobId != 0) {
62             bsendmsg(ua, DotStatusJob, edit_int64(njcr->JobId, ed1), 
63                      njcr->JobStatus, njcr->JobErrors);
64          }
65       }
66       endeach_jcr(njcr);
67    } else if (strcasecmp(ua->argk[2], "last") == 0) {
68       bsendmsg(ua, OKqstatus, ua->argk[2]);
69       if ((last_jobs) && (last_jobs->size() > 0)) {
70          job = (s_last_job*)last_jobs->last();
71          bsendmsg(ua, DotStatusJob, edit_int64(job->JobId, ed1), 
72                   job->JobStatus, job->Errors);
73       }
74    } else {
75       bsendmsg(ua, "1900 Bad .status command, wrong argument.\n");
76       return 1;
77    }
78
79    return 1;
80 }
81
82 /*
83  * status command
84  */
85 int status_cmd(UAContext *ua, const char *cmd)
86 {
87    STORE *store;
88    CLIENT *client;
89    int item, i;
90
91    if (!open_db(ua)) {
92       return 1;
93    }
94    Dmsg1(20, "status:%s:\n", cmd);
95
96    for (i=1; i<ua->argc; i++) {
97       if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
98          do_all_status(ua);
99          return 1;
100       } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
101                  strcasecmp(ua->argk[i], NT_("director")) == 0) {
102          do_director_status(ua);
103          return 1;
104       } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
105          client = get_client_resource(ua);
106          if (client) {
107             do_client_status(ua, client);
108          }
109          return 1;
110       } else {
111          store = get_storage_resource(ua, false/*no default*/);
112          if (store) {
113             do_storage_status(ua, store);
114          }
115          return 1;
116       }
117    }
118    /* If no args, ask for status type */
119    if (ua->argc == 1) {
120        char prmt[MAX_NAME_LENGTH];
121
122       start_prompt(ua, _("Status available for:\n"));
123       add_prompt(ua, NT_("Director"));
124       add_prompt(ua, NT_("Storage"));
125       add_prompt(ua, NT_("Client"));
126       add_prompt(ua, NT_("All"));
127       Dmsg0(20, "do_prompt: select daemon\n");
128       if ((item=do_prompt(ua, "",  _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
129          return 1;
130       }
131       Dmsg1(20, "item=%d\n", item);
132       switch (item) {
133       case 0:                         /* Director */
134          do_director_status(ua);
135          break;
136       case 1:
137          store = select_storage_resource(ua);
138          if (store) {
139             do_storage_status(ua, store);
140          }
141          break;
142       case 2:
143          client = select_client_resource(ua);
144          if (client) {
145             do_client_status(ua, client);
146          }
147          break;
148       case 3:
149          do_all_status(ua);
150          break;
151       default:
152          break;
153       }
154    }
155    return 1;
156 }
157
158 static void do_all_status(UAContext *ua)
159 {
160    STORE *store, **unique_store;
161    CLIENT *client, **unique_client;
162    int i, j;
163    bool found;
164
165    do_director_status(ua);
166
167    /* Count Storage items */
168    LockRes();
169    i = 0;
170    foreach_res(store, R_STORAGE) {
171       i++;
172    }
173    unique_store = (STORE **) malloc(i * sizeof(STORE));
174    /* Find Unique Storage address/port */
175    i = 0;
176    foreach_res(store, R_STORAGE) {
177       found = false;
178       if (!acl_access_ok(ua, Storage_ACL, store->name())) {
179          continue;
180       }
181       for (j=0; j<i; j++) {
182          if (strcmp(unique_store[j]->address, store->address) == 0 &&
183              unique_store[j]->SDport == store->SDport) {
184             found = true;
185             break;
186          }
187       }
188       if (!found) {
189          unique_store[i++] = store;
190          Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
191       }
192    }
193    UnlockRes();
194
195    /* Call each unique Storage daemon */
196    for (j=0; j<i; j++) {
197       do_storage_status(ua, unique_store[j]);
198    }
199    free(unique_store);
200
201    /* Count Client items */
202    LockRes();
203    i = 0;
204    foreach_res(client, R_CLIENT) {
205       i++;
206    }
207    unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
208    /* Find Unique Client address/port */
209    i = 0;
210    foreach_res(client, R_CLIENT) {
211       found = false;
212       if (!acl_access_ok(ua, Client_ACL, client->name())) {
213          continue;
214       }
215       for (j=0; j<i; j++) {
216          if (strcmp(unique_client[j]->address, client->address) == 0 &&
217              unique_client[j]->FDport == client->FDport) {
218             found = true;
219             break;
220          }
221       }
222       if (!found) {
223          unique_client[i++] = client;
224          Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
225       }
226    }
227    UnlockRes();
228
229    /* Call each unique File daemon */
230    for (j=0; j<i; j++) {
231       do_client_status(ua, unique_client[j]);
232    }
233    free(unique_client);
234
235 }
236
237 static void do_director_status(UAContext *ua)
238 {
239    char dt[MAX_TIME_LENGTH];
240    char b1[35], b2[35], b3[35], b4[35];
241
242    bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
243             HOST_OS, DISTNAME, DISTVER);
244    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
245    if (num_jobs_run == 1) {
246       bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
247    }
248    else {
249       bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
250         dt, num_jobs_run);
251    }
252    bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
253             edit_uint64_with_commas(sm_bytes, b1),
254             edit_uint64_with_commas(sm_max_bytes, b2),
255             edit_uint64_with_commas(sm_buffers, b3),
256             edit_uint64_with_commas(sm_max_buffers, b4));
257
258    /*
259     * List scheduled Jobs
260     */
261    list_scheduled_jobs(ua);
262
263    /*
264     * List running jobs
265     */
266    list_running_jobs(ua);
267
268    /*
269     * List terminated jobs
270     */
271    list_terminated_jobs(ua);
272    bsendmsg(ua, _("====\n"));
273 }
274
275 static void do_storage_status(UAContext *ua, STORE *store)
276 {
277    BSOCK *sd;
278
279    set_wstorage(ua->jcr, store);
280    /* Try connecting for up to 15 seconds */
281    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
282       store->name(), store->address, store->SDport);
283    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
284       bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
285          store->name());
286       if (ua->jcr->store_bsock) {
287          bnet_close(ua->jcr->store_bsock);
288          ua->jcr->store_bsock = NULL;
289       }
290       return;
291    }
292    Dmsg0(20, _("Connected to storage daemon\n"));
293    sd = ua->jcr->store_bsock;
294    bnet_fsend(sd, "status");
295    while (bnet_recv(sd) >= 0) {
296       bsendmsg(ua, "%s", sd->msg);
297    }
298    bnet_sig(sd, BNET_TERMINATE);
299    bnet_close(sd);
300    ua->jcr->store_bsock = NULL;
301    return;
302 }
303
304 static void do_client_status(UAContext *ua, CLIENT *client)
305 {
306    BSOCK *fd;
307
308    /* Connect to File daemon */
309
310    ua->jcr->client = client;
311    /* Release any old dummy key */
312    if (ua->jcr->sd_auth_key) {
313       free(ua->jcr->sd_auth_key);
314    }
315    /* Create a new dummy SD auth key */
316    ua->jcr->sd_auth_key = bstrdup("dummy");
317
318    /* Try to connect for 15 seconds */
319    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
320       client->name(), client->address, client->FDport);
321    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
322       bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
323          client->name());
324       if (ua->jcr->file_bsock) {
325          bnet_close(ua->jcr->file_bsock);
326          ua->jcr->file_bsock = NULL;
327       }
328       return;
329    }
330    Dmsg0(20, _("Connected to file daemon\n"));
331    fd = ua->jcr->file_bsock;
332    bnet_fsend(fd, "status");
333    while (bnet_recv(fd) >= 0) {
334       bsendmsg(ua, "%s", fd->msg);
335    }
336    bnet_sig(fd, BNET_TERMINATE);
337    bnet_close(fd);
338    ua->jcr->file_bsock = NULL;
339
340    return;
341 }
342
343 static void prt_runhdr(UAContext *ua)
344 {
345    bsendmsg(ua, _("\nScheduled Jobs:\n"));
346    bsendmsg(ua, _("Level          Type     Pri  Scheduled          Name               Volume\n"));
347    bsendmsg(ua, _("===================================================================================\n"));
348 }
349
350 /* Scheduling packet */
351 struct sched_pkt {
352    dlink link;                        /* keep this as first item!!! */
353    JOB *job;
354    int level;
355    int priority;
356    time_t runtime;
357    POOL *pool;
358    STORE *store;
359 };
360
361 static void prt_runtime(UAContext *ua, sched_pkt *sp)
362 {
363    char dt[MAX_TIME_LENGTH];
364    const char *level_ptr;
365    bool ok = false;
366    bool close_db = false;
367    JCR *jcr = ua->jcr;
368    MEDIA_DBR mr;
369
370    memset(&mr, 0, sizeof(mr));
371    if (sp->job->JobType == JT_BACKUP) {
372       jcr->db = NULL;
373       ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
374       if (jcr->db) {
375          close_db = true;             /* new db opened, remember to close it */
376       }
377       if (ok) {
378          mr.PoolId = jcr->jr.PoolId;
379          mr.StorageId = sp->store->StorageId;
380          ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
381       }
382       if (!ok) {
383          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
384       }
385    }
386    bstrftime_nc(dt, sizeof(dt), sp->runtime);
387    switch (sp->job->JobType) {
388    case JT_ADMIN:
389    case JT_RESTORE:
390       level_ptr = " ";
391       break;
392    default:
393       level_ptr = level_to_str(sp->level);
394       break;
395    }
396    bsendmsg(ua, _("%-14s %-8s %3d  %-18s %-18s %s\n"),
397       level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
398       sp->job->name(), mr.VolumeName);
399    if (close_db) {
400       db_close_database(jcr, jcr->db);
401    }
402    jcr->db = ua->db;                  /* restore ua db to jcr */
403
404 }
405
406 /*
407  * Sort items by runtime, priority
408  */
409 static int my_compare(void *item1, void *item2)
410 {
411    sched_pkt *p1 = (sched_pkt *)item1;
412    sched_pkt *p2 = (sched_pkt *)item2;
413    if (p1->runtime < p2->runtime) {
414       return -1;
415    } else if (p1->runtime > p2->runtime) {
416       return 1;
417    }
418    if (p1->priority < p2->priority) {
419       return -1;
420    } else if (p1->priority > p2->priority) {
421       return 1;
422    }
423    return 0;
424 }
425
426 /*
427  * Find all jobs to be run in roughly the
428  *  next 24 hours.
429  */
430 static void list_scheduled_jobs(UAContext *ua)
431 {
432    time_t runtime;
433    RUN *run;
434    JOB *job;
435    STORE* store;
436    int level, num_jobs = 0;
437    int priority;
438    bool hdr_printed = false;
439    dlist sched;
440    sched_pkt *sp;
441    int days, i;
442
443    Dmsg0(200, "enter list_sched_jobs()\n");
444
445    days = 1;
446    i = find_arg_with_value(ua, NT_("days"));
447    if (i >= 0) {
448      days = atoi(ua->argv[i]);
449      if ((days < 0) || (days > 50)) {
450        bsendmsg(ua, _("Ignoring illegal value for days.\n"));
451        days = 1;
452      }
453    }
454
455    /* Loop through all jobs */
456    LockRes();
457    foreach_res(job, R_JOB) {
458       if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
459          continue;
460       }
461       for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
462          level = job->JobLevel;
463          if (run->level) {
464             level = run->level;
465          }
466          priority = job->Priority;
467          if (run->Priority) {
468             priority = run->Priority;
469          }
470          if (run->storage) {
471             store = run->storage;
472          } else {
473             store = (STORE *)job->storage->first();
474          }
475          if (!hdr_printed) {
476             prt_runhdr(ua);
477             hdr_printed = true;
478          }
479          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
480          sp->job = job;
481          sp->level = level;
482          sp->priority = priority;
483          sp->runtime = runtime;
484          sp->pool = run->pool;
485          sp->store = store;
486          sched.binary_insert_multiple(sp, my_compare);
487          num_jobs++;
488       }
489    } /* end for loop over resources */
490    UnlockRes();
491    foreach_dlist(sp, &sched) {
492       prt_runtime(ua, sp);
493    }
494    if (num_jobs == 0) {
495       bsendmsg(ua, _("No Scheduled Jobs.\n"));
496    }
497    bsendmsg(ua, _("====\n"));
498    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
499 }
500
501 static void list_running_jobs(UAContext *ua)
502 {
503    JCR *jcr;
504    int njobs = 0;
505    const char *msg;
506    char *emsg;                        /* edited message */
507    char dt[MAX_TIME_LENGTH];
508    char level[10];
509    bool pool_mem = false;
510
511    Dmsg0(200, "enter list_run_jobs()\n");
512    bsendmsg(ua, _("\nRunning Jobs:\n"));
513    foreach_jcr(jcr) {
514       if (jcr->JobId == 0) {      /* this is us */
515          /* this is a console or other control job. We only show console
516           * jobs in the status output.
517           */
518          if (jcr->JobType == JT_CONSOLE) {
519             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
520             bsendmsg(ua, _("Console connected at %s\n"), dt);
521          }
522          continue;
523       }       
524       njobs++;
525    }
526    endeach_jcr(jcr);
527
528    if (njobs == 0) {
529       /* Note the following message is used in regress -- don't change */
530       bsendmsg(ua, _("No Jobs running.\n====\n"));
531       Dmsg0(200, "leave list_run_jobs()\n");
532       return;
533    }
534    njobs = 0;
535    bsendmsg(ua, _(" JobId Level   Name                       Status\n"));
536    bsendmsg(ua, _("======================================================================\n"));
537    foreach_jcr(jcr) {
538       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
539          continue;
540       }
541       njobs++;
542       switch (jcr->JobStatus) {
543       case JS_Created:
544          msg = _("is waiting execution");
545          break;
546       case JS_Running:
547          msg = _("is running");
548          break;
549       case JS_Blocked:
550          msg = _("is blocked");
551          break;
552       case JS_Terminated:
553          msg = _("has terminated");
554          break;
555       case JS_ErrorTerminated:
556          msg = _("has erred");
557          break;
558       case JS_Error:
559          msg = _("has errors");
560          break;
561       case JS_FatalError:
562          msg = _("has a fatal error");
563          break;
564       case JS_Differences:
565          msg = _("has verify differences");
566          break;
567       case JS_Canceled:
568          msg = _("has been canceled");
569          break;
570       case JS_WaitFD:
571          emsg = (char *) get_pool_memory(PM_FNAME);
572          Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
573          pool_mem = true;
574          msg = emsg;
575          break;
576       case JS_WaitSD:
577          emsg = (char *) get_pool_memory(PM_FNAME);
578          if (jcr->wstore) {
579             Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
580          } else {
581             Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
582          }
583          pool_mem = true;
584          msg = emsg;
585          break;
586       case JS_WaitStoreRes:
587          msg = _("is waiting on max Storage jobs");
588          break;
589       case JS_WaitClientRes:
590          msg = _("is waiting on max Client jobs");
591          break;
592       case JS_WaitJobRes:
593          msg = _("is waiting on max Job jobs");
594          break;
595       case JS_WaitMaxJobs:
596          msg = _("is waiting on max total jobs");
597          break;
598       case JS_WaitStartTime:
599          msg = _("is waiting for its start time");
600          break;
601       case JS_WaitPriority:
602          msg = _("is waiting for higher priority jobs to finish");
603          break;
604
605       default:
606          emsg = (char *) get_pool_memory(PM_FNAME);
607          Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
608          pool_mem = true;
609          msg = emsg;
610          break;
611       }
612       /*
613        * Now report Storage daemon status code
614        */
615       switch (jcr->SDJobStatus) {
616       case JS_WaitMount:
617          if (pool_mem) {
618             free_pool_memory(emsg);
619             pool_mem = false;
620          }
621          msg = _("is waiting for a mount request");
622          break;
623       case JS_WaitMedia:
624          if (pool_mem) {
625             free_pool_memory(emsg);
626             pool_mem = false;
627          }
628          msg = _("is waiting for an appendable Volume");
629          break;
630       case JS_WaitFD:
631          if (!pool_mem) {
632             emsg = (char *) get_pool_memory(PM_FNAME);
633             pool_mem = true;
634          }
635          Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
636               jcr->client->name(), jcr->wstore->name());
637          msg = emsg;
638          break;
639       }
640       switch (jcr->JobType) {
641       case JT_ADMIN:
642       case JT_RESTORE:
643          bstrncpy(level, "      ", sizeof(level));
644          break;
645       default:
646          bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
647          level[7] = 0;
648          break;
649       }
650
651       bsendmsg(ua, _("%6d %-6s  %-20s %s\n"),
652          jcr->JobId,
653          level,
654          jcr->Job,
655          msg);
656
657       if (pool_mem) {
658          free_pool_memory(emsg);
659          pool_mem = false;
660       }
661    }
662    endeach_jcr(jcr);
663    bsendmsg(ua, _("====\n"));
664    Dmsg0(200, "leave list_run_jobs()\n");
665 }
666
667 static void list_terminated_jobs(UAContext *ua)
668 {
669    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
670    char level[10];
671
672    if (last_jobs->empty()) {
673       bsendmsg(ua, _("No Terminated Jobs.\n"));
674       return;
675    }
676    lock_last_jobs_list();
677    struct s_last_job *je;
678    bsendmsg(ua, _("\nTerminated Jobs:\n"));
679    bsendmsg(ua, _(" JobId  Level    Files      Bytes   Status   Finished        Name \n"));
680    bsendmsg(ua, _("====================================================================\n"));
681    foreach_dlist(je, last_jobs) {
682       char JobName[MAX_NAME_LENGTH];
683       const char *termstat;
684
685       bstrncpy(JobName, je->Job, sizeof(JobName));
686       /* There are three periods after the Job name */
687       char *p;
688       for (int i=0; i<3; i++) {
689          if ((p=strrchr(JobName, '.')) != NULL) {
690             *p = 0;
691          }
692       }
693
694       if (!acl_access_ok(ua, Job_ACL, JobName)) {
695          continue;
696       }
697
698       bstrftime_nc(dt, sizeof(dt), je->end_time);
699       switch (je->JobType) {
700       case JT_ADMIN:
701       case JT_RESTORE:
702          bstrncpy(level, "    ", sizeof(level));
703          break;
704       default:
705          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
706          level[4] = 0;
707          break;
708       }
709       switch (je->JobStatus) {
710       case JS_Created:
711          termstat = _("Created");
712          break;
713       case JS_FatalError:
714       case JS_ErrorTerminated:
715          termstat = _("Error");
716          break;
717       case JS_Differences:
718          termstat = _("Diffs");
719          break;
720       case JS_Canceled:
721          termstat = _("Cancel");
722          break;
723       case JS_Terminated:
724          termstat = _("OK");
725          break;
726       default:
727          termstat = _("Other");
728          break;
729       }
730       bsendmsg(ua, _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
731          je->JobId,
732          level,
733          edit_uint64_with_commas(je->JobFiles, b1),
734          edit_uint64_with_suffix(je->JobBytes, b2),
735          termstat,
736          dt, JobName);
737    }
738    bsendmsg(ua, _("\n"));
739    unlock_last_jobs_list();
740 }