2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Status Command
32 * Kern Sibbald, August MMI
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";
56 bool dot_status_cmd(UAContext *ua, const char *cmd)
62 Dmsg1(20, "status:%s:\n", cmd);
64 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
65 ua->send_msg("1900 Bad .status command, missing arguments.\n");
69 if (strcasecmp(ua->argk[2], "current") == 0) {
70 ua->send_msg(OKqstatus, ua->argk[2]);
72 if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
73 ua->send_msg(DotStatusJob, edit_int64(njcr->JobId, ed1),
74 njcr->JobStatus, njcr->JobErrors);
78 } else if (strcasecmp(ua->argk[2], "last") == 0) {
79 ua->send_msg(OKqstatus, ua->argk[2]);
80 if ((last_jobs) && (last_jobs->size() > 0)) {
81 job = (s_last_job*)last_jobs->last();
82 if (acl_access_ok(ua, Job_ACL, job->Job)) {
83 ua->send_msg(DotStatusJob, edit_int64(job->JobId, ed1),
84 job->JobStatus, job->Errors);
88 ua->send_msg("1900 Bad .status command, wrong argument.\n");
95 /* This is the *old* command handler, so we must return
96 * 1 or it closes the connection
98 int qstatus_cmd(UAContext *ua, const char *cmd)
100 dot_status_cmd(ua, cmd);
107 int status_cmd(UAContext *ua, const char *cmd)
113 Dmsg1(20, "status:%s:\n", cmd);
115 for (i=1; i<ua->argc; i++) {
116 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
119 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
120 strcasecmp(ua->argk[i], NT_("director")) == 0) {
121 do_director_status(ua);
123 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
124 client = get_client_resource(ua);
126 do_client_status(ua, client);
130 store = get_storage_resource(ua, false/*no default*/);
132 do_storage_status(ua, store);
137 /* If no args, ask for status type */
139 char prmt[MAX_NAME_LENGTH];
141 start_prompt(ua, _("Status available for:\n"));
142 add_prompt(ua, NT_("Director"));
143 add_prompt(ua, NT_("Storage"));
144 add_prompt(ua, NT_("Client"));
145 add_prompt(ua, NT_("All"));
146 Dmsg0(20, "do_prompt: select daemon\n");
147 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
150 Dmsg1(20, "item=%d\n", item);
152 case 0: /* Director */
153 do_director_status(ua);
156 store = select_storage_resource(ua);
158 do_storage_status(ua, store);
162 client = select_client_resource(ua);
164 do_client_status(ua, client);
177 static void do_all_status(UAContext *ua)
179 STORE *store, **unique_store;
180 CLIENT *client, **unique_client;
184 do_director_status(ua);
186 /* Count Storage items */
189 foreach_res(store, R_STORAGE) {
192 unique_store = (STORE **) malloc(i * sizeof(STORE));
193 /* Find Unique Storage address/port */
195 foreach_res(store, R_STORAGE) {
197 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
200 for (j=0; j<i; j++) {
201 if (strcmp(unique_store[j]->address, store->address) == 0 &&
202 unique_store[j]->SDport == store->SDport) {
208 unique_store[i++] = store;
209 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
214 /* Call each unique Storage daemon */
215 for (j=0; j<i; j++) {
216 do_storage_status(ua, unique_store[j]);
220 /* Count Client items */
223 foreach_res(client, R_CLIENT) {
226 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
227 /* Find Unique Client address/port */
229 foreach_res(client, R_CLIENT) {
231 if (!acl_access_ok(ua, Client_ACL, client->name())) {
234 for (j=0; j<i; j++) {
235 if (strcmp(unique_client[j]->address, client->address) == 0 &&
236 unique_client[j]->FDport == client->FDport) {
242 unique_client[i++] = client;
243 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
248 /* Call each unique File daemon */
249 for (j=0; j<i; j++) {
250 do_client_status(ua, unique_client[j]);
256 void list_dir_status_header(UAContext *ua)
258 char dt[MAX_TIME_LENGTH];
259 char b1[35], b2[35], b3[35], b4[35];
261 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
262 HOST_OS, DISTNAME, DISTVER);
263 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
264 if (num_jobs_run == 1) {
265 ua->send_msg(_("Daemon started %s, 1 Job run since started.\n"), dt);
268 ua->send_msg(_("Daemon started %s, %d Jobs run since started.\n"),
271 ua->send_msg(_(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
272 edit_uint64_with_commas(sm_bytes, b1),
273 edit_uint64_with_commas(sm_max_bytes, b2),
274 edit_uint64_with_commas(sm_buffers, b3),
275 edit_uint64_with_commas(sm_max_buffers, b4));
278 static void do_director_status(UAContext *ua)
280 list_dir_status_header(ua);
283 * List scheduled Jobs
285 list_scheduled_jobs(ua);
290 list_running_jobs(ua);
293 * List terminated jobs
295 list_terminated_jobs(ua);
296 ua->send_msg(_("====\n"));
299 static void do_storage_status(UAContext *ua, STORE *store)
304 lstore.store = store;
305 pm_strcpy(lstore.store_source, _("unknown source"));
306 set_wstorage(ua->jcr, &lstore);
307 /* Try connecting for up to 15 seconds */
308 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
309 store->name(), store->address, store->SDport);
310 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
311 ua->send_msg(_("\nFailed to connect to Storage daemon %s.\n====\n"),
313 if (ua->jcr->store_bsock) {
314 bnet_close(ua->jcr->store_bsock);
315 ua->jcr->store_bsock = NULL;
319 Dmsg0(20, _("Connected to storage daemon\n"));
320 sd = ua->jcr->store_bsock;
321 bnet_fsend(sd, "status");
322 while (bnet_recv(sd) >= 0) {
323 ua->send_msg("%s", sd->msg);
325 bnet_sig(sd, BNET_TERMINATE);
327 ua->jcr->store_bsock = NULL;
331 static void do_client_status(UAContext *ua, CLIENT *client)
335 /* Connect to File daemon */
337 ua->jcr->client = client;
338 /* Release any old dummy key */
339 if (ua->jcr->sd_auth_key) {
340 free(ua->jcr->sd_auth_key);
342 /* Create a new dummy SD auth key */
343 ua->jcr->sd_auth_key = bstrdup("dummy");
345 /* Try to connect for 15 seconds */
346 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
347 client->name(), client->address, client->FDport);
348 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
349 ua->send_msg(_("Failed to connect to Client %s.\n====\n"),
351 if (ua->jcr->file_bsock) {
352 bnet_close(ua->jcr->file_bsock);
353 ua->jcr->file_bsock = NULL;
357 Dmsg0(20, _("Connected to file daemon\n"));
358 fd = ua->jcr->file_bsock;
359 bnet_fsend(fd, "status");
360 while (bnet_recv(fd) >= 0) {
361 ua->send_msg("%s", fd->msg);
363 bnet_sig(fd, BNET_TERMINATE);
365 ua->jcr->file_bsock = NULL;
370 static void prt_runhdr(UAContext *ua)
372 ua->send_msg(_("\nScheduled Jobs:\n"));
373 ua->send_msg(_("Level Type Pri Scheduled Name Volume\n"));
374 ua->send_msg(_("===================================================================================\n"));
377 /* Scheduling packet */
379 dlink link; /* keep this as first item!!! */
388 static void prt_runtime(UAContext *ua, sched_pkt *sp)
390 char dt[MAX_TIME_LENGTH];
391 const char *level_ptr;
393 bool close_db = false;
397 memset(&mr, 0, sizeof(mr));
398 if (sp->job->JobType == JT_BACKUP) {
400 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
401 Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
403 close_db = true; /* new db opened, remember to close it */
406 mr.PoolId = jcr->jr.PoolId;
407 mr.StorageId = sp->store->StorageId;
408 jcr->wstore = sp->store;
409 Dmsg0(250, "call find_next_volume_for_append\n");
410 ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune);
413 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
416 bstrftime_nc(dt, sizeof(dt), sp->runtime);
417 switch (sp->job->JobType) {
423 level_ptr = level_to_str(sp->level);
426 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
427 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
428 sp->job->name(), mr.VolumeName);
430 db_close_database(jcr, jcr->db);
432 jcr->db = ua->db; /* restore ua db to jcr */
436 * Sort items by runtime, priority
438 static int my_compare(void *item1, void *item2)
440 sched_pkt *p1 = (sched_pkt *)item1;
441 sched_pkt *p2 = (sched_pkt *)item2;
442 if (p1->runtime < p2->runtime) {
444 } else if (p1->runtime > p2->runtime) {
447 if (p1->priority < p2->priority) {
449 } else if (p1->priority > p2->priority) {
456 * Find all jobs to be run in roughly the
459 static void list_scheduled_jobs(UAContext *ua)
464 int level, num_jobs = 0;
466 bool hdr_printed = false;
471 Dmsg0(200, "enter list_sched_jobs()\n");
474 i = find_arg_with_value(ua, NT_("days"));
476 days = atoi(ua->argv[i]);
477 if ((days < 0) || (days > 50)) {
478 ua->send_msg(_("Ignoring invalid value for days. Max is 50.\n"));
483 /* Loop through all jobs */
485 foreach_res(job, R_JOB) {
486 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
489 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
491 level = job->JobLevel;
495 priority = job->Priority;
497 priority = run->Priority;
503 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
506 sp->priority = priority;
507 sp->runtime = runtime;
508 sp->pool = run->pool;
509 get_job_storage(&store, job, run);
510 sp->store = store.store;
511 Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
512 sched.binary_insert_multiple(sp, my_compare);
515 } /* end for loop over resources */
517 foreach_dlist(sp, &sched) {
521 ua->send_msg(_("No Scheduled Jobs.\n"));
523 ua->send_msg(_("====\n"));
524 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
527 static void list_running_jobs(UAContext *ua)
532 char *emsg; /* edited message */
533 char dt[MAX_TIME_LENGTH];
535 bool pool_mem = false;
537 Dmsg0(200, "enter list_run_jobs()\n");
538 ua->send_msg(_("\nRunning Jobs:\n"));
540 if (jcr->JobId == 0) { /* this is us */
541 /* this is a console or other control job. We only show console
542 * jobs in the status output.
544 if (jcr->JobType == JT_CONSOLE) {
545 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
546 ua->send_msg(_("Console connected at %s\n"), dt);
555 /* Note the following message is used in regress -- don't change */
556 ua->send_msg(_("No Jobs running.\n====\n"));
557 Dmsg0(200, "leave list_run_jobs()\n");
561 ua->send_msg(_(" JobId Level Name Status\n"));
562 ua->send_msg(_("======================================================================\n"));
564 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
568 switch (jcr->JobStatus) {
570 msg = _("is waiting execution");
573 msg = _("is running");
576 msg = _("is blocked");
579 msg = _("has terminated");
581 case JS_ErrorTerminated:
582 msg = _("has erred");
585 msg = _("has errors");
588 msg = _("has a fatal error");
591 msg = _("has verify differences");
594 msg = _("has been canceled");
597 emsg = (char *) get_pool_memory(PM_FNAME);
598 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
603 emsg = (char *) get_pool_memory(PM_FNAME);
605 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
607 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
612 case JS_WaitStoreRes:
613 msg = _("is waiting on max Storage jobs");
615 case JS_WaitClientRes:
616 msg = _("is waiting on max Client jobs");
619 msg = _("is waiting on max Job jobs");
622 msg = _("is waiting on max total jobs");
624 case JS_WaitStartTime:
625 msg = _("is waiting for its start time");
627 case JS_WaitPriority:
628 msg = _("is waiting for higher priority jobs to finish");
632 emsg = (char *) get_pool_memory(PM_FNAME);
633 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
639 * Now report Storage daemon status code
641 switch (jcr->SDJobStatus) {
644 free_pool_memory(emsg);
647 msg = _("is waiting for a mount request");
651 free_pool_memory(emsg);
654 msg = _("is waiting for an appendable Volume");
658 emsg = (char *)get_pool_memory(PM_FNAME);
661 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
662 jcr->client->name(), jcr->wstore->name());
666 switch (jcr->JobType) {
669 bstrncpy(level, " ", sizeof(level));
672 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
677 ua->send_msg(_("%6d %-6s %-20s %s\n"),
684 free_pool_memory(emsg);
689 ua->send_msg(_("====\n"));
690 Dmsg0(200, "leave list_run_jobs()\n");
693 static void list_terminated_jobs(UAContext *ua)
695 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
698 if (last_jobs->empty()) {
699 ua->send_msg(_("No Terminated Jobs.\n"));
702 lock_last_jobs_list();
703 struct s_last_job *je;
704 ua->send_msg(_("\nTerminated Jobs:\n"));
705 ua->send_msg(_(" JobId Level Files Bytes Status Finished Name \n"));
706 ua->send_msg(_("====================================================================\n"));
707 foreach_dlist(je, last_jobs) {
708 char JobName[MAX_NAME_LENGTH];
709 const char *termstat;
711 bstrncpy(JobName, je->Job, sizeof(JobName));
712 /* There are three periods after the Job name */
714 for (int i=0; i<3; i++) {
715 if ((p=strrchr(JobName, '.')) != NULL) {
720 if (!acl_access_ok(ua, Job_ACL, JobName)) {
724 bstrftime_nc(dt, sizeof(dt), je->end_time);
725 switch (je->JobType) {
728 bstrncpy(level, " ", sizeof(level));
731 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
735 switch (je->JobStatus) {
737 termstat = _("Created");
740 case JS_ErrorTerminated:
741 termstat = _("Error");
744 termstat = _("Diffs");
747 termstat = _("Cancel");
753 termstat = _("Other");
756 ua->send_msg(_("%6d %-6s %8s %10s %-7s %-8s %s\n"),
759 edit_uint64_with_commas(je->JobFiles, b1),
760 edit_uint64_with_suffix(je->JobBytes, b2),
764 ua->send_msg(_("\n"));
765 unlock_last_jobs_list();