]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
a65efae62dcefa0a9bd9d9f508b77f5f708d03e5
[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 /*
11    Copyright (C) 2000-2003 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32
33 extern char my_name[];
34 extern time_t daemon_start_time;
35 extern int num_jobs_run;
36
37 static void list_scheduled_jobs(UAContext *ua);
38 static void list_running_jobs(UAContext *ua);
39 static void list_terminated_jobs(UAContext *ua);
40 static void do_storage_status(UAContext *ua, STORE *store);
41 static void do_client_status(UAContext *ua, CLIENT *client);
42 static void do_director_status(UAContext *ua, char *cmd);
43 static void do_all_status(UAContext *ua, char *cmd);
44
45 /*
46  * status command
47  */
48 int status_cmd(UAContext *ua, char *cmd)
49 {
50    STORE *store;
51    CLIENT *client;
52    int item, i;
53
54    if (!open_db(ua)) {
55       return 1;
56    }
57    Dmsg1(20, "status:%s:\n", cmd);
58
59    for (i=1; i<ua->argc; i++) {
60       if (strcasecmp(ua->argk[i], _("all")) == 0) {
61          do_all_status(ua, cmd);
62          return 1;
63       } else if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
64                  strcasecmp(ua->argk[i], _("director")) == 0) {
65          do_director_status(ua, cmd);
66          return 1;
67       } else if (strcasecmp(ua->argk[i], _("client")) == 0) {
68          client = get_client_resource(ua);
69          if (client) {
70             do_client_status(ua, client);
71          }
72          return 1;
73       } else {
74          store = get_storage_resource(ua, 0);
75          if (store) {
76             do_storage_status(ua, store);
77          }
78          return 1;
79       }
80    }
81    /* If no args, ask for status type */
82    if (ua->argc == 1) {                                    
83       start_prompt(ua, _("Status available for:\n"));
84       add_prompt(ua, _("Director"));
85       add_prompt(ua, _("Storage"));
86       add_prompt(ua, _("Client"));
87       add_prompt(ua, _("All"));
88       Dmsg0(20, "do_prompt: select daemon\n");
89       if ((item=do_prompt(ua, "",  _("Select daemon type for status"), cmd, MAX_NAME_LENGTH)) < 0) {
90          return 1;
91       }
92       Dmsg1(20, "item=%d\n", item);
93       switch (item) { 
94       case 0:                         /* Director */
95          do_director_status(ua, cmd);
96          break;
97       case 1:
98          store = select_storage_resource(ua);
99          if (store) {
100             do_storage_status(ua, store);
101          }
102          break;
103       case 2:
104          client = select_client_resource(ua);
105          if (client) {
106             do_client_status(ua, client);
107          }
108          break;
109       case 3:
110          do_all_status(ua, cmd);
111          break;
112       default:
113          break;
114       }
115    }
116    return 1;
117 }
118
119 static void do_all_status(UAContext *ua, char *cmd)
120 {
121    STORE *store, **unique_store;
122    CLIENT *client, **unique_client;
123    int i, j;
124    bool found;
125
126    do_director_status(ua, cmd);
127
128    /* Count Storage items */
129    LockRes();
130    i = 0;
131    foreach_res(store, R_STORAGE) {
132       i++;
133    }
134    unique_store = (STORE **) malloc(i * sizeof(STORE));
135    /* Find Unique Storage address/port */         
136    i = 0;
137    foreach_res(store, R_STORAGE) {
138       found = false;
139       if (!acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
140          continue;
141       }
142       for (j=0; j<i; j++) {
143          if (strcmp(unique_store[j]->address, store->address) == 0 &&
144              unique_store[j]->SDport == store->SDport) {
145             found = true;
146             break;
147          }
148       }
149       if (!found) {
150          unique_store[i++] = store;
151          Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
152       }
153    }
154    UnlockRes();
155
156    /* Call each unique Storage daemon */
157    for (j=0; j<i; j++) {
158       do_storage_status(ua, unique_store[j]);
159    }
160    free(unique_store);
161
162    /* Count Client items */
163    LockRes();
164    i = 0;
165    foreach_res(client, R_CLIENT) {
166       i++;
167    }
168    unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
169    /* Find Unique Client address/port */         
170    i = 0;
171    foreach_res(client, R_CLIENT) {
172       found = false;
173       if (!acl_access_ok(ua, Client_ACL, client->hdr.name)) {
174          continue;
175       }
176       for (j=0; j<i; j++) {
177          if (strcmp(unique_client[j]->address, client->address) == 0 &&
178              unique_client[j]->FDport == client->FDport) {
179             found = true;
180             break;
181          }
182       }
183       if (!found) {
184          unique_client[i++] = client;
185          Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
186       }
187    }
188    UnlockRes();
189
190    /* Call each unique File daemon */
191    for (j=0; j<i; j++) {
192       do_client_status(ua, unique_client[j]);
193    }
194    free(unique_client);
195    
196 }
197
198 static void do_director_status(UAContext *ua, char *cmd)
199 {
200    char dt[MAX_TIME_LENGTH];
201
202    bsendmsg(ua, "%s Version: " VERSION " (" BDATE ") %s %s %s\n", my_name,
203             HOST_OS, DISTNAME, DISTVER);
204    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
205    bsendmsg(ua, _("Daemon started %s, %d Job%s run since started.\n"), 
206         dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
207    /*
208     * List scheduled Jobs
209     */
210    list_scheduled_jobs(ua);
211
212    /* 
213     * List running jobs
214     */
215    list_running_jobs(ua);
216
217    /* 
218     * List terminated jobs
219     */
220    list_terminated_jobs(ua);
221    bsendmsg(ua, "====\n");
222 }
223
224 static void do_storage_status(UAContext *ua, STORE *store)
225 {
226    BSOCK *sd;
227
228    ua->jcr->store = store;
229    /* Try connecting for up to 15 seconds */
230    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"), 
231       store->hdr.name, store->address, store->SDport);
232    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
233       bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
234          store->hdr.name);
235       if (ua->jcr->store_bsock) {
236          bnet_close(ua->jcr->store_bsock);
237          ua->jcr->store_bsock = NULL;
238       }         
239       return;
240    }
241    Dmsg0(20, _("Connected to storage daemon\n"));
242    sd = ua->jcr->store_bsock;
243    bnet_fsend(sd, "status");
244    while (bnet_recv(sd) >= 0) {
245       bsendmsg(ua, "%s", sd->msg);
246    }
247    bnet_sig(sd, BNET_TERMINATE);
248    bnet_close(sd);
249    ua->jcr->store_bsock = NULL;
250    return;  
251 }
252    
253 static void do_client_status(UAContext *ua, CLIENT *client)
254 {
255    BSOCK *fd;
256
257    /* Connect to File daemon */
258
259    ua->jcr->client = client;
260    /* Release any old dummy key */
261    if (ua->jcr->sd_auth_key) {
262       free(ua->jcr->sd_auth_key);
263    }
264    /* Create a new dummy SD auth key */
265    ua->jcr->sd_auth_key = bstrdup("dummy");
266
267    /* Try to connect for 15 seconds */
268    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"), 
269       client->hdr.name, client->address, client->FDport);
270    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
271       bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
272          client->hdr.name);
273       if (ua->jcr->file_bsock) {
274          bnet_close(ua->jcr->file_bsock);
275          ua->jcr->file_bsock = NULL;
276       }         
277       return;
278    }
279    Dmsg0(20, _("Connected to file daemon\n"));
280    fd = ua->jcr->file_bsock;
281    bnet_fsend(fd, "status");
282    while (bnet_recv(fd) >= 0) {
283       bsendmsg(ua, "%s", fd->msg);
284    }
285    bnet_sig(fd, BNET_TERMINATE);
286    bnet_close(fd);
287    ua->jcr->file_bsock = NULL;
288
289    return;  
290 }
291
292 static void prt_runhdr(UAContext *ua)
293 {
294    bsendmsg(ua, _("\nScheduled Jobs:\n"));
295    bsendmsg(ua, _("Level          Type     Scheduled          Name               Volume\n"));
296    bsendmsg(ua, _("===============================================================================\n"));
297 }
298
299 static void prt_runtime(UAContext *ua, JOB *job, int level, time_t runtime, POOL *pool)
300 {
301    char dt[MAX_TIME_LENGTH];       
302    char *level_ptr;
303    bool ok = false;
304    bool close_db = false;
305    JCR *jcr = ua->jcr;
306    MEDIA_DBR mr;
307    memset(&mr, 0, sizeof(mr));
308    if (job->JobType == JT_BACKUP) {
309       jcr->db = NULL;
310       ok = complete_jcr_for_job(jcr, job, pool);
311       if (jcr->db) {
312          close_db = true;             /* new db opened, remember to close it */
313       }
314       if (ok) {
315          ok = find_next_volume_for_append(jcr, &mr, 0);
316       }
317       if (!ok) {
318          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
319       }
320    }
321    bstrftime_nc(dt, sizeof(dt), runtime);
322    switch (job->JobType) {
323    case JT_ADMIN:
324    case JT_RESTORE:
325       level_ptr = " ";
326       break;
327    default:
328       level_ptr = level_to_str(level);
329       break;
330    }
331    bsendmsg(ua, _("%-14s %-8s %-18s %-18s %s\n"), 
332       level_ptr, job_type_to_str(job->JobType), dt, job->hdr.name, mr.VolumeName);
333    if (close_db) {
334       db_close_database(jcr, jcr->db);
335    }
336    jcr->db = ua->db;                  /* restore ua db to jcr */
337
338 }
339
340 /*          
341  * Find all jobs to be run in roughly the
342  *  next 24 hours.
343  */
344 static void list_scheduled_jobs(UAContext *ua)
345 {
346    time_t runtime;
347    RUN *run;
348    JOB *job;
349    int level, num_jobs = 0;
350    bool hdr_printed = false;
351
352    Dmsg0(200, "enter list_sched_jobs()\n");
353
354    /* Loop through all jobs */
355    LockRes();
356    foreach_res(job, R_JOB) {
357       if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
358          continue;
359       }
360       for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
361          level = job->level;   
362          if (run->level) {
363             level = run->level;
364          }
365          if (!hdr_printed) {
366             prt_runhdr(ua);
367             hdr_printed = true;
368          }
369          prt_runtime(ua, job, level, runtime, run->pool);
370          num_jobs++;
371       }
372
373    } /* end for loop over resources */
374    UnlockRes();
375    if (num_jobs == 0) {
376       bsendmsg(ua, _("No Scheduled Jobs.\n"));
377    } else {
378       bsendmsg(ua, "\n");
379    }
380    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
381 }
382
383 static void list_running_jobs(UAContext *ua)
384 {
385    JCR *jcr;
386    int njobs = 0;
387    char *msg;
388    char dt[MAX_TIME_LENGTH];
389    char level[10];
390    bool pool_mem = false;
391
392    Dmsg0(200, "enter list_run_jobs()\n");
393    bsendmsg(ua, _("Running Jobs:\n"));
394    lock_jcr_chain();
395    foreach_jcr(jcr) {
396       njobs++;
397       if (jcr->JobId == 0) {      /* this is us */
398          /* this is a console or other control job. We only show console
399           * jobs in the status output.
400           */
401          if (jcr->JobType == JT_CONSOLE) {
402             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
403             bsendmsg(ua, _("Console connected at %s\n"), dt);
404          }
405          njobs--;
406       }
407       free_locked_jcr(jcr);
408    }
409    if (njobs == 0) {
410       unlock_jcr_chain();
411       /* Note the following message is used in regress -- don't change */
412       bsendmsg(ua, _("No Jobs running.\n====\n"));
413       Dmsg0(200, "leave list_run_jobs()\n");
414       return;
415    }
416    njobs = 0;
417    bsendmsg(ua, _(" JobId Level   Name                       Status\n"));
418    bsendmsg(ua, _("======================================================================\n"));
419    foreach_jcr(jcr) {
420       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
421          free_locked_jcr(jcr);
422          continue;
423       }
424       njobs++;
425       switch (jcr->JobStatus) {
426       case JS_Created:
427          msg = _("is waiting execution");
428          break;
429       case JS_Running:
430          msg = _("is running");
431          break;
432       case JS_Blocked:
433          msg = _("is blocked");
434          break;
435       case JS_Terminated:
436          msg = _("has terminated");
437          break;
438       case JS_ErrorTerminated:
439          msg = _("has erred");
440          break;
441       case JS_Error:
442          msg = _("has errors");
443          break;
444       case JS_FatalError:
445          msg = _("has a fatal error");
446          break;
447       case JS_Differences:
448          msg = _("has verify differences");
449          break;
450       case JS_Canceled:
451          msg = _("has been canceled");
452          break;
453       case JS_WaitFD:
454          msg = (char *) get_pool_memory(PM_FNAME);
455          Mmsg(&msg, _("is waiting on Client %s"), jcr->client->hdr.name);
456          pool_mem = true;
457          break;
458       case JS_WaitSD:
459          msg = (char *) get_pool_memory(PM_FNAME);
460          Mmsg(&msg, _("is waiting on Storage %s"), jcr->store->hdr.name);
461          pool_mem = true;
462          break;
463       case JS_WaitStoreRes:
464          msg = _("is waiting on max Storage jobs");
465          break;
466       case JS_WaitClientRes:
467          msg = _("is waiting on max Client jobs");
468          break;
469       case JS_WaitJobRes:
470          msg = _("is waiting on max Job jobs");
471          break;
472       case JS_WaitMaxJobs:
473          msg = _("is waiting on max total jobs");
474          break;
475       case JS_WaitStartTime:
476          msg = _("is waiting for its start time");
477          break;
478       case JS_WaitPriority:
479          msg = _("is waiting for higher priority jobs to finish");
480          break;
481
482       default:
483          msg = (char *) get_pool_memory(PM_FNAME);
484          Mmsg(&msg, _("is in unknown state %c"), jcr->JobStatus);
485          pool_mem = true;
486          break;
487       }
488       /* 
489        * Now report Storage daemon status code 
490        */
491       switch (jcr->SDJobStatus) {
492       case JS_WaitMount:
493          if (pool_mem) {
494             free_pool_memory(msg);
495             pool_mem = false;
496          }
497          msg = _("is waiting for a mount request");
498          break;
499       case JS_WaitMedia:
500          if (pool_mem) {
501             free_pool_memory(msg);
502             pool_mem = false;
503          }
504          msg = _("is waiting for an appendable Volume");
505          break;
506       case JS_WaitFD:
507          if (!pool_mem) {
508             msg = (char *) get_pool_memory(PM_FNAME);
509             pool_mem = true;
510          }
511          Mmsg(&msg, _("is waiting for Client %s to connect to Storage %s"),
512               jcr->client->hdr.name, jcr->store->hdr.name);
513          break;
514       }
515       switch (jcr->JobType) {
516       case JT_ADMIN:
517       case JT_RESTORE:
518          bstrncpy(level, "      ", sizeof(level));
519          break;
520       default:
521          bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
522          level[7] = 0;
523          break;
524       }
525
526       bsendmsg(ua, _("%6d %-6s  %-20s %s\n"), 
527          jcr->JobId,
528          level, 
529          jcr->Job,
530          msg);
531
532       if (pool_mem) {
533          free_pool_memory(msg);
534          pool_mem = false;
535       }
536       free_locked_jcr(jcr);
537    }
538    unlock_jcr_chain();
539    bsendmsg(ua, "====\n");
540    Dmsg0(200, "leave list_run_jobs()\n");
541 }
542
543 static void list_terminated_jobs(UAContext *ua)
544 {
545    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
546    char level[10];
547
548    if (last_jobs->empty()) {
549       bsendmsg(ua, _("No Terminated Jobs.\n"));
550       return;
551    }
552    lock_last_jobs_list();
553    struct s_last_job *je;
554    bsendmsg(ua, _("\nTerminated Jobs:\n"));
555    bsendmsg(ua, _(" JobId  Level     Files      Bytes     Status   Finished        Name \n"));
556    bsendmsg(ua, _("========================================================================\n"));
557    foreach_dlist(je, last_jobs) {
558       char JobName[MAX_NAME_LENGTH];
559       char *termstat;
560
561       bstrftime_nc(dt, sizeof(dt), je->end_time);
562       switch (je->JobType) {
563       case JT_ADMIN:
564       case JT_RESTORE:
565          bstrncpy(level, "    ", sizeof(level));
566          break;
567       default:
568          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
569          level[4] = 0;
570          break;
571       }
572       switch (je->JobStatus) {
573       case JS_Created:
574          termstat = "Created";
575          break;
576       case JS_FatalError:
577       case JS_ErrorTerminated:
578          termstat = "Error";
579          break;
580       case JS_Differences:
581          termstat = "Diffs";
582          break;
583       case JS_Canceled:
584          termstat = "Cancel";
585          break;
586       case JS_Terminated:
587          termstat = "OK";
588          break;
589       default:
590          termstat = "Other";
591          break;
592       }
593       bstrncpy(JobName, je->Job, sizeof(JobName));
594       /* There are three periods after the Job name */
595       char *p;
596       for (int i=0; i<3; i++) {
597          if ((p=strrchr(JobName, '.')) != NULL) {
598             *p = 0;
599          }
600       }
601       bsendmsg(ua, _("%6d  %-6s %8s %14s %-7s  %-8s %s\n"), 
602          je->JobId,
603          level, 
604          edit_uint64_with_commas(je->JobFiles, b1),
605          edit_uint64_with_commas(je->JobBytes, b2), 
606          termstat,
607          dt, JobName);
608    }
609    bsendmsg(ua, "\n");
610    unlock_last_jobs_list();
611 }