]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
Fix setip crash + missing unlocks()+cleanups
[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;
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.\n"), dt, last_job.NumJobs,
206         last_job.NumJobs == 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    lock_jcr_chain();
394    for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) {
395       if (jcr->JobId == 0) {      /* this is us */
396          /* this is a console or other control job. We only show console
397           * jobs in the status output.
398           */
399          if (jcr->JobType == JT_CONSOLE) {
400             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
401             bsendmsg(ua, _("Console connected at %s\n"), dt);
402          }
403          njobs--;
404       }
405       free_locked_jcr(jcr);
406    }
407    if (njobs == 0) {
408       unlock_jcr_chain();
409       bsendmsg(ua, _("No Running Jobs.\n"));
410       Dmsg0(200, "leave list_run_jobs()\n");
411       return;
412    }
413    njobs = 0;
414    bsendmsg(ua, _("\nRunning Jobs:\n"));
415    bsendmsg(ua, _("Level JobId  Job                        Status\n"));
416    bsendmsg(ua, _("====================================================================\n"));
417    for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) {
418       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
419          njobs--;
420          free_locked_jcr(jcr);
421          continue;
422       }
423       switch (jcr->JobStatus) {
424       case JS_Created:
425          msg = _("is waiting execution");
426          break;
427       case JS_Running:
428          msg = _("is running");
429          break;
430       case JS_Blocked:
431          msg = _("is blocked");
432          break;
433       case JS_Terminated:
434          msg = _("has terminated");
435          break;
436       case JS_ErrorTerminated:
437          msg = _("has erred");
438          break;
439       case JS_Error:
440          msg = _("has errors");
441          break;
442       case JS_FatalError:
443          msg = _("has a fatal error");
444          break;
445       case JS_Differences:
446          msg = _("has verify differences");
447          break;
448       case JS_Canceled:
449          msg = _("has been canceled");
450          break;
451       case JS_WaitFD:
452          msg = (char *) get_pool_memory(PM_FNAME);
453          Mmsg(&msg, _("is waiting on Client %s"), jcr->client->hdr.name);
454          pool_mem = true;
455          break;
456       case JS_WaitSD:
457          msg = (char *) get_pool_memory(PM_FNAME);
458          Mmsg(&msg, _("is waiting on Storage %s"), jcr->store->hdr.name);
459          pool_mem = true;
460          break;
461       case JS_WaitStoreRes:
462          msg = _("is waiting on max Storage jobs");
463          break;
464       case JS_WaitClientRes:
465          msg = _("is waiting on max Client jobs");
466          break;
467       case JS_WaitJobRes:
468          msg = _("is waiting on max Job jobs");
469          break;
470       case JS_WaitMaxJobs:
471          msg = _("is waiting on max total jobs");
472          break;
473       case JS_WaitStartTime:
474          msg = _("is waiting for its start time");
475          break;
476       case JS_WaitPriority:
477          msg = _("is waiting for higher priority jobs to finish");
478          break;
479
480       default:
481          msg = (char *) get_pool_memory(PM_FNAME);
482          Mmsg(&msg, _("is in unknown state %c"), jcr->JobStatus);
483          pool_mem = true;
484          break;
485       }
486       /* 
487        * Now report Storage daemon status code 
488        */
489       switch (jcr->SDJobStatus) {
490       case JS_WaitMount:
491          if (pool_mem) {
492             free_pool_memory(msg);
493             pool_mem = false;
494          }
495          msg = _("is waiting for a mount request");
496          break;
497       case JS_WaitMedia:
498          if (pool_mem) {
499             free_pool_memory(msg);
500             pool_mem = false;
501          }
502          msg = _("is waiting for an appendable Volume");
503          break;
504       case JS_WaitFD:
505          if (!pool_mem) {
506             msg = (char *) get_pool_memory(PM_FNAME);
507             pool_mem = true;
508          }
509          Mmsg(&msg, _("is waiting for Client %s to connect to Storage %s"),
510               jcr->client->hdr.name, jcr->store->hdr.name);
511          break;
512       }
513       switch (jcr->JobType) {
514       case JT_ADMIN:
515       case JT_RESTORE:
516          bstrncpy(level, "    ", sizeof(level));
517          break;
518       default:
519          bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
520          level[4] = 0;
521          break;
522       }
523
524       bsendmsg(ua, _("%-4s %6d  %-20s %s\n"), 
525          level, 
526          jcr->JobId,
527          jcr->Job,
528          msg);
529
530       if (pool_mem) {
531          free_pool_memory(msg);
532          pool_mem = false;
533       }
534       free_locked_jcr(jcr);
535    }
536    unlock_jcr_chain();
537    bsendmsg(ua, "\n");
538    Dmsg0(200, "leave list_run_jobs()\n");
539 }
540
541 static void list_terminated_jobs(UAContext *ua)
542 {
543    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
544    char level[10];
545
546    if (last_jobs->empty()) {
547       bsendmsg(ua, _("No Terminated Jobs.\n"));
548       return;
549    }
550    lock_last_jobs_list();
551    struct s_last_job *je;
552    bsendmsg(ua, _("\nTerminated Jobs:\n"));
553    bsendmsg(ua, _(" JobId  Level   Files          Bytes Status   Finished        Name \n"));
554    bsendmsg(ua, _("======================================================================\n"));
555    foreach_dlist(je, last_jobs) {
556       char JobName[MAX_NAME_LENGTH];
557       char *termstat;
558
559       bstrftime_nc(dt, sizeof(dt), je->end_time);
560       switch (je->JobType) {
561       case JT_ADMIN:
562       case JT_RESTORE:
563          bstrncpy(level, "    ", sizeof(level));
564          break;
565       default:
566          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
567          level[4] = 0;
568          break;
569       }
570       switch (je->JobStatus) {
571       case JS_Created:
572          termstat = "Created";
573          break;
574       case JS_FatalError:
575       case JS_ErrorTerminated:
576          termstat = "Error";
577          break;
578       case JS_Differences:
579          termstat = "Diffs";
580          break;
581       case JS_Canceled:
582          termstat = "Cancel";
583          break;
584       case JS_Terminated:
585          termstat = "OK";
586          break;
587       default:
588          termstat = "Other";
589          break;
590       }
591       bstrncpy(JobName, je->Job, sizeof(JobName));
592       /* There are three periods after the Job name */
593       char *p;
594       for (int i=0; i<3; i++) {
595          if ((p=strrchr(JobName, '.')) != NULL) {
596             *p = 0;
597          }
598       }
599       bsendmsg(ua, _("%6d  %-4s %8s %14s %-7s  %-8s %s\n"), 
600          je->JobId,
601          level, 
602          edit_uint64_with_commas(je->JobFiles, b1),
603          edit_uint64_with_commas(je->JobBytes, b2), 
604          termstat,
605          dt, JobName);
606    }
607    bsendmsg(ua, "\n");
608    unlock_last_jobs_list();
609 }