]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
This commit was manufactured by cvs2svn to create tag
[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    STORE *store;
364 };
365
366 static void prt_runtime(UAContext *ua, sched_pkt *sp)
367 {
368    char dt[MAX_TIME_LENGTH];
369    const char *level_ptr;
370    bool ok = false;
371    bool close_db = false;
372    JCR *jcr = ua->jcr;
373    MEDIA_DBR mr;
374
375    memset(&mr, 0, sizeof(mr));
376    if (sp->job->JobType == JT_BACKUP) {
377       jcr->db = NULL;
378       ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
379       if (jcr->db) {
380          close_db = true;             /* new db opened, remember to close it */
381       }
382       if (ok) {
383          mr.PoolId = jcr->PoolId;
384          mr.StorageId = sp->store->StorageId;
385          ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
386       }
387       if (!ok) {
388          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
389       }
390    }
391    bstrftime_nc(dt, sizeof(dt), sp->runtime);
392    switch (sp->job->JobType) {
393    case JT_ADMIN:
394    case JT_RESTORE:
395       level_ptr = " ";
396       break;
397    default:
398       level_ptr = level_to_str(sp->level);
399       break;
400    }
401    bsendmsg(ua, _("%-14s %-8s %3d  %-18s %-18s %s\n"),
402       level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
403       sp->job->hdr.name, mr.VolumeName);
404    if (close_db) {
405       db_close_database(jcr, jcr->db);
406    }
407    jcr->db = ua->db;                  /* restore ua db to jcr */
408
409 }
410
411 /*
412  * Sort items by runtime, priority
413  */
414 static int my_compare(void *item1, void *item2)
415 {
416    sched_pkt *p1 = (sched_pkt *)item1;
417    sched_pkt *p2 = (sched_pkt *)item2;
418    if (p1->runtime < p2->runtime) {
419       return -1;
420    } else if (p1->runtime > p2->runtime) {
421       return 1;
422    }
423    if (p1->priority < p2->priority) {
424       return -1;
425    } else if (p1->priority > p2->priority) {
426       return 1;
427    }
428    return 0;
429 }
430
431 /*
432  * Find all jobs to be run in roughly the
433  *  next 24 hours.
434  */
435 static void list_scheduled_jobs(UAContext *ua)
436 {
437    time_t runtime;
438    RUN *run;
439    JOB *job;
440    STORE* store;
441    int level, num_jobs = 0;
442    int priority;
443    bool hdr_printed = false;
444    dlist sched;
445    sched_pkt *sp;
446    int days, i;
447
448    Dmsg0(200, "enter list_sched_jobs()\n");
449
450    days = 1;
451    i = find_arg_with_value(ua, N_("days"));
452    if (i >= 0) {
453      days = atoi(ua->argv[i]);
454      if ((days < 0) || (days > 50)) {
455        bsendmsg(ua, _("Ignoring illegal value for days.\n"));
456        days = 1;
457      }
458    }
459
460    /* Loop through all jobs */
461    LockRes();
462    foreach_res(job, R_JOB) {
463       if (!acl_access_ok(ua, Job_ACL, job->hdr.name) || !job->enabled) {
464          continue;
465       }
466       for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
467          level = job->JobLevel;
468          if (run->level) {
469             level = run->level;
470          }
471          priority = job->Priority;
472          if (run->Priority) {
473             priority = run->Priority;
474          }
475          if (run->storage) {
476             store = run->storage;
477          } else {
478             store = (STORE *)job->storage->first();
479          }
480          if (!hdr_printed) {
481             prt_runhdr(ua);
482             hdr_printed = true;
483          }
484          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
485          sp->job = job;
486          sp->level = level;
487          sp->priority = priority;
488          sp->runtime = runtime;
489          sp->pool = run->pool;
490          sp->store = store;
491          sched.binary_insert_multiple(sp, my_compare);
492          num_jobs++;
493       }
494    } /* end for loop over resources */
495    UnlockRes();
496    foreach_dlist(sp, &sched) {
497       prt_runtime(ua, sp);
498    }
499    if (num_jobs == 0) {
500       bsendmsg(ua, _("No Scheduled Jobs.\n"));
501    }
502    bsendmsg(ua, _("====\n"));
503    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
504 }
505
506 static void list_running_jobs(UAContext *ua)
507 {
508    JCR *jcr;
509    int njobs = 0;
510    const char *msg;
511    char *emsg;                        /* edited message */
512    char dt[MAX_TIME_LENGTH];
513    char level[10];
514    bool pool_mem = false;
515
516    Dmsg0(200, "enter list_run_jobs()\n");
517    bsendmsg(ua, _("\nRunning Jobs:\n"));
518    foreach_jcr(jcr) {
519       if (jcr->JobId == 0) {      /* this is us */
520          /* this is a console or other control job. We only show console
521           * jobs in the status output.
522           */
523          if (jcr->JobType == JT_CONSOLE) {
524             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
525             bsendmsg(ua, _("Console connected at %s\n"), dt);
526          }
527          continue;
528       }       
529       njobs++;
530    }
531    endeach_jcr(jcr);
532
533    if (njobs == 0) {
534       /* Note the following message is used in regress -- don't change */
535       bsendmsg(ua, _("No Jobs running.\n====\n"));
536       Dmsg0(200, "leave list_run_jobs()\n");
537       return;
538    }
539    njobs = 0;
540    bsendmsg(ua, _(" JobId Level   Name                       Status\n"));
541    bsendmsg(ua, _("======================================================================\n"));
542    foreach_jcr(jcr) {
543       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
544          continue;
545       }
546       njobs++;
547       switch (jcr->JobStatus) {
548       case JS_Created:
549          msg = _("is waiting execution");
550          break;
551       case JS_Running:
552          msg = _("is running");
553          break;
554       case JS_Blocked:
555          msg = _("is blocked");
556          break;
557       case JS_Terminated:
558          msg = _("has terminated");
559          break;
560       case JS_ErrorTerminated:
561          msg = _("has erred");
562          break;
563       case JS_Error:
564          msg = _("has errors");
565          break;
566       case JS_FatalError:
567          msg = _("has a fatal error");
568          break;
569       case JS_Differences:
570          msg = _("has verify differences");
571          break;
572       case JS_Canceled:
573          msg = _("has been canceled");
574          break;
575       case JS_WaitFD:
576          emsg = (char *) get_pool_memory(PM_FNAME);
577          Mmsg(emsg, _("is waiting on Client %s"), jcr->client->hdr.name);
578          pool_mem = true;
579          msg = emsg;
580          break;
581       case JS_WaitSD:
582          emsg = (char *) get_pool_memory(PM_FNAME);
583          Mmsg(emsg, _("is waiting on Storage %s"), jcr->store->hdr.name);
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->hdr.name, jcr->store->hdr.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 }