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