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