]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
======================= Warning ==========================
[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
241    bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
242             HOST_OS, DISTNAME, DISTVER);
243    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
244    if (num_jobs_run == 1) {
245       bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
246    }
247    else {
248       bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
249         dt, num_jobs_run);
250    }
251    if (debug_level > 0) {
252       char b1[35], b2[35], b3[35], b4[35];
253       bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
254             edit_uint64_with_commas(sm_bytes, b1),
255             edit_uint64_with_commas(sm_max_bytes, b2),
256             edit_uint64_with_commas(sm_buffers, b3),
257             edit_uint64_with_commas(sm_max_buffers, b4));
258    }
259    /*
260     * List scheduled Jobs
261     */
262    list_scheduled_jobs(ua);
263
264    /*
265     * List running jobs
266     */
267    list_running_jobs(ua);
268
269    /*
270     * List terminated jobs
271     */
272    list_terminated_jobs(ua);
273    bsendmsg(ua, _("====\n"));
274 }
275
276 static void do_storage_status(UAContext *ua, STORE *store)
277 {
278    BSOCK *sd;
279
280    set_wstorage(ua->jcr, store);
281    /* Try connecting for up to 15 seconds */
282    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
283       store->name(), store->address, store->SDport);
284    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
285       bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
286          store->name());
287       if (ua->jcr->store_bsock) {
288          bnet_close(ua->jcr->store_bsock);
289          ua->jcr->store_bsock = NULL;
290       }
291       return;
292    }
293    Dmsg0(20, _("Connected to storage daemon\n"));
294    sd = ua->jcr->store_bsock;
295    bnet_fsend(sd, "status");
296    while (bnet_recv(sd) >= 0) {
297       bsendmsg(ua, "%s", sd->msg);
298    }
299    bnet_sig(sd, BNET_TERMINATE);
300    bnet_close(sd);
301    ua->jcr->store_bsock = NULL;
302    return;
303 }
304
305 static void do_client_status(UAContext *ua, CLIENT *client)
306 {
307    BSOCK *fd;
308
309    /* Connect to File daemon */
310
311    ua->jcr->client = client;
312    /* Release any old dummy key */
313    if (ua->jcr->sd_auth_key) {
314       free(ua->jcr->sd_auth_key);
315    }
316    /* Create a new dummy SD auth key */
317    ua->jcr->sd_auth_key = bstrdup("dummy");
318
319    /* Try to connect for 15 seconds */
320    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
321       client->name(), client->address, client->FDport);
322    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
323       bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
324          client->name());
325       if (ua->jcr->file_bsock) {
326          bnet_close(ua->jcr->file_bsock);
327          ua->jcr->file_bsock = NULL;
328       }
329       return;
330    }
331    Dmsg0(20, _("Connected to file daemon\n"));
332    fd = ua->jcr->file_bsock;
333    bnet_fsend(fd, "status");
334    while (bnet_recv(fd) >= 0) {
335       bsendmsg(ua, "%s", fd->msg);
336    }
337    bnet_sig(fd, BNET_TERMINATE);
338    bnet_close(fd);
339    ua->jcr->file_bsock = NULL;
340
341    return;
342 }
343
344 static void prt_runhdr(UAContext *ua)
345 {
346    bsendmsg(ua, _("\nScheduled Jobs:\n"));
347    bsendmsg(ua, _("Level          Type     Pri  Scheduled          Name               Volume\n"));
348    bsendmsg(ua, _("===================================================================================\n"));
349 }
350
351 /* Scheduling packet */
352 struct sched_pkt {
353    dlink link;                        /* keep this as first item!!! */
354    JOB *job;
355    int level;
356    int priority;
357    time_t runtime;
358    POOL *pool;
359    STORE *store;
360 };
361
362 static void prt_runtime(UAContext *ua, sched_pkt *sp)
363 {
364    char dt[MAX_TIME_LENGTH];
365    const char *level_ptr;
366    bool ok = false;
367    bool close_db = false;
368    JCR *jcr = ua->jcr;
369    MEDIA_DBR mr;
370
371    memset(&mr, 0, sizeof(mr));
372    if (sp->job->JobType == JT_BACKUP) {
373       jcr->db = NULL;
374       ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
375       if (jcr->db) {
376          close_db = true;             /* new db opened, remember to close it */
377       }
378       if (ok) {
379          mr.PoolId = jcr->jr.PoolId;
380          mr.StorageId = sp->store->StorageId;
381          ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
382       }
383       if (!ok) {
384          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
385       }
386    }
387    bstrftime_nc(dt, sizeof(dt), sp->runtime);
388    switch (sp->job->JobType) {
389    case JT_ADMIN:
390    case JT_RESTORE:
391       level_ptr = " ";
392       break;
393    default:
394       level_ptr = level_to_str(sp->level);
395       break;
396    }
397    bsendmsg(ua, _("%-14s %-8s %3d  %-18s %-18s %s\n"),
398       level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
399       sp->job->name(), mr.VolumeName);
400    if (close_db) {
401       db_close_database(jcr, jcr->db);
402    }
403    jcr->db = ua->db;                  /* restore ua db to jcr */
404
405 }
406
407 /*
408  * Sort items by runtime, priority
409  */
410 static int my_compare(void *item1, void *item2)
411 {
412    sched_pkt *p1 = (sched_pkt *)item1;
413    sched_pkt *p2 = (sched_pkt *)item2;
414    if (p1->runtime < p2->runtime) {
415       return -1;
416    } else if (p1->runtime > p2->runtime) {
417       return 1;
418    }
419    if (p1->priority < p2->priority) {
420       return -1;
421    } else if (p1->priority > p2->priority) {
422       return 1;
423    }
424    return 0;
425 }
426
427 /*
428  * Find all jobs to be run in roughly the
429  *  next 24 hours.
430  */
431 static void list_scheduled_jobs(UAContext *ua)
432 {
433    time_t runtime;
434    RUN *run;
435    JOB *job;
436    STORE* store;
437    int level, num_jobs = 0;
438    int priority;
439    bool hdr_printed = false;
440    dlist sched;
441    sched_pkt *sp;
442    int days, i;
443
444    Dmsg0(200, "enter list_sched_jobs()\n");
445
446    days = 1;
447    i = find_arg_with_value(ua, NT_("days"));
448    if (i >= 0) {
449      days = atoi(ua->argv[i]);
450      if ((days < 0) || (days > 50)) {
451        bsendmsg(ua, _("Ignoring illegal value for days.\n"));
452        days = 1;
453      }
454    }
455
456    /* Loop through all jobs */
457    LockRes();
458    foreach_res(job, R_JOB) {
459       if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
460          continue;
461       }
462       for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
463          level = job->JobLevel;
464          if (run->level) {
465             level = run->level;
466          }
467          priority = job->Priority;
468          if (run->Priority) {
469             priority = run->Priority;
470          }
471          if (run->storage) {
472             store = run->storage;
473          } else {
474             store = (STORE *)job->storage->first();
475          }
476          if (!hdr_printed) {
477             prt_runhdr(ua);
478             hdr_printed = true;
479          }
480          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
481          sp->job = job;
482          sp->level = level;
483          sp->priority = priority;
484          sp->runtime = runtime;
485          sp->pool = run->pool;
486          sp->store = store;
487          sched.binary_insert_multiple(sp, my_compare);
488          num_jobs++;
489       }
490    } /* end for loop over resources */
491    UnlockRes();
492    foreach_dlist(sp, &sched) {
493       prt_runtime(ua, sp);
494    }
495    if (num_jobs == 0) {
496       bsendmsg(ua, _("No Scheduled Jobs.\n"));
497    }
498    bsendmsg(ua, _("====\n"));
499    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
500 }
501
502 static void list_running_jobs(UAContext *ua)
503 {
504    JCR *jcr;
505    int njobs = 0;
506    const char *msg;
507    char *emsg;                        /* edited message */
508    char dt[MAX_TIME_LENGTH];
509    char level[10];
510    bool pool_mem = false;
511
512    Dmsg0(200, "enter list_run_jobs()\n");
513    bsendmsg(ua, _("\nRunning Jobs:\n"));
514    foreach_jcr(jcr) {
515       if (jcr->JobId == 0) {      /* this is us */
516          /* this is a console or other control job. We only show console
517           * jobs in the status output.
518           */
519          if (jcr->JobType == JT_CONSOLE) {
520             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
521             bsendmsg(ua, _("Console connected at %s\n"), dt);
522          }
523          continue;
524       }       
525       njobs++;
526    }
527    endeach_jcr(jcr);
528
529    if (njobs == 0) {
530       /* Note the following message is used in regress -- don't change */
531       bsendmsg(ua, _("No Jobs running.\n====\n"));
532       Dmsg0(200, "leave list_run_jobs()\n");
533       return;
534    }
535    njobs = 0;
536    bsendmsg(ua, _(" JobId Level   Name                       Status\n"));
537    bsendmsg(ua, _("======================================================================\n"));
538    foreach_jcr(jcr) {
539       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
540          continue;
541       }
542       njobs++;
543       switch (jcr->JobStatus) {
544       case JS_Created:
545          msg = _("is waiting execution");
546          break;
547       case JS_Running:
548          msg = _("is running");
549          break;
550       case JS_Blocked:
551          msg = _("is blocked");
552          break;
553       case JS_Terminated:
554          msg = _("has terminated");
555          break;
556       case JS_ErrorTerminated:
557          msg = _("has erred");
558          break;
559       case JS_Error:
560          msg = _("has errors");
561          break;
562       case JS_FatalError:
563          msg = _("has a fatal error");
564          break;
565       case JS_Differences:
566          msg = _("has verify differences");
567          break;
568       case JS_Canceled:
569          msg = _("has been canceled");
570          break;
571       case JS_WaitFD:
572          emsg = (char *) get_pool_memory(PM_FNAME);
573          Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
574          pool_mem = true;
575          msg = emsg;
576          break;
577       case JS_WaitSD:
578          emsg = (char *) get_pool_memory(PM_FNAME);
579          if (jcr->wstore) {
580             Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
581          } else {
582             Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
583          }
584          pool_mem = true;
585          msg = emsg;
586          break;
587       case JS_WaitStoreRes:
588          msg = _("is waiting on max Storage jobs");
589          break;
590       case JS_WaitClientRes:
591          msg = _("is waiting on max Client jobs");
592          break;
593       case JS_WaitJobRes:
594          msg = _("is waiting on max Job jobs");
595          break;
596       case JS_WaitMaxJobs:
597          msg = _("is waiting on max total jobs");
598          break;
599       case JS_WaitStartTime:
600          msg = _("is waiting for its start time");
601          break;
602       case JS_WaitPriority:
603          msg = _("is waiting for higher priority jobs to finish");
604          break;
605
606       default:
607          emsg = (char *) get_pool_memory(PM_FNAME);
608          Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
609          pool_mem = true;
610          msg = emsg;
611          break;
612       }
613       /*
614        * Now report Storage daemon status code
615        */
616       switch (jcr->SDJobStatus) {
617       case JS_WaitMount:
618          if (pool_mem) {
619             free_pool_memory(emsg);
620             pool_mem = false;
621          }
622          msg = _("is waiting for a mount request");
623          break;
624       case JS_WaitMedia:
625          if (pool_mem) {
626             free_pool_memory(emsg);
627             pool_mem = false;
628          }
629          msg = _("is waiting for an appendable Volume");
630          break;
631       case JS_WaitFD:
632          if (!pool_mem) {
633             emsg = (char *) get_pool_memory(PM_FNAME);
634             pool_mem = true;
635          }
636          Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
637               jcr->client->name(), jcr->wstore->name());
638          msg = emsg;
639          break;
640       }
641       switch (jcr->JobType) {
642       case JT_ADMIN:
643       case JT_RESTORE:
644          bstrncpy(level, "      ", sizeof(level));
645          break;
646       default:
647          bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
648          level[7] = 0;
649          break;
650       }
651
652       bsendmsg(ua, _("%6d %-6s  %-20s %s\n"),
653          jcr->JobId,
654          level,
655          jcr->Job,
656          msg);
657
658       if (pool_mem) {
659          free_pool_memory(emsg);
660          pool_mem = false;
661       }
662    }
663    endeach_jcr(jcr);
664    bsendmsg(ua, _("====\n"));
665    Dmsg0(200, "leave list_run_jobs()\n");
666 }
667
668 static void list_terminated_jobs(UAContext *ua)
669 {
670    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
671    char level[10];
672
673    if (last_jobs->empty()) {
674       bsendmsg(ua, _("No Terminated Jobs.\n"));
675       return;
676    }
677    lock_last_jobs_list();
678    struct s_last_job *je;
679    bsendmsg(ua, _("\nTerminated Jobs:\n"));
680    bsendmsg(ua, _(" JobId  Level     Files      Bytes     Status   Finished        Name \n"));
681    bsendmsg(ua, _("========================================================================\n"));
682    foreach_dlist(je, last_jobs) {
683       char JobName[MAX_NAME_LENGTH];
684       const char *termstat;
685
686       bstrncpy(JobName, je->Job, sizeof(JobName));
687       /* There are three periods after the Job name */
688       char *p;
689       for (int i=0; i<3; i++) {
690          if ((p=strrchr(JobName, '.')) != NULL) {
691             *p = 0;
692          }
693       }
694
695       if (!acl_access_ok(ua, Job_ACL, JobName)) {
696          continue;
697       }
698
699       bstrftime_nc(dt, sizeof(dt), je->end_time);
700       switch (je->JobType) {
701       case JT_ADMIN:
702       case JT_RESTORE:
703          bstrncpy(level, "    ", sizeof(level));
704          break;
705       default:
706          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
707          level[4] = 0;
708          break;
709       }
710       switch (je->JobStatus) {
711       case JS_Created:
712          termstat = _("Created");
713          break;
714       case JS_FatalError:
715       case JS_ErrorTerminated:
716          termstat = _("Error");
717          break;
718       case JS_Differences:
719          termstat = _("Diffs");
720          break;
721       case JS_Canceled:
722          termstat = _("Cancel");
723          break;
724       case JS_Terminated:
725          termstat = _("OK");
726          break;
727       default:
728          termstat = _("Other");
729          break;
730       }
731       bsendmsg(ua, _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"),
732          je->JobId,
733          level,
734          edit_uint64_with_commas(je->JobFiles, b1),
735          edit_uint64_with_commas(je->JobBytes, b2),
736          termstat,
737          dt, JobName);
738    }
739    bsendmsg(ua, _("\n"));
740    unlock_last_jobs_list();
741 }