]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
bugfix (used static functions instead of dynamic ones)
[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], _("all")) == 0) {
102          do_all_status(ua);
103          return 1;
104       } else if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
105                  strcasecmp(ua->argk[i], _("director")) == 0) {
106          do_director_status(ua);
107          return 1;
108       } else if (strcasecmp(ua->argk[i], _("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, 0);
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, _("Director"));
128       add_prompt(ua, _("Storage"));
129       add_prompt(ua, _("Client"));
130       add_prompt(ua, _("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: " VERSION " (" BDATE ") %s %s %s\n", my_name,
246             HOST_OS, DISTNAME, DISTVER);
247    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
248    bsendmsg(ua, _("Daemon started %s, %d Job%s run since started.\n"),
249         dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
250    if (debug_level > 0) {
251       char b1[35], b2[35], b3[35], b4[35];
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_storage(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->hdr.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->hdr.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->hdr.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->hdr.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 };
359
360 static void prt_runtime(UAContext *ua, sched_pkt *sp)
361 {
362    char dt[MAX_TIME_LENGTH];
363    const char *level_ptr;
364    bool ok = false;
365    bool close_db = false;
366    JCR *jcr = ua->jcr;
367    MEDIA_DBR mr;
368
369    memset(&mr, 0, sizeof(mr));
370    if (sp->job->JobType == JT_BACKUP) {
371       jcr->db = NULL;
372       ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
373       if (jcr->db) {
374          close_db = true;             /* new db opened, remember to close it */
375       }
376       if (ok) {
377          mr.PoolId = jcr->PoolId;
378          ok = find_next_volume_for_append(jcr, &mr, 0);
379       }
380       if (!ok) {
381          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
382       }
383    }
384    bstrftime_nc(dt, sizeof(dt), sp->runtime);
385    switch (sp->job->JobType) {
386    case JT_ADMIN:
387    case JT_RESTORE:
388       level_ptr = " ";
389       break;
390    default:
391       level_ptr = level_to_str(sp->level);
392       break;
393    }
394    bsendmsg(ua, _("%-14s %-8s %3d  %-18s %-18s %s\n"),
395       level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
396       sp->job->hdr.name, mr.VolumeName);
397    if (close_db) {
398       db_close_database(jcr, jcr->db);
399    }
400    jcr->db = ua->db;                  /* restore ua db to jcr */
401
402 }
403
404 /*
405  * Sort items by runtime, priority
406  */
407 static int my_compare(void *item1, void *item2)
408 {
409    sched_pkt *p1 = (sched_pkt *)item1;
410    sched_pkt *p2 = (sched_pkt *)item2;
411    if (p1->runtime < p2->runtime) {
412       return -1;
413    } else if (p1->runtime > p2->runtime) {
414       return 1;
415    }
416    if (p1->priority < p2->priority) {
417       return -1;
418    } else if (p1->priority > p2->priority) {
419       return 1;
420    }
421    return 0;
422 }
423
424 /*
425  * Find all jobs to be run in roughly the
426  *  next 24 hours.
427  */
428 static void list_scheduled_jobs(UAContext *ua)
429 {
430    time_t runtime;
431    RUN *run;
432    JOB *job;
433    int level, num_jobs = 0;
434    int priority;
435    bool hdr_printed = false;
436    dlist sched;
437    sched_pkt *sp;
438
439    Dmsg0(200, "enter list_sched_jobs()\n");
440
441    /* Loop through all jobs */
442    LockRes();
443    foreach_res(job, R_JOB) {
444       if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
445          continue;
446       }
447       for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
448          level = job->JobLevel;
449          if (run->level) {
450             level = run->level;
451          }
452          priority = job->Priority;
453          if (run->Priority) {
454             priority = run->Priority;
455          }
456          if (!hdr_printed) {
457             prt_runhdr(ua);
458             hdr_printed = true;
459          }
460          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
461          sp->job = job;
462          sp->level = level;
463          sp->priority = priority;
464          sp->runtime = runtime;
465          sp->pool = run->pool;
466          sched.binary_insert_multiple(sp, my_compare);
467          num_jobs++;
468       }
469    } /* end for loop over resources */
470    UnlockRes();
471    foreach_dlist(sp, &sched) {
472       prt_runtime(ua, sp);
473    }
474    if (num_jobs == 0) {
475       bsendmsg(ua, _("No Scheduled Jobs.\n"));
476    }
477    bsendmsg(ua, "====\n");
478    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
479 }
480
481 static void list_running_jobs(UAContext *ua)
482 {
483    JCR *jcr;
484    int njobs = 0;
485    const char *msg;
486    char *emsg;                        /* edited message */
487    char dt[MAX_TIME_LENGTH];
488    char level[10];
489    bool pool_mem = false;
490
491    Dmsg0(200, "enter list_run_jobs()\n");
492    bsendmsg(ua, _("\nRunning Jobs:\n"));
493    foreach_jcr(jcr) {
494       njobs++;
495       if (jcr->JobId == 0) {      /* this is us */
496          /* this is a console or other control job. We only show console
497           * jobs in the status output.
498           */
499          if (jcr->JobType == JT_CONSOLE) {
500             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
501             bsendmsg(ua, _("Console connected at %s\n"), dt);
502          }
503          njobs--;
504       }
505       free_jcr(jcr);
506    }
507    if (njobs == 0) {
508       /* Note the following message is used in regress -- don't change */
509       bsendmsg(ua, _("No Jobs running.\n====\n"));
510       Dmsg0(200, "leave list_run_jobs()\n");
511       return;
512    }
513    njobs = 0;
514    bsendmsg(ua, _(" JobId Level   Name                       Status\n"));
515    bsendmsg(ua, _("======================================================================\n"));
516    foreach_jcr(jcr) {
517       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
518          free_jcr(jcr);
519          continue;
520       }
521       njobs++;
522       switch (jcr->JobStatus) {
523       case JS_Created:
524          msg = _("is waiting execution");
525          break;
526       case JS_Running:
527          msg = _("is running");
528          break;
529       case JS_Blocked:
530          msg = _("is blocked");
531          break;
532       case JS_Terminated:
533          msg = _("has terminated");
534          break;
535       case JS_ErrorTerminated:
536          msg = _("has erred");
537          break;
538       case JS_Error:
539          msg = _("has errors");
540          break;
541       case JS_FatalError:
542          msg = _("has a fatal error");
543          break;
544       case JS_Differences:
545          msg = _("has verify differences");
546          break;
547       case JS_Canceled:
548          msg = _("has been canceled");
549          break;
550       case JS_WaitFD:
551          emsg = (char *) get_pool_memory(PM_FNAME);
552          Mmsg(emsg, _("is waiting on Client %s"), jcr->client->hdr.name);
553          pool_mem = true;
554          msg = emsg;
555          break;
556       case JS_WaitSD:
557          emsg = (char *) get_pool_memory(PM_FNAME);
558          Mmsg(emsg, _("is waiting on Storage %s"), jcr->store->hdr.name);
559          pool_mem = true;
560          msg = emsg;
561          break;
562       case JS_WaitStoreRes:
563          msg = _("is waiting on max Storage jobs");
564          break;
565       case JS_WaitClientRes:
566          msg = _("is waiting on max Client jobs");
567          break;
568       case JS_WaitJobRes:
569          msg = _("is waiting on max Job jobs");
570          break;
571       case JS_WaitMaxJobs:
572          msg = _("is waiting on max total jobs");
573          break;
574       case JS_WaitStartTime:
575          msg = _("is waiting for its start time");
576          break;
577       case JS_WaitPriority:
578          msg = _("is waiting for higher priority jobs to finish");
579          break;
580
581       default:
582          emsg = (char *) get_pool_memory(PM_FNAME);
583          Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
584          pool_mem = true;
585          msg = emsg;
586          break;
587       }
588       /*
589        * Now report Storage daemon status code
590        */
591       switch (jcr->SDJobStatus) {
592       case JS_WaitMount:
593          if (pool_mem) {
594             free_pool_memory(emsg);
595             pool_mem = false;
596          }
597          msg = _("is waiting for a mount request");
598          break;
599       case JS_WaitMedia:
600          if (pool_mem) {
601             free_pool_memory(emsg);
602             pool_mem = false;
603          }
604          msg = _("is waiting for an appendable Volume");
605          break;
606       case JS_WaitFD:
607          if (!pool_mem) {
608             emsg = (char *) get_pool_memory(PM_FNAME);
609             pool_mem = true;
610          }
611          Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
612               jcr->client->hdr.name, jcr->store->hdr.name);
613          msg = emsg;
614          break;
615       }
616       switch (jcr->JobType) {
617       case JT_ADMIN:
618       case JT_RESTORE:
619          bstrncpy(level, "      ", sizeof(level));
620          break;
621       default:
622          bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
623          level[7] = 0;
624          break;
625       }
626
627       bsendmsg(ua, _("%6d %-6s  %-20s %s\n"),
628          jcr->JobId,
629          level,
630          jcr->Job,
631          msg);
632
633       if (pool_mem) {
634          free_pool_memory(emsg);
635          pool_mem = false;
636       }
637       free_jcr(jcr);
638    }
639    bsendmsg(ua, "====\n");
640    Dmsg0(200, "leave list_run_jobs()\n");
641 }
642
643 static void list_terminated_jobs(UAContext *ua)
644 {
645    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
646    char level[10];
647
648    if (last_jobs->empty()) {
649       bsendmsg(ua, _("No Terminated Jobs.\n"));
650       return;
651    }
652    lock_last_jobs_list();
653    struct s_last_job *je;
654    bsendmsg(ua, _("\nTerminated Jobs:\n"));
655    bsendmsg(ua, _(" JobId  Level     Files      Bytes     Status   Finished        Name \n"));
656    bsendmsg(ua, _("========================================================================\n"));
657    foreach_dlist(je, last_jobs) {
658       char JobName[MAX_NAME_LENGTH];
659       const char *termstat;
660
661       bstrncpy(JobName, je->Job, sizeof(JobName));
662       /* There are three periods after the Job name */
663       char *p;
664       for (int i=0; i<3; i++) {
665          if ((p=strrchr(JobName, '.')) != NULL) {
666             *p = 0;
667          }
668       }
669
670       if (!acl_access_ok(ua, Job_ACL, JobName)) {
671          continue;
672       }
673
674       bstrftime_nc(dt, sizeof(dt), je->end_time);
675       switch (je->JobType) {
676       case JT_ADMIN:
677       case JT_RESTORE:
678          bstrncpy(level, "    ", sizeof(level));
679          break;
680       default:
681          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
682          level[4] = 0;
683          break;
684       }
685       switch (je->JobStatus) {
686       case JS_Created:
687          termstat = "Created";
688          break;
689       case JS_FatalError:
690       case JS_ErrorTerminated:
691          termstat = "Error";
692          break;
693       case JS_Differences:
694          termstat = "Diffs";
695          break;
696       case JS_Canceled:
697          termstat = "Cancel";
698          break;
699       case JS_Terminated:
700          termstat = "OK";
701          break;
702       default:
703          termstat = "Other";
704          break;
705       }
706       bsendmsg(ua, _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"),
707          je->JobId,
708          level,
709          edit_uint64_with_commas(je->JobFiles, b1),
710          edit_uint64_with_commas(je->JobBytes, b2),
711          termstat,
712          dt, JobName);
713    }
714    bsendmsg(ua, "\n");
715    unlock_last_jobs_list();
716 }