]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
- Implement search for exact Volume in reservation before
[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-2005 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          free_jcr(njcr);
70       }
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(njcr->JobId, ed1), 
76                   njcr->JobStatus, njcr->JobErrors);
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->PoolId;
383          ok = find_next_volume_for_append(jcr, &mr, 0);
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
444    Dmsg0(200, "enter list_sched_jobs()\n");
445
446    /* Loop through all jobs */
447    LockRes();
448    foreach_res(job, R_JOB) {
449       if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
450          continue;
451       }
452       for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
453          level = job->JobLevel;
454          if (run->level) {
455             level = run->level;
456          }
457          priority = job->Priority;
458          if (run->Priority) {
459             priority = run->Priority;
460          }
461          if (!hdr_printed) {
462             prt_runhdr(ua);
463             hdr_printed = true;
464          }
465          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
466          sp->job = job;
467          sp->level = level;
468          sp->priority = priority;
469          sp->runtime = runtime;
470          sp->pool = run->pool;
471          sched.binary_insert_multiple(sp, my_compare);
472          num_jobs++;
473       }
474    } /* end for loop over resources */
475    UnlockRes();
476    foreach_dlist(sp, &sched) {
477       prt_runtime(ua, sp);
478    }
479    if (num_jobs == 0) {
480       bsendmsg(ua, _("No Scheduled Jobs.\n"));
481    }
482    bsendmsg(ua, _("====\n"));
483    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
484 }
485
486 static void list_running_jobs(UAContext *ua)
487 {
488    JCR *jcr;
489    int njobs = 0;
490    const char *msg;
491    char *emsg;                        /* edited message */
492    char dt[MAX_TIME_LENGTH];
493    char level[10];
494    bool pool_mem = false;
495
496    Dmsg0(200, "enter list_run_jobs()\n");
497    bsendmsg(ua, _("\nRunning Jobs:\n"));
498    foreach_jcr(jcr) {
499       njobs++;
500       if (jcr->JobId == 0) {      /* this is us */
501          /* this is a console or other control job. We only show console
502           * jobs in the status output.
503           */
504          if (jcr->JobType == JT_CONSOLE) {
505             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
506             bsendmsg(ua, _("Console connected at %s\n"), dt);
507          }
508          njobs--;
509       }
510       free_jcr(jcr);
511    }
512    if (njobs == 0) {
513       /* Note the following message is used in regress -- don't change */
514       bsendmsg(ua, _("No Jobs running.\n====\n"));
515       Dmsg0(200, "leave list_run_jobs()\n");
516       return;
517    }
518    njobs = 0;
519    bsendmsg(ua, _(" JobId Level   Name                       Status\n"));
520    bsendmsg(ua, _("======================================================================\n"));
521    foreach_jcr(jcr) {
522       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
523          free_jcr(jcr);
524          continue;
525       }
526       njobs++;
527       switch (jcr->JobStatus) {
528       case JS_Created:
529          msg = _("is waiting execution");
530          break;
531       case JS_Running:
532          msg = _("is running");
533          break;
534       case JS_Blocked:
535          msg = _("is blocked");
536          break;
537       case JS_Terminated:
538          msg = _("has terminated");
539          break;
540       case JS_ErrorTerminated:
541          msg = _("has erred");
542          break;
543       case JS_Error:
544          msg = _("has errors");
545          break;
546       case JS_FatalError:
547          msg = _("has a fatal error");
548          break;
549       case JS_Differences:
550          msg = _("has verify differences");
551          break;
552       case JS_Canceled:
553          msg = _("has been canceled");
554          break;
555       case JS_WaitFD:
556          emsg = (char *) get_pool_memory(PM_FNAME);
557          Mmsg(emsg, _("is waiting on Client %s"), jcr->client->hdr.name);
558          pool_mem = true;
559          msg = emsg;
560          break;
561       case JS_WaitSD:
562          emsg = (char *) get_pool_memory(PM_FNAME);
563          Mmsg(emsg, _("is waiting on Storage %s"), jcr->store->hdr.name);
564          pool_mem = true;
565          msg = emsg;
566          break;
567       case JS_WaitStoreRes:
568          msg = _("is waiting on max Storage jobs");
569          break;
570       case JS_WaitClientRes:
571          msg = _("is waiting on max Client jobs");
572          break;
573       case JS_WaitJobRes:
574          msg = _("is waiting on max Job jobs");
575          break;
576       case JS_WaitMaxJobs:
577          msg = _("is waiting on max total jobs");
578          break;
579       case JS_WaitStartTime:
580          msg = _("is waiting for its start time");
581          break;
582       case JS_WaitPriority:
583          msg = _("is waiting for higher priority jobs to finish");
584          break;
585
586       default:
587          emsg = (char *) get_pool_memory(PM_FNAME);
588          Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
589          pool_mem = true;
590          msg = emsg;
591          break;
592       }
593       /*
594        * Now report Storage daemon status code
595        */
596       switch (jcr->SDJobStatus) {
597       case JS_WaitMount:
598          if (pool_mem) {
599             free_pool_memory(emsg);
600             pool_mem = false;
601          }
602          msg = _("is waiting for a mount request");
603          break;
604       case JS_WaitMedia:
605          if (pool_mem) {
606             free_pool_memory(emsg);
607             pool_mem = false;
608          }
609          msg = _("is waiting for an appendable Volume");
610          break;
611       case JS_WaitFD:
612          if (!pool_mem) {
613             emsg = (char *) get_pool_memory(PM_FNAME);
614             pool_mem = true;
615          }
616          Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
617               jcr->client->hdr.name, jcr->store->hdr.name);
618          msg = emsg;
619          break;
620       }
621       switch (jcr->JobType) {
622       case JT_ADMIN:
623       case JT_RESTORE:
624          bstrncpy(level, "      ", sizeof(level));
625          break;
626       default:
627          bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
628          level[7] = 0;
629          break;
630       }
631
632       bsendmsg(ua, _("%6d %-6s  %-20s %s\n"),
633          jcr->JobId,
634          level,
635          jcr->Job,
636          msg);
637
638       if (pool_mem) {
639          free_pool_memory(emsg);
640          pool_mem = false;
641       }
642       free_jcr(jcr);
643    }
644    bsendmsg(ua, _("====\n"));
645    Dmsg0(200, "leave list_run_jobs()\n");
646 }
647
648 static void list_terminated_jobs(UAContext *ua)
649 {
650    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
651    char level[10];
652
653    if (last_jobs->empty()) {
654       bsendmsg(ua, _("No Terminated Jobs.\n"));
655       return;
656    }
657    lock_last_jobs_list();
658    struct s_last_job *je;
659    bsendmsg(ua, _("\nTerminated Jobs:\n"));
660    bsendmsg(ua, _(" JobId  Level     Files      Bytes     Status   Finished        Name \n"));
661    bsendmsg(ua, _("========================================================================\n"));
662    foreach_dlist(je, last_jobs) {
663       char JobName[MAX_NAME_LENGTH];
664       const char *termstat;
665
666       bstrncpy(JobName, je->Job, sizeof(JobName));
667       /* There are three periods after the Job name */
668       char *p;
669       for (int i=0; i<3; i++) {
670          if ((p=strrchr(JobName, '.')) != NULL) {
671             *p = 0;
672          }
673       }
674
675       if (!acl_access_ok(ua, Job_ACL, JobName)) {
676          continue;
677       }
678
679       bstrftime_nc(dt, sizeof(dt), je->end_time);
680       switch (je->JobType) {
681       case JT_ADMIN:
682       case JT_RESTORE:
683          bstrncpy(level, "    ", sizeof(level));
684          break;
685       default:
686          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
687          level[4] = 0;
688          break;
689       }
690       switch (je->JobStatus) {
691       case JS_Created:
692          termstat = _("Created");
693          break;
694       case JS_FatalError:
695       case JS_ErrorTerminated:
696          termstat = _("Error");
697          break;
698       case JS_Differences:
699          termstat = _("Diffs");
700          break;
701       case JS_Canceled:
702          termstat = _("Cancel");
703          break;
704       case JS_Terminated:
705          termstat = _("OK");
706          break;
707       default:
708          termstat = _("Other");
709          break;
710       }
711       bsendmsg(ua, _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"),
712          je->JobId,
713          level,
714          edit_uint64_with_commas(je->JobFiles, b1),
715          edit_uint64_with_commas(je->JobBytes, b2),
716          termstat,
717          dt, JobName);
718    }
719    bsendmsg(ua, _("\n"));
720    unlock_last_jobs_list();
721 }