]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
This commit was manufactured by cvs2svn to create tag
[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 struct s_last_job last_job;
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, found;
124
125    do_director_status(ua, cmd);
126
127    /* Count Storage items */
128    LockRes();
129    store = NULL;
130    for (i=0; (store = (STORE *)GetNextRes(R_STORAGE, (RES *)store)); i++)
131       { }
132    unique_store = (STORE **) malloc(i * sizeof(STORE));
133    /* Find Unique Storage address/port */         
134    store = (STORE *)GetNextRes(R_STORAGE, NULL);
135    i = 0;
136    unique_store[i++] = store;
137    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
138       found = 0;
139       for (j=0; j<i; j++) {
140          if (strcmp(unique_store[j]->address, store->address) == 0 &&
141              unique_store[j]->SDport == store->SDport) {
142             found = 1;
143             break;
144          }
145       }
146       if (!found) {
147          unique_store[i++] = store;
148          Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
149       }
150    }
151    UnlockRes();
152
153    /* Call each unique Storage daemon */
154    for (j=0; j<i; j++) {
155       do_storage_status(ua, unique_store[j]);
156    }
157    free(unique_store);
158
159    /* Count Client items */
160    LockRes();
161    client = NULL;
162    for (i=0; (client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client)); i++)
163       { }
164    unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
165    /* Find Unique Client address/port */         
166    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
167    i = 0;
168    unique_client[i++] = client;
169    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
170       found = 0;
171       for (j=0; j<i; j++) {
172          if (strcmp(unique_client[j]->address, client->address) == 0 &&
173              unique_client[j]->FDport == client->FDport) {
174             found = 1;
175             break;
176          }
177       }
178       if (!found) {
179          unique_client[i++] = client;
180          Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
181       }
182    }
183    UnlockRes();
184
185    /* Call each unique File daemon */
186    for (j=0; j<i; j++) {
187       do_client_status(ua, unique_client[j]);
188    }
189    free(unique_client);
190    
191 }
192
193 static void do_director_status(UAContext *ua, char *cmd)
194 {
195    char dt[MAX_TIME_LENGTH];
196
197    bsendmsg(ua, "%s Version: " VERSION " (" BDATE ") %s %s %s\n", my_name,
198             HOST_OS, DISTNAME, DISTVER);
199    bstrftime(dt, sizeof(dt), daemon_start_time);
200    strcpy(dt+7, dt+9);     /* cut century */
201    bsendmsg(ua, _("Daemon started %s, %d Job%s run.\n"), dt, last_job.NumJobs,
202         last_job.NumJobs == 1 ? "" : "s");
203    /*
204     * List scheduled Jobs
205     */
206    list_scheduled_jobs(ua);
207
208    /* 
209     * List running jobs
210     */
211    list_running_jobs(ua);
212
213    /* 
214     * List terminated jobs
215     */
216    list_terminated_jobs(ua);
217    bsendmsg(ua, "====\n");
218 }
219
220 static void do_storage_status(UAContext *ua, STORE *store)
221 {
222    BSOCK *sd;
223
224    ua->jcr->store = store;
225    /* Try connecting for up to 15 seconds */
226    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"), 
227       store->hdr.name, store->address, store->SDport);
228    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
229       bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
230          store->hdr.name);
231       if (ua->jcr->store_bsock) {
232          bnet_close(ua->jcr->store_bsock);
233          ua->jcr->store_bsock = NULL;
234       }         
235       return;
236    }
237    Dmsg0(20, _("Connected to storage daemon\n"));
238    sd = ua->jcr->store_bsock;
239    bnet_fsend(sd, "status");
240    while (bnet_recv(sd) >= 0) {
241       bsendmsg(ua, "%s", sd->msg);
242    }
243    bnet_sig(sd, BNET_TERMINATE);
244    bnet_close(sd);
245    ua->jcr->store_bsock = NULL;
246    return;  
247 }
248    
249 static void do_client_status(UAContext *ua, CLIENT *client)
250 {
251    BSOCK *fd;
252
253    /* Connect to File daemon */
254
255    ua->jcr->client = client;
256    /* Release any old dummy key */
257    if (ua->jcr->sd_auth_key) {
258       free(ua->jcr->sd_auth_key);
259    }
260    /* Create a new dummy SD auth key */
261    ua->jcr->sd_auth_key = bstrdup("dummy");
262
263    /* Try to connect for 15 seconds */
264    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"), 
265       client->hdr.name, client->address, client->FDport);
266    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
267       bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
268          client->hdr.name);
269       if (ua->jcr->file_bsock) {
270          bnet_close(ua->jcr->file_bsock);
271          ua->jcr->file_bsock = NULL;
272       }         
273       return;
274    }
275    Dmsg0(20, _("Connected to file daemon\n"));
276    fd = ua->jcr->file_bsock;
277    bnet_fsend(fd, "status");
278    while (bnet_recv(fd) >= 0) {
279       bsendmsg(ua, "%s", fd->msg);
280    }
281    bnet_sig(fd, BNET_TERMINATE);
282    bnet_close(fd);
283    ua->jcr->file_bsock = NULL;
284
285    return;  
286 }
287
288 static void prt_runhdr(UAContext *ua)
289 {
290    bsendmsg(ua, _("\nScheduled Jobs:\n"));
291    bsendmsg(ua, _("Level          Type     Scheduled          Name               Volume\n"));
292    bsendmsg(ua, _("===============================================================================\n"));
293 }
294
295 static void prt_runtime(UAContext *ua, JOB *job, int level, time_t runtime, POOL *pool)
296 {
297    char dt[MAX_TIME_LENGTH];       
298    char *level_ptr;
299    bool ok = false;
300    bool close_db = false;
301    JCR *jcr = ua->jcr;
302    MEDIA_DBR mr;
303    memset(&mr, 0, sizeof(mr));
304    if (job->JobType == JT_BACKUP) {
305       jcr->db = NULL;
306       ok = complete_jcr_for_job(jcr, job, pool);
307       if (jcr->db) {
308          close_db = true;             /* new db opened, remember to close it */
309       }
310       if (ok) {
311          ok = find_next_volume_for_append(jcr, &mr, 0);
312       }
313       if (!ok) {
314          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
315       }
316    }
317    bstrftime(dt, sizeof(dt), runtime);
318    strcpy(dt+7, dt+9);     /* cut century */
319    switch (job->JobType) {
320    case JT_ADMIN:
321    case JT_RESTORE:
322       level_ptr = " ";
323       break;
324    default:
325       level_ptr = level_to_str(level);
326       break;
327    }
328    bsendmsg(ua, _("%-14s %-8s %-18s %-18s %s\n"), 
329       level_ptr, job_type_to_str(job->JobType), dt, job->hdr.name, mr.VolumeName);
330    if (close_db) {
331       db_close_database(jcr, jcr->db);
332    }
333    jcr->db = ua->db;                  /* restore ua db to jcr */
334
335 }
336
337 /*          
338  * Find all jobs to be run in roughly the
339  *  next 24 hours.
340  */
341 static void list_scheduled_jobs(UAContext *ua)
342 {
343    time_t runtime;
344    RUN *run;
345    JOB *job;
346    int level, num_jobs = 0;
347    bool hdr_printed = false;
348
349    Dmsg0(200, "enter find_runs()\n");
350
351    /* Loop through all jobs */
352    LockRes();
353    for (job=NULL; (job=(JOB *)GetNextRes(R_JOB, (RES *)job)); ) {
354       for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
355          level = job->level;   
356          if (run->level) {
357             level = run->level;
358          }
359          if (!hdr_printed) {
360             prt_runhdr(ua);
361             hdr_printed = true;
362          }
363          prt_runtime(ua, job, level, runtime, run->pool);
364          num_jobs++;
365       }
366
367    } /* end for loop over resources */
368    UnlockRes();
369    if (num_jobs == 0) {
370       bsendmsg(ua, _("No Scheduled Jobs.\n"));
371    } else {
372       bsendmsg(ua, "\n");
373    }
374    Dmsg0(200, "Leave find_runs()\n");
375 }
376
377 static void list_running_jobs(UAContext *ua)
378 {
379    JCR *jcr;
380    int njobs = 0;
381    char *msg;
382    char dt[MAX_TIME_LENGTH];
383    char level[10];
384    bool pool_mem = false;
385
386    lock_jcr_chain();
387    for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) {
388       if (jcr->JobId == 0) {      /* this is us */
389          /* this is a console or other control job. We only show console
390           * jobs in the status output.
391           */
392          if (jcr->JobType == JT_CONSOLE) {
393             bstrftime(dt, sizeof(dt), jcr->start_time);
394             strcpy(dt+7, dt+9);  /* cut century */
395             bsendmsg(ua, _("Console connected at %s\n"), dt);
396          }
397          njobs--;
398       }
399       free_locked_jcr(jcr);
400    }
401    if (njobs == 0) {
402       unlock_jcr_chain();
403       bsendmsg(ua, _("No Running Jobs.\n"));
404       return;
405    }
406    njobs = 0;
407    bsendmsg(ua, _("\nRunning Jobs:\n"));
408    bsendmsg(ua, _("Level JobId  Job                        Status\n"));
409    bsendmsg(ua, _("====================================================================\n"));
410    for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) {
411       if (jcr->JobId == 0) {      /* this is us */
412          njobs--;
413          free_locked_jcr(jcr);
414          continue;
415       }
416       switch (jcr->JobStatus) {
417       case JS_Created:
418          msg = _("is waiting execution");
419          break;
420       case JS_Running:
421          msg = _("is running");
422          break;
423       case JS_Blocked:
424          msg = _("is blocked");
425          break;
426       case JS_Terminated:
427          msg = _("has terminated");
428          break;
429       case JS_ErrorTerminated:
430          msg = _("has erred");
431          break;
432       case JS_Error:
433          msg = _("has errors");
434          break;
435       case JS_FatalError:
436          msg = _("has a fatal error");
437          break;
438       case JS_Differences:
439          msg = _("has verify differences");
440          break;
441       case JS_Canceled:
442          msg = _("has been canceled");
443          break;
444       case JS_WaitFD:
445          msg = (char *) get_pool_memory(PM_FNAME);
446          Mmsg(&msg, _("is waiting on Client %s"), jcr->client->hdr.name);
447          pool_mem = true;
448          break;
449       case JS_WaitSD:
450          msg = (char *) get_pool_memory(PM_FNAME);
451          Mmsg(&msg, _("is waiting on Storage %s"), jcr->store->hdr.name);
452          pool_mem = true;
453          break;
454       case JS_WaitStoreRes:
455          msg = _("is waiting on max Storage jobs");
456          break;
457       case JS_WaitClientRes:
458          msg = _("is waiting on max Client jobs");
459          break;
460       case JS_WaitJobRes:
461          msg = _("is waiting on max Job jobs");
462          break;
463       case JS_WaitMaxJobs:
464          msg = _("is waiting on max total jobs");
465          break;
466       case JS_WaitStartTime:
467          msg = _("is waiting for its start time");
468          break;
469       case JS_WaitPriority:
470          msg = _("is waiting for higher priority jobs to finish");
471          break;
472
473       default:
474          msg = (char *) get_pool_memory(PM_FNAME);
475          Mmsg(&msg, _("is in unknown state %c"), jcr->JobStatus);
476          pool_mem = true;
477          break;
478       }
479       /* 
480        * Now report Storage daemon status code 
481        */
482       switch (jcr->SDJobStatus) {
483       case JS_WaitMount:
484          if (pool_mem) {
485             free_pool_memory(msg);
486             pool_mem = false;
487          }
488          msg = _("is waiting for a mount request");
489          break;
490       case JS_WaitMedia:
491          if (pool_mem) {
492             free_pool_memory(msg);
493             pool_mem = false;
494          }
495          msg = _("is waiting for an appendable Volume");
496          break;
497       case JS_WaitFD:
498          if (!pool_mem) {
499             msg = (char *) get_pool_memory(PM_FNAME);
500             pool_mem = true;
501          }
502          Mmsg(&msg, _("is waiting for Client %s to connect to Storage %s"),
503               jcr->client->hdr.name, jcr->store->hdr.name);
504          break;
505       }
506       switch (jcr->JobType) {
507       case JT_ADMIN:
508       case JT_RESTORE:
509          bstrncpy(level, "    ", sizeof(level));
510          break;
511       default:
512          bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
513          level[4] = 0;
514          break;
515       }
516
517       bsendmsg(ua, _("%-4s %6d  %-20s %s\n"), 
518          level, 
519          jcr->JobId,
520          jcr->Job,
521          msg);
522
523       if (pool_mem) {
524          free_pool_memory(msg);
525          pool_mem = false;
526       }
527       free_locked_jcr(jcr);
528    }
529    unlock_jcr_chain();
530
531    bsendmsg(ua, "\n");
532 }
533
534 static void list_terminated_jobs(UAContext *ua)
535 {
536    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
537    char level[10];
538
539    if (last_jobs->empty()) {
540       bsendmsg(ua, _("No Terminated Jobs.\n"));
541       return;
542    }
543    lock_last_jobs_list();
544    struct s_last_job *je;
545    bsendmsg(ua, _("\nTerminated Jobs:\n"));
546    bsendmsg(ua, _(" JobId  Level   Files          Bytes Status   Finished        Name \n"));
547    bsendmsg(ua, _("======================================================================\n"));
548    for (je=NULL; (je=(s_last_job *)last_jobs->next(je)); ) {
549       char JobName[MAX_NAME_LENGTH];
550       char *termstat;
551
552       bstrftime(dt, sizeof(dt), je->end_time);
553       strcpy(dt+7, dt+9);     /* cut century */
554       switch (je->JobType) {
555       case JT_ADMIN:
556       case JT_RESTORE:
557          bstrncpy(level, "    ", sizeof(level));
558          break;
559       default:
560          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
561          level[4] = 0;
562          break;
563       }
564       switch (je->JobStatus) {
565       case JS_Created:
566          termstat = "Created";
567          break;
568       case JS_FatalError:
569       case JS_ErrorTerminated:
570          termstat = "Error";
571          break;
572       case JS_Differences:
573          termstat = "Diffs";
574          break;
575       case JS_Canceled:
576          termstat = "Cancel";
577          break;
578       case JS_Terminated:
579          termstat = "OK";
580          break;
581       default:
582          termstat = "Other";
583          break;
584       }
585       bstrncpy(JobName, je->Job, sizeof(JobName));
586       /* There are three periods after the Job name */
587       char *p;
588       for (int i=0; i<3; i++) {
589          if ((p=strrchr(JobName, '.')) != NULL) {
590             *p = 0;
591          }
592       }
593       bsendmsg(ua, _("%6d  %-4s %8s %14s %-7s  %-8s %s\n"), 
594          je->JobId,
595          level, 
596          edit_uint64_with_commas(je->JobFiles, b1),
597          edit_uint64_with_commas(je->JobBytes, b2), 
598          termstat,
599          dt, JobName);
600    }
601    bsendmsg(ua, "\n");
602    unlock_last_jobs_list();
603 }