3 * Bacula Director -- User Agent Status Command
5 * Kern Sibbald, August MMI
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2001-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
41 static void list_scheduled_jobs(UAContext *ua);
42 static void list_running_jobs(UAContext *ua);
43 static void list_terminated_jobs(UAContext *ua);
44 static void do_storage_status(UAContext *ua, STORE *store);
45 static void do_client_status(UAContext *ua, CLIENT *client);
46 static void do_director_status(UAContext *ua);
47 static void do_all_status(UAContext *ua);
49 static char OKqstatus[] = "1000 OK .status\n";
50 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
55 int qstatus_cmd(UAContext *ua, const char *cmd)
64 Dmsg1(20, "status:%s:\n", cmd);
66 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
67 bsendmsg(ua, "1900 Bad .status command, missing arguments.\n");
71 if (strcasecmp(ua->argk[2], "current") == 0) {
72 bsendmsg(ua, OKqstatus, ua->argk[2]);
74 if (njcr->JobId != 0) {
75 bsendmsg(ua, DotStatusJob, edit_int64(njcr->JobId, ed1),
76 njcr->JobStatus, njcr->JobErrors);
80 } else if (strcasecmp(ua->argk[2], "last") == 0) {
81 bsendmsg(ua, OKqstatus, ua->argk[2]);
82 if ((last_jobs) && (last_jobs->size() > 0)) {
83 job = (s_last_job*)last_jobs->last();
84 bsendmsg(ua, DotStatusJob, edit_int64(job->JobId, ed1),
85 job->JobStatus, job->Errors);
88 bsendmsg(ua, "1900 Bad .status command, wrong argument.\n");
98 int status_cmd(UAContext *ua, const char *cmd)
107 Dmsg1(20, "status:%s:\n", cmd);
109 for (i=1; i<ua->argc; i++) {
110 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
113 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
114 strcasecmp(ua->argk[i], NT_("director")) == 0) {
115 do_director_status(ua);
117 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
118 client = get_client_resource(ua);
120 do_client_status(ua, client);
124 store = get_storage_resource(ua, false/*no default*/);
126 do_storage_status(ua, store);
131 /* If no args, ask for status type */
133 char prmt[MAX_NAME_LENGTH];
135 start_prompt(ua, _("Status available for:\n"));
136 add_prompt(ua, NT_("Director"));
137 add_prompt(ua, NT_("Storage"));
138 add_prompt(ua, NT_("Client"));
139 add_prompt(ua, NT_("All"));
140 Dmsg0(20, "do_prompt: select daemon\n");
141 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
144 Dmsg1(20, "item=%d\n", item);
146 case 0: /* Director */
147 do_director_status(ua);
150 store = select_storage_resource(ua);
152 do_storage_status(ua, store);
156 client = select_client_resource(ua);
158 do_client_status(ua, client);
171 static void do_all_status(UAContext *ua)
173 STORE *store, **unique_store;
174 CLIENT *client, **unique_client;
178 do_director_status(ua);
180 /* Count Storage items */
183 foreach_res(store, R_STORAGE) {
186 unique_store = (STORE **) malloc(i * sizeof(STORE));
187 /* Find Unique Storage address/port */
189 foreach_res(store, R_STORAGE) {
191 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
194 for (j=0; j<i; j++) {
195 if (strcmp(unique_store[j]->address, store->address) == 0 &&
196 unique_store[j]->SDport == store->SDport) {
202 unique_store[i++] = store;
203 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
208 /* Call each unique Storage daemon */
209 for (j=0; j<i; j++) {
210 do_storage_status(ua, unique_store[j]);
214 /* Count Client items */
217 foreach_res(client, R_CLIENT) {
220 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
221 /* Find Unique Client address/port */
223 foreach_res(client, R_CLIENT) {
225 if (!acl_access_ok(ua, Client_ACL, client->name())) {
228 for (j=0; j<i; j++) {
229 if (strcmp(unique_client[j]->address, client->address) == 0 &&
230 unique_client[j]->FDport == client->FDport) {
236 unique_client[i++] = client;
237 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
242 /* Call each unique File daemon */
243 for (j=0; j<i; j++) {
244 do_client_status(ua, unique_client[j]);
250 static void do_director_status(UAContext *ua)
252 char dt[MAX_TIME_LENGTH];
253 char b1[35], b2[35], b3[35], b4[35];
255 bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
256 HOST_OS, DISTNAME, DISTVER);
257 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
258 if (num_jobs_run == 1) {
259 bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
262 bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
265 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
266 edit_uint64_with_commas(sm_bytes, b1),
267 edit_uint64_with_commas(sm_max_bytes, b2),
268 edit_uint64_with_commas(sm_buffers, b3),
269 edit_uint64_with_commas(sm_max_buffers, b4));
272 * List scheduled Jobs
274 list_scheduled_jobs(ua);
279 list_running_jobs(ua);
282 * List terminated jobs
284 list_terminated_jobs(ua);
285 bsendmsg(ua, _("====\n"));
288 static void do_storage_status(UAContext *ua, STORE *store)
292 set_wstorage(ua->jcr, store);
293 /* Try connecting for up to 15 seconds */
294 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
295 store->name(), store->address, store->SDport);
296 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
297 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
299 if (ua->jcr->store_bsock) {
300 bnet_close(ua->jcr->store_bsock);
301 ua->jcr->store_bsock = NULL;
305 Dmsg0(20, _("Connected to storage daemon\n"));
306 sd = ua->jcr->store_bsock;
307 bnet_fsend(sd, "status");
308 while (bnet_recv(sd) >= 0) {
309 bsendmsg(ua, "%s", sd->msg);
311 bnet_sig(sd, BNET_TERMINATE);
313 ua->jcr->store_bsock = NULL;
317 static void do_client_status(UAContext *ua, CLIENT *client)
321 /* Connect to File daemon */
323 ua->jcr->client = client;
324 /* Release any old dummy key */
325 if (ua->jcr->sd_auth_key) {
326 free(ua->jcr->sd_auth_key);
328 /* Create a new dummy SD auth key */
329 ua->jcr->sd_auth_key = bstrdup("dummy");
331 /* Try to connect for 15 seconds */
332 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
333 client->name(), client->address, client->FDport);
334 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
335 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
337 if (ua->jcr->file_bsock) {
338 bnet_close(ua->jcr->file_bsock);
339 ua->jcr->file_bsock = NULL;
343 Dmsg0(20, _("Connected to file daemon\n"));
344 fd = ua->jcr->file_bsock;
345 bnet_fsend(fd, "status");
346 while (bnet_recv(fd) >= 0) {
347 bsendmsg(ua, "%s", fd->msg);
349 bnet_sig(fd, BNET_TERMINATE);
351 ua->jcr->file_bsock = NULL;
356 static void prt_runhdr(UAContext *ua)
358 bsendmsg(ua, _("\nScheduled Jobs:\n"));
359 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
360 bsendmsg(ua, _("===================================================================================\n"));
363 /* Scheduling packet */
365 dlink link; /* keep this as first item!!! */
374 static void prt_runtime(UAContext *ua, sched_pkt *sp)
376 char dt[MAX_TIME_LENGTH];
377 const char *level_ptr;
379 bool close_db = false;
383 memset(&mr, 0, sizeof(mr));
384 if (sp->job->JobType == JT_BACKUP) {
386 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
388 close_db = true; /* new db opened, remember to close it */
391 mr.PoolId = jcr->jr.PoolId;
392 mr.StorageId = sp->store->StorageId;
393 jcr->wstore = sp->store;
394 ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
397 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
400 bstrftime_nc(dt, sizeof(dt), sp->runtime);
401 switch (sp->job->JobType) {
407 level_ptr = level_to_str(sp->level);
410 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
411 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
412 sp->job->name(), mr.VolumeName);
414 db_close_database(jcr, jcr->db);
416 jcr->db = ua->db; /* restore ua db to jcr */
420 * Sort items by runtime, priority
422 static int my_compare(void *item1, void *item2)
424 sched_pkt *p1 = (sched_pkt *)item1;
425 sched_pkt *p2 = (sched_pkt *)item2;
426 if (p1->runtime < p2->runtime) {
428 } else if (p1->runtime > p2->runtime) {
431 if (p1->priority < p2->priority) {
433 } else if (p1->priority > p2->priority) {
440 * Find all jobs to be run in roughly the
443 static void list_scheduled_jobs(UAContext *ua)
449 int level, num_jobs = 0;
451 bool hdr_printed = false;
456 Dmsg0(200, "enter list_sched_jobs()\n");
459 i = find_arg_with_value(ua, NT_("days"));
461 days = atoi(ua->argv[i]);
462 if ((days < 0) || (days > 50)) {
463 bsendmsg(ua, _("Ignoring illegal value for days.\n"));
468 /* Loop through all jobs */
470 foreach_res(job, R_JOB) {
471 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
474 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
475 level = job->JobLevel;
479 priority = job->Priority;
481 priority = run->Priority;
484 store = run->storage;
486 store = (STORE *)job->storage->first();
492 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
495 sp->priority = priority;
496 sp->runtime = runtime;
497 sp->pool = run->pool;
499 sched.binary_insert_multiple(sp, my_compare);
502 } /* end for loop over resources */
504 foreach_dlist(sp, &sched) {
508 bsendmsg(ua, _("No Scheduled Jobs.\n"));
510 bsendmsg(ua, _("====\n"));
511 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
514 static void list_running_jobs(UAContext *ua)
519 char *emsg; /* edited message */
520 char dt[MAX_TIME_LENGTH];
522 bool pool_mem = false;
524 Dmsg0(200, "enter list_run_jobs()\n");
525 bsendmsg(ua, _("\nRunning Jobs:\n"));
527 if (jcr->JobId == 0) { /* this is us */
528 /* this is a console or other control job. We only show console
529 * jobs in the status output.
531 if (jcr->JobType == JT_CONSOLE) {
532 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
533 bsendmsg(ua, _("Console connected at %s\n"), dt);
542 /* Note the following message is used in regress -- don't change */
543 bsendmsg(ua, _("No Jobs running.\n====\n"));
544 Dmsg0(200, "leave list_run_jobs()\n");
548 bsendmsg(ua, _(" JobId Level Name Status\n"));
549 bsendmsg(ua, _("======================================================================\n"));
551 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
555 switch (jcr->JobStatus) {
557 msg = _("is waiting execution");
560 msg = _("is running");
563 msg = _("is blocked");
566 msg = _("has terminated");
568 case JS_ErrorTerminated:
569 msg = _("has erred");
572 msg = _("has errors");
575 msg = _("has a fatal error");
578 msg = _("has verify differences");
581 msg = _("has been canceled");
584 emsg = (char *) get_pool_memory(PM_FNAME);
585 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
590 emsg = (char *) get_pool_memory(PM_FNAME);
592 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
594 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
599 case JS_WaitStoreRes:
600 msg = _("is waiting on max Storage jobs");
602 case JS_WaitClientRes:
603 msg = _("is waiting on max Client jobs");
606 msg = _("is waiting on max Job jobs");
609 msg = _("is waiting on max total jobs");
611 case JS_WaitStartTime:
612 msg = _("is waiting for its start time");
614 case JS_WaitPriority:
615 msg = _("is waiting for higher priority jobs to finish");
619 emsg = (char *) get_pool_memory(PM_FNAME);
620 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
626 * Now report Storage daemon status code
628 switch (jcr->SDJobStatus) {
631 free_pool_memory(emsg);
634 msg = _("is waiting for a mount request");
638 free_pool_memory(emsg);
641 msg = _("is waiting for an appendable Volume");
645 emsg = (char *) get_pool_memory(PM_FNAME);
648 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
649 jcr->client->name(), jcr->wstore->name());
653 switch (jcr->JobType) {
656 bstrncpy(level, " ", sizeof(level));
659 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
664 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
671 free_pool_memory(emsg);
676 bsendmsg(ua, _("====\n"));
677 Dmsg0(200, "leave list_run_jobs()\n");
680 static void list_terminated_jobs(UAContext *ua)
682 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
685 if (last_jobs->empty()) {
686 bsendmsg(ua, _("No Terminated Jobs.\n"));
689 lock_last_jobs_list();
690 struct s_last_job *je;
691 bsendmsg(ua, _("\nTerminated Jobs:\n"));
692 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
693 bsendmsg(ua, _("====================================================================\n"));
694 foreach_dlist(je, last_jobs) {
695 char JobName[MAX_NAME_LENGTH];
696 const char *termstat;
698 bstrncpy(JobName, je->Job, sizeof(JobName));
699 /* There are three periods after the Job name */
701 for (int i=0; i<3; i++) {
702 if ((p=strrchr(JobName, '.')) != NULL) {
707 if (!acl_access_ok(ua, Job_ACL, JobName)) {
711 bstrftime_nc(dt, sizeof(dt), je->end_time);
712 switch (je->JobType) {
715 bstrncpy(level, " ", sizeof(level));
718 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
722 switch (je->JobStatus) {
724 termstat = _("Created");
727 case JS_ErrorTerminated:
728 termstat = _("Error");
731 termstat = _("Diffs");
734 termstat = _("Cancel");
740 termstat = _("Other");
743 bsendmsg(ua, _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
746 edit_uint64_with_commas(je->JobFiles, b1),
747 edit_uint64_with_suffix(je->JobBytes, b2),
751 bsendmsg(ua, _("\n"));
752 unlock_last_jobs_list();