]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
Restrict valid chars in Volume name + doc
[bacula/bacula] / bacula / src / dird / ua_cmds.c
1 /*
2  *
3  *   Bacula Director -- User Agent Commands
4  *
5  *     Kern Sibbald, September MM
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 /* Imported subroutines */
34 extern void run_job(JCR *jcr);
35
36 /* Imported variables */
37 extern int r_first;
38 extern int r_last;
39 extern struct s_res resources[];
40 extern char my_name[];
41 #ifndef USE_SEMAPHORE 
42 extern workq_t job_wq;                /* work queue */
43 #endif
44
45 extern char *list_pool;
46
47 /* Imported functions */
48 extern int statuscmd(UAContext *ua, char *cmd);
49 extern int listcmd(UAContext *ua, char *cmd);
50 extern int llistcmd(UAContext *ua, char *cmd);
51 extern int showcmd(UAContext *ua, char *cmd);
52 extern int messagescmd(UAContext *ua, char *cmd);
53 extern int autodisplaycmd(UAContext *ua, char *cmd);
54 extern int sqlquerycmd(UAContext *ua, char *cmd);
55 extern int querycmd(UAContext *ua, char *cmd);
56 extern int runcmd(UAContext *ua, char *cmd);
57 extern int retentioncmd(UAContext *ua, char *cmd);
58 extern int prunecmd(UAContext *ua, char *cmd);
59 extern int purgecmd(UAContext *ua, char *cmd);
60 extern int restorecmd(UAContext *ua, char *cmd);
61 extern int labelcmd(UAContext *ua, char *cmd);
62 extern int relabelcmd(UAContext *ua, char *cmd);
63 extern int update_slots(UAContext *ua);  /* ua_label.c */
64
65 /* Forward referenced functions */
66 static int addcmd(UAContext *ua, char *cmd),  createcmd(UAContext *ua, char *cmd), cancelcmd(UAContext *ua, char *cmd);
67 static int setdebugcmd(UAContext *ua, char *cmd);
68 static int helpcmd(UAContext *ua, char *cmd);
69 static int deletecmd(UAContext *ua, char *cmd);
70 static int usecmd(UAContext *ua, char *cmd),  unmountcmd(UAContext *ua, char *cmd);
71 static int versioncmd(UAContext *ua, char *cmd), automountcmd(UAContext *ua, char *cmd);
72 static int timecmd(UAContext *ua, char *cmd);
73 static int update_volume(UAContext *ua);
74 static int update_pool(UAContext *ua);
75 static int delete_volume(UAContext *ua);
76 static int delete_pool(UAContext *ua);
77 static int mountcmd(UAContext *ua, char *cmd);
78 static int updatecmd(UAContext *ua, char *cmd);
79 static int waitcmd(UAContext *ua, char *cmd);
80
81 int quitcmd(UAContext *ua, char *cmd);
82
83
84 struct cmdstruct { char *key; int (*func)(UAContext *ua, char *cmd); char *help; }; 
85 static struct cmdstruct commands[] = {
86  { N_("add"),        addcmd,       _("add media to a pool")},
87  { N_("autodisplay"), autodisplaycmd, _("autodisplay [on/off] -- console messages")},
88  { N_("automount"),   automountcmd,   _("automount [on/off] -- after label")},
89  { N_("cancel"),     cancelcmd,    _("cancel job=nnn -- cancel a job")},
90  { N_("create"),     createcmd,    _("create DB Pool from resource")},  
91  { N_("delete"),     deletecmd,    _("delete [pool=<pool-name> | media volume=<volume-name>]")},    
92  { N_("help"),       helpcmd,      _("print this command")},
93  { N_("label"),      labelcmd,     _("label a tape")},
94  { N_("relabel"),    relabelcmd,   _("relabel a tape")},
95  { N_("list"),       listcmd,      _("list [pools | jobs | jobtotals | media <pool> | files job=<nn>]; from catalog")},
96  { N_("llist"),      llistcmd,     _("full or long list like list command")},
97  { N_("messages"),   messagescmd,  _("messages")},
98  { N_("mount"),      mountcmd,     _("mount <storage-name>")},
99  { N_("restore"),    restorecmd,   _("restore files")},
100  { N_("prune"),      prunecmd,     _("prune expired records from catalog")},
101  { N_("purge"),      purgecmd,     _("purge records from catalog")},
102  { N_("run"),        runcmd,       _("run <job-name>")},
103  { N_("setdebug"),   setdebugcmd,  _("sets debug level")},
104  { N_("show"),       showcmd,      _("show (resource records) [jobs | pools | ... | all]")},
105  { N_("sqlquery"),   sqlquerycmd,  _("use SQL to query catalog")}, 
106  { N_("status"),     statuscmd,    _("status [storage | client]=<name>")},
107  { N_("unmount"),    unmountcmd,   _("unmount <storage-name>")},
108  { N_("update"),     updatecmd,    _("update Volume or Pool")},
109  { N_("use"),        usecmd,       _("use catalog xxx")},
110  { N_("version"),    versioncmd,   _("print Director version")},
111  { N_("quit"),       quitcmd,      _("quit")},
112  { N_("query"),      querycmd,     _("query catalog")},
113  { N_("time"),       timecmd,      _("print current time")},
114  { N_("exit"),       quitcmd,      _("exit = quit")},
115  { N_("wait"),       waitcmd,      _("wait until no jobs are running")},
116              };
117 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
118
119 /*
120  * Execute a command from the UA
121  */
122 int do_a_command(UAContext *ua, char *cmd)
123 {
124    unsigned int i;
125    int len, stat;
126    int found;
127
128    found = 0;
129    stat = 1;
130
131    Dmsg1(120, "Command: %s\n", ua->UA_sock->msg);
132    if (ua->argc == 0) {
133       return 1;
134    }
135
136    len = strlen(ua->argk[0]);
137    for (i=0; i<comsize; i++) {     /* search for command */
138       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
139          stat = (*commands[i].func)(ua, cmd);   /* go execute command */
140          found = 1;
141          break;
142       }
143    }
144    if (!found) {
145       pm_strcat(&ua->UA_sock->msg, _(": is an illegal command\n"));
146       ua->UA_sock->msglen = strlen(ua->UA_sock->msg);
147       bnet_send(ua->UA_sock);
148    }
149    return stat;
150 }
151
152 /*
153  * This is a common routine used to stuff the Pool DB record defaults
154  *   into the Media DB record just before creating a media (Volume) 
155  *   record.
156  */
157 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
158 {
159    mr->PoolId = pr->PoolId;
160    strcpy(mr->VolStatus, "Append");
161    mr->Recycle = pr->Recycle;
162    mr->VolRetention = pr->VolRetention;
163    mr->VolUseDuration = pr->VolUseDuration;
164    mr->MaxVolJobs = pr->MaxVolJobs;
165    mr->MaxVolFiles = pr->MaxVolFiles;
166    mr->MaxVolBytes = pr->MaxVolBytes;
167 }
168
169
170 /*
171  *  Add Volumes to an existing Pool
172  */
173 static int addcmd(UAContext *ua, char *cmd) 
174 {
175    POOL_DBR pr;
176    MEDIA_DBR mr;
177    int num, i, max, startnum;
178    int first_id = 0;
179    char name[MAX_NAME_LENGTH];
180    STORE *store;
181    int slot = 0;
182
183    bsendmsg(ua, _(
184 "You probably don't want to be using this command since it\n"
185 "creates database records without labeling the Volumes.\n"
186 "You probably want to use the \"label\" command.\n\n"));
187
188    if (!open_db(ua)) {
189       return 1;
190    }
191
192    memset(&pr, 0, sizeof(pr));
193    memset(&mr, 0, sizeof(mr));
194
195    if (!get_pool_dbr(ua, &pr)) {
196       return 1;
197    }
198
199    Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
200       pr.MaxVols, pr.PoolType);
201
202    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
203       bsendmsg(ua, _("Pool already has maximum volumes = %d\n"), pr.MaxVols);
204       for (;;) {
205          if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
206             return 1;
207          }
208          pr.MaxVols = ua->pint32_val;
209       }
210    }
211
212    /* Get media type */
213    if ((store = get_storage_resource(ua, 0)) != NULL) {
214       strcpy(mr.MediaType, store->media_type);
215    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
216       return 1;
217    }
218       
219    if (pr.MaxVols == 0) {
220       max = 1000;
221    } else {
222       max = pr.MaxVols - pr.NumVols;
223    }
224    for (;;) {
225       char buf[100]; 
226       sprintf(buf, _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
227       if (!get_pint(ua, buf)) {
228          return 1;
229       }
230       num = ua->pint32_val;
231       if (num < 0 || num > max) {
232          bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
233          continue;
234       }
235       break;
236    }
237 getVolName:
238    if (num == 0) {
239       if (!get_cmd(ua, _("Enter Volume name: "))) {
240          return 1;
241       }
242    } else {
243       if (!get_cmd(ua, _("Enter base volume name: "))) {
244          return 1;
245       }
246    }
247    /* Don't allow | in Volume name because it is the volume separator character */
248    if (!is_volume_name_legal(ua, ua->cmd)) {
249       goto getVolName;
250    }
251    if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
252       bsendmsg(ua, _("Volume name too long.\n"));
253       goto getVolName;
254    }
255    if (strlen(ua->cmd) == 0) {
256       bsendmsg(ua, _("Volume name must be at least one character long.\n"));
257       goto getVolName;
258    }
259
260    strcpy(name, ua->cmd);
261    if (num > 0) {
262       strcat(name, "%04d");
263
264       for (;;) {
265          if (!get_pint(ua, _("Enter the starting number: "))) {
266             return 1;
267          }
268          startnum = ua->pint32_val;
269          if (startnum < 1) {
270             bsendmsg(ua, _("Start number must be greater than zero.\n"));
271             continue;
272          }
273          break;
274       }
275    } else {
276       startnum = 1;
277       num = 1;
278    }
279
280    if (store && store->autochanger) {
281       if (!get_pint(ua, _("Enter slot (0 for none): "))) {
282          return 1;
283       }
284       slot = ua->pint32_val;
285    }
286            
287    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
288    for (i=startnum; i < num+startnum; i++) { 
289       sprintf(mr.VolumeName, name, i);
290       mr.Slot = slot++;
291       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
292       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
293          bsendmsg(ua, db_strerror(ua->db));
294          return 1;
295       }
296       if (i == startnum) {
297          first_id = mr.PoolId;
298       }
299    }
300    pr.NumVols += num;
301    Dmsg0(200, "Update pool record.\n"); 
302    if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
303       bsendmsg(ua, db_strerror(ua->db));
304       return 1;
305    }
306    bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
307
308    return 1;
309 }
310
311 /*
312  * Turn auto mount on/off  
313  * 
314  *  automount on 
315  *  automount off
316  */
317 int automountcmd(UAContext *ua, char *cmd)
318 {
319    char *onoff;
320
321    if (ua->argc != 2) {
322       if (!get_cmd(ua, _("Turn on or off? "))) {
323             return 1;
324       }
325       onoff = ua->cmd;
326    } else {
327       onoff = ua->argk[1];
328    }
329
330    ua->automount = (strcasecmp(onoff, _("off")) == 0) ? 0 : 1;
331    return 1; 
332 }
333
334
335 /*
336  * Cancel a job
337  */
338 static int cancelcmd(UAContext *ua, char *cmd)
339 {
340    int i;
341    int njobs = 0;
342    BSOCK *sd, *fd;
343    JCR *jcr = NULL;
344    char JobName[MAX_NAME_LENGTH];
345
346    if (!open_db(ua)) {
347       return 1;
348    }
349
350    for (i=1; i<ua->argc; i++) {
351       if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
352          if (!ua->argv[i]) {
353             break;
354          }
355          if (!(jcr=get_jcr_by_id(atoi(ua->argv[i])))) {
356             bsendmsg(ua, _("JobId %d is not running.\n"), atoi(ua->argv[i]));
357             return 1;
358          }
359          break;
360       } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
361          if (!ua->argv[i]) {
362             break;
363          }
364          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
365             bsendmsg(ua, _("Job %s is not running.\n"), ua->argv[i]);
366             return 1;
367          }
368          break;
369       }
370    }
371    /* If we still do not have a jcr,
372     *   throw up a list and ask the user to select one.
373     */
374    if (!jcr) {
375       /* Count Jobs running */
376       lock_jcr_chain();
377       for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) {
378          if (jcr->JobId == 0) {      /* this is us */
379             free_locked_jcr(jcr);
380             njobs--;
381             continue;
382          }
383          free_locked_jcr(jcr);
384       }
385       unlock_jcr_chain();
386
387       if (njobs == 0) {
388          bsendmsg(ua, _("No Jobs running.\n"));
389          return 1;
390       }
391       start_prompt(ua, _("Select Job:\n"));
392       lock_jcr_chain();
393       for (jcr=NULL; (jcr=get_next_jcr(jcr)); ) {
394          if (jcr->JobId == 0) {      /* this is us */
395             free_locked_jcr(jcr);
396             continue;
397          }
398          add_prompt(ua, jcr->Job);
399          free_locked_jcr(jcr);
400       }
401       unlock_jcr_chain();
402
403       if (do_prompt(ua, _("Choose Job to cancel"), JobName, sizeof(JobName)) < 0) {
404          return 1;
405       }
406       if (njobs == 1) {
407          if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
408             return 1;
409          }
410       }
411       jcr = get_jcr_by_full_name(JobName);
412       if (!jcr) {
413          bsendmsg(ua, _("Job %s not found.\n"), JobName);
414          return 1;
415       }
416    }
417      
418    switch (jcr->JobStatus) {
419    case JS_Created:
420    case JS_WaitJobRes:
421    case JS_WaitClientRes:
422    case JS_WaitStoreRes:
423    case JS_WaitMaxJobs:
424    case JS_WaitStartTime:
425       set_jcr_job_status(jcr, JS_Canceled);
426       bsendmsg(ua, _("JobId %d, Job %s marked to be canceled.\n"),
427               jcr->JobId, jcr->Job);
428 #ifndef USE_SEMAPHORE
429       workq_remove(&job_wq, jcr->work_item); /* attempt to remove it from queue */
430 #endif
431       free_jcr(jcr);
432       return 1;
433          
434    default:
435       set_jcr_job_status(jcr, JS_Canceled);
436
437       /* Cancel File daemon */
438       if (jcr->file_bsock) {
439          ua->jcr->client = jcr->client;
440          if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
441             bsendmsg(ua, _("Failed to connect to File daemon.\n"));
442             free_jcr(jcr);
443             return 1;
444          }
445          Dmsg0(200, "Connected to file daemon\n");
446          fd = ua->jcr->file_bsock;
447          bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
448          while (bnet_recv(fd) >= 0) {
449             bsendmsg(ua, "%s", fd->msg);
450          }
451          bnet_sig(fd, BNET_TERMINATE);
452          bnet_close(fd);
453          ua->jcr->file_bsock = NULL;
454       }
455
456       /* Cancel Storage daemon */
457       if (jcr->store_bsock) {
458          ua->jcr->store = jcr->store;
459          if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
460             bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
461             free_jcr(jcr);
462             return 1;
463          }
464          Dmsg0(200, "Connected to storage daemon\n");
465          sd = ua->jcr->store_bsock;
466          bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
467          while (bnet_recv(sd) >= 0) {
468             bsendmsg(ua, "%s", sd->msg);
469          }
470          bnet_sig(sd, BNET_TERMINATE);
471          bnet_close(sd);
472          ua->jcr->store_bsock = NULL;
473       }
474    }
475    free_jcr(jcr);
476
477    return 1; 
478 }
479
480 /*
481  * This is a common routine to create or update a
482  *   Pool DB base record from a Pool Resource. We handle
483  *   the setting of MaxVols and NumVols slightly differently
484  *   depending on if we are creating the Pool or we are
485  *   simply bringing it into agreement with the resource (updage).
486  */
487 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, int create)
488 {
489    strcpy(pr->PoolType, pool->pool_type);
490    if (create) {
491       pr->MaxVols = pool->max_volumes;
492       pr->NumVols = 0;
493    } else {          /* update pool */
494       if (pr->MaxVols != pool->max_volumes) {
495          pr->MaxVols = pool->max_volumes;
496       }
497       if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
498          pr->MaxVols = pr->NumVols;
499       }
500    }
501    pr->UseOnce = pool->use_volume_once;
502    pr->UseCatalog = pool->use_catalog;
503    pr->AcceptAnyVolume = pool->accept_any_volume;
504    pr->Recycle = pool->Recycle;
505    pr->VolRetention = pool->VolRetention;
506    pr->VolUseDuration = pool->VolUseDuration;
507    pr->MaxVolJobs = pool->MaxVolJobs;
508    pr->MaxVolFiles = pool->MaxVolFiles;
509    pr->MaxVolBytes = pool->MaxVolBytes;
510    if (pool->label_format) {
511       strcpy(pr->LabelFormat, pool->label_format);
512    } else {
513       strcpy(pr->LabelFormat, "*");    /* none */
514    }
515 }
516
517
518 /*
519  * Create a pool record from a given Pool resource
520  *   Also called from backup.c
521  * Returns: -1  on error
522  *           0  record already exists
523  *           1  record created
524  */
525
526 int create_pool(JCR *jcr, B_DB *db, POOL *pool, int create)
527 {
528    POOL_DBR  pr;
529
530    memset(&pr, 0, sizeof(POOL_DBR));
531
532    strcpy(pr.Name, pool->hdr.name);
533
534    if (db_get_pool_record(jcr, db, &pr)) {
535       /* Pool Exists */
536       if (!create) {  /* update request */
537          set_pooldbr_from_poolres(&pr, pool, 0);
538          db_update_pool_record(jcr, db, &pr);
539       }
540       return 0;                       /* exists */
541    }
542
543    set_pooldbr_from_poolres(&pr, pool, 1);
544
545    if (!db_create_pool_record(jcr, db, &pr)) {
546       return -1;                      /* error */
547    }
548    return 1;
549 }
550
551
552
553 /*
554  * Create a Pool Record in the database.
555  *  It is always created from the Resource record.
556  */
557 static int createcmd(UAContext *ua, char *cmd) 
558 {
559    POOL *pool;
560
561    if (!open_db(ua)) {
562       return 1;
563    }
564
565    pool = get_pool_resource(ua);
566    if (!pool) {
567       return 1;
568    }
569
570    switch (create_pool(ua->jcr, ua->db, pool, 1)) {
571    case 0:
572       bsendmsg(ua, _("Error: Pool %s already exists.\n\
573 Use update to change it.\n"), pool->hdr.name);
574       break;
575
576    case -1:
577       bsendmsg(ua, db_strerror(ua->db));
578       break;
579
580    default:
581      break;
582    }
583    bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
584    return 1;
585 }
586
587
588
589
590 /*
591  * Update a Pool Record in the database.
592  *  It is always updated from the Resource record.
593  *
594  *    update pool=<pool-name>
595  *         updates pool from Pool resource
596  *    update media pool=<pool-name> volume=<volume-name>
597  *         changes pool info for volume
598  */
599 static int updatecmd(UAContext *ua, char *cmd) 
600 {
601    static char *kw[] = {
602       N_("media"),  /* 0 */
603       N_("volume"), /* 1 */
604       N_("pool"),   /* 2 */
605       N_("slots"),  /* 3 */
606       NULL};
607
608    if (!open_db(ua)) {
609       return 1;
610    }
611
612    switch (find_arg_keyword(ua, kw)) {
613       case 0:
614       case 1:
615          update_volume(ua);
616          return 1;
617       case 2:
618          update_pool(ua);
619          return 1;
620       case 3:
621          update_slots(ua);
622          return 1;
623       default:
624          break;
625    }
626     
627    start_prompt(ua, _("Update choice:\n"));
628    add_prompt(ua, _("Volume parameters"));
629    add_prompt(ua, _("Pool from resource"));
630    add_prompt(ua, _("Slots from autochanger"));
631    switch (do_prompt(ua, _("Choose catalog item to update"), NULL, 0)) {
632       case 0:
633          update_volume(ua);
634          break;
635       case 1:
636          update_pool(ua);
637          break;
638       case 2:
639          update_slots(ua);
640          break;
641       default:
642          break;
643    }
644    return 1;
645 }
646
647 /*
648  * Update a media record -- allows you to change the
649  *  Volume status. E.g. if you want Bacula to stop
650  *  writing on the volume, set it to anything other
651  *  than Append.
652  */              
653 static int update_volume(UAContext *ua)
654 {
655    MEDIA_DBR mr;
656    POOLMEM *query;
657    char ed1[30];
658
659
660    for (int done=0; !done; ) {
661       if (!select_media_dbr(ua, &mr)) {
662          return 0;
663       }
664       bsendmsg(ua, _("Updating Volume \"%s\"\n"), mr.VolumeName);
665       start_prompt(ua, _("Parameters to modify:\n"));
666       add_prompt(ua, _("Volume Status"));
667       add_prompt(ua, _("Volume Retention Period"));
668       add_prompt(ua, _("Volume Use Duration"));
669       add_prompt(ua, _("Maximum Volume Jobs"));
670       add_prompt(ua, _("Maximum Volume Files"));
671       add_prompt(ua, _("Maximum Volume Bytes"));
672       add_prompt(ua, _("Recycle Flag"));
673       add_prompt(ua, _("Slot"));
674       add_prompt(ua, _("Volume Files"));
675       add_prompt(ua, _("Done"));
676       switch (do_prompt(ua, _("Select parameter to modify"), NULL, 0)) {
677       case 0:                         /* Volume Status */
678          /* Modify Volume Status */
679          bsendmsg(ua, _("Current Volume status is: %s\n"), mr.VolStatus);
680          start_prompt(ua, _("Possible Values are:\n"));
681          add_prompt(ua, "Append");      /* Better not translate these as */
682          add_prompt(ua, "Archive");     /* They are known in the database code */
683          add_prompt(ua, "Disabled");
684          add_prompt(ua, "Full");
685          add_prompt(ua, "Used");
686          add_prompt(ua, "Cleaning");
687          if (strcmp(mr.VolStatus, "Purged") == 0) {
688             add_prompt(ua, "Recycle");
689          }
690          add_prompt(ua, "Read-Only");
691          if (do_prompt(ua, _("Choose new Volume Status"), ua->cmd, sizeof(mr.VolStatus)) < 0) {
692             return 1;
693          }
694          bstrncpy(mr.VolStatus, ua->cmd, sizeof(mr.VolStatus));
695          query = get_pool_memory(PM_MESSAGE);
696          Mmsg(&query, "UPDATE Media SET VolStatus='%s' WHERE MediaId=%u",
697             mr.VolStatus, mr.MediaId);
698          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
699             bsendmsg(ua, "%s", db_strerror(ua->db));
700          } else {
701             bsendmsg(ua, _("New Volume status is: %s\n"), mr.VolStatus);
702          }
703          free_pool_memory(query);
704          break;
705       case 1:                         /* Retention */
706          bsendmsg(ua, _("Current retention seconds is: %s\n"),
707             edit_utime(mr.VolRetention, ed1));
708          if (!get_cmd(ua, _("Enter Volume Retention period: "))) {
709             return 0;
710          }
711          if (!duration_to_utime(ua->cmd, &mr.VolRetention)) {
712             bsendmsg(ua, _("Invalid retention period specified.\n"));
713             break;
714          }
715          query = get_pool_memory(PM_MESSAGE);
716          Mmsg(&query, "UPDATE Media SET VolRetention=%s WHERE MediaId=%u",
717             edit_uint64(mr.VolRetention, ed1), mr.MediaId);
718          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
719             bsendmsg(ua, "%s", db_strerror(ua->db));
720          } else {
721             bsendmsg(ua, _("New retention seconds is: %s\n"),
722                edit_utime(mr.VolRetention, ed1));
723          }
724          free_pool_memory(query);
725          break;
726
727       case 2:                         /* Use Duration */
728          bsendmsg(ua, _("Current use duration is: %s\n"),
729             edit_utime(mr.VolUseDuration, ed1));
730          if (!get_cmd(ua, _("Enter Volume Use Duration: "))) {
731             return 0;
732          }
733          if (!duration_to_utime(ua->cmd, &mr.VolUseDuration)) {
734             bsendmsg(ua, _("Invalid use duration specified.\n"));
735             break;
736          }
737          query = get_pool_memory(PM_MESSAGE);
738          Mmsg(&query, "UPDATE Media SET VolUseDuration=%s WHERE MediaId=%u",
739             edit_uint64(mr.VolUseDuration, ed1), mr.MediaId);
740          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
741             bsendmsg(ua, "%s", db_strerror(ua->db));
742          } else {
743             bsendmsg(ua, _("New use duration is: %s\n"),
744                edit_utime(mr.VolUseDuration, ed1));
745          }
746          free_pool_memory(query);
747          break;
748
749       case 3:                         /* Max Jobs */
750          int32_t maxjobs;
751          bsendmsg(ua, _("Current max jobs is: %u\n"), mr.MaxVolJobs);
752          if (!get_pint(ua, _("Enter new Maximum Jobs: "))) {
753             return 0;
754          }
755          maxjobs = ua->pint32_val;
756          query = get_pool_memory(PM_MESSAGE);
757          Mmsg(&query, "UPDATE Media SET MaxVolJobs=%u WHERE MediaId=%u",
758             maxjobs, mr.MediaId);
759          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
760             bsendmsg(ua, "%s", db_strerror(ua->db));
761          } else {
762             bsendmsg(ua, _("New max jobs is: %u\n"), maxjobs);
763          }
764          free_pool_memory(query);
765          break;
766
767       case 4:                         /* Max Files */
768          int32_t maxfiles;
769          bsendmsg(ua, _("Current max files is: %u\n"), mr.MaxVolFiles);
770          if (!get_pint(ua, _("Enter new Maximum Files: "))) {
771             return 0;
772          }
773          maxfiles = ua->pint32_val;
774          query = get_pool_memory(PM_MESSAGE);
775          Mmsg(&query, "UPDATE Media SET MaxVolFiles=%u WHERE MediaId=%u",
776             maxfiles, mr.MediaId);
777          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
778             bsendmsg(ua, "%s", db_strerror(ua->db));
779          } else {
780             bsendmsg(ua, _("New max files is: %u\n"), maxfiles);
781          }
782          free_pool_memory(query);
783          break;
784
785       case 5:                         /* Max Bytes */
786          uint64_t maxbytes;
787          bsendmsg(ua, _("Current value is: %s\n"), edit_uint64(mr.MaxVolBytes, ed1));
788          if (!get_cmd(ua, _("Enter new Maximum Bytes: "))) {
789             return 0;
790          }
791          if (!size_to_uint64(ua->cmd, strlen(ua->cmd), &maxbytes)) {
792             bsendmsg(ua, _("Invalid byte size specification.\n"));
793             break;
794          } 
795          query = get_pool_memory(PM_MESSAGE);
796          Mmsg(&query, "UPDATE Media SET MaxVolBytes=%s WHERE MediaId=%u",
797             edit_uint64(maxbytes, ed1), mr.MediaId);
798          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
799             bsendmsg(ua, "%s", db_strerror(ua->db));
800          } else {
801             bsendmsg(ua, _("New Max bytes is: %s\n"), edit_uint64(maxbytes, ed1));
802          }
803          free_pool_memory(query);
804          break;
805
806
807       case 6:                         /* Recycle */
808          int recycle;
809          bsendmsg(ua, _("Current recycle flag is: %s\n"),
810             mr.Recycle==1?_("yes"):_("no"));
811          if (!get_yesno(ua, _("Enter new Recycle status: "))) {
812             return 0;
813          }
814          recycle = ua->pint32_val;
815          query = get_pool_memory(PM_MESSAGE);
816          Mmsg(&query, "UPDATE Media SET Recycle=%d WHERE MediaId=%u",
817             recycle, mr.MediaId);
818          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
819             bsendmsg(ua, "%s", db_strerror(ua->db));
820          } else {       
821             bsendmsg(ua, _("New recycle flag is: %s\n"),
822                mr.Recycle==1?_("yes"):_("no"));
823          }
824          free_pool_memory(query);
825          break;
826
827       case 7:                         /* Slot */
828          int slot;
829          POOL_DBR pr;
830
831          memset(&pr, 0, sizeof(POOL_DBR));
832          pr.PoolId = mr.PoolId;
833          if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
834             bsendmsg(ua, "%s", db_strerror(ua->db));
835             return 0;
836          }
837          bsendmsg(ua, _("Current Slot is: %d\n"), mr.Slot);
838          if (!get_pint(ua, _("Enter new Slot: "))) {
839             return 0;
840          }
841          slot = ua->pint32_val;
842          if (pr.MaxVols > 0 && slot > (int)pr.MaxVols) {
843             bsendmsg(ua, _("Invalid slot, it must be between 0 and %d\n"),
844                pr.MaxVols);
845             break;
846          }
847          query = get_pool_memory(PM_MESSAGE);
848          Mmsg(&query, "UPDATE Media SET Slot=%d WHERE MediaId=%u",
849             slot, mr.MediaId);
850          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
851             bsendmsg(ua, "%s", db_strerror(ua->db));
852          } else {
853             bsendmsg(ua, "New Slot is: %d\n", slot);
854          }
855          free_pool_memory(query);
856          break;
857
858       case 8:                         /* Volume Files */
859          int32_t VolFiles;
860          bsendmsg(ua, _("Warning changing Volume Files can result\n"
861                         "in loss of data on your Volume\n\n"));
862          bsendmsg(ua, _("Current Volume Files is: %u\n"), mr.VolFiles);
863          if (!get_pint(ua, _("Enter new number of Files for Volume: "))) {
864             return 0;
865          }
866          VolFiles = ua->pint32_val;
867          if (VolFiles != (int)(mr.VolFiles + 1)) {
868             bsendmsg(ua, _("Normally, you should only increase Volume Files by one!\n"));
869             if (!get_yesno(ua, _("Continue? (yes/no): ")) || ua->pint32_val == 0) {
870                break;
871             }
872          }
873          query = get_pool_memory(PM_MESSAGE);
874          Mmsg(&query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%u",
875             VolFiles, mr.MediaId);
876          if (!db_sql_query(ua->db, query, NULL, NULL)) {  
877             bsendmsg(ua, "%s", db_strerror(ua->db));
878          } else {
879             bsendmsg(ua, _("New Volume Files is: %u\n"), VolFiles);
880          }
881          free_pool_memory(query);
882          break;
883
884       default:                        /* Done or error */
885          bsendmsg(ua, "Selection done.\n");
886          return 1;
887       }
888    }
889    return 1;
890 }
891
892 /* 
893  * Update pool record -- pull info from current POOL resource
894  */
895 static int update_pool(UAContext *ua)
896 {
897    POOL_DBR  pr;
898    int id;
899    POOL *pool;
900    POOLMEM *query;       
901    
902    
903    pool = get_pool_resource(ua);
904    if (!pool) {
905       return 0;
906    }
907
908    memset(&pr, 0, sizeof(pr));
909    strcpy(pr.Name, pool->hdr.name);
910    if (!get_pool_dbr(ua, &pr)) {
911       return 0;
912    }
913
914    set_pooldbr_from_poolres(&pr, pool, 0); /* update */
915
916    id = db_update_pool_record(ua->jcr, ua->db, &pr);
917    if (id <= 0) {
918       bsendmsg(ua, _("db_update_pool_record returned %d. ERR=%s\n"),
919          id, db_strerror(ua->db));
920    }
921    query = get_pool_memory(PM_MESSAGE);
922    Mmsg(&query, list_pool, pr.PoolId);
923    db_list_sql_query(ua->jcr, ua->db, query, prtit, ua, 1, 0);
924    free_pool_memory(query);
925    bsendmsg(ua, _("Pool DB record updated from resource.\n"));
926    return 1;
927 }
928
929
930 static void do_storage_setdebug(UAContext *ua, STORE *store, int level)
931 {
932    BSOCK *sd;
933
934    ua->jcr->store = store;
935    /* Try connecting for up to 15 seconds */
936    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"), 
937       store->hdr.name, store->address, store->SDport);
938    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
939       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
940       return;
941    }
942    Dmsg0(120, _("Connected to storage daemon\n"));
943    sd = ua->jcr->store_bsock;
944    bnet_fsend(sd, "setdebug=%d\n", level);
945    if (bnet_recv(sd) >= 0) {
946       bsendmsg(ua, "%s", sd->msg);
947    }
948    bnet_sig(sd, BNET_TERMINATE);
949    bnet_close(sd);
950    ua->jcr->store_bsock = NULL;
951    return;  
952 }
953    
954 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level)
955 {
956    BSOCK *fd;
957
958    /* Connect to File daemon */
959
960    ua->jcr->client = client;
961    /* Try to connect for 15 seconds */
962    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"), 
963       client->hdr.name, client->address, client->FDport);
964    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
965       bsendmsg(ua, _("Failed to connect to Client.\n"));
966       return;
967    }
968    Dmsg0(120, "Connected to file daemon\n");
969    fd = ua->jcr->file_bsock;
970    bnet_fsend(fd, "setdebug=%d\n", level);
971    if (bnet_recv(fd) >= 0) {
972       bsendmsg(ua, "%s", fd->msg);
973    }
974    bnet_sig(fd, BNET_TERMINATE);
975    bnet_close(fd);
976    ua->jcr->file_bsock = NULL;
977
978    return;  
979 }
980
981
982 static void do_all_setdebug(UAContext *ua, int level)
983 {
984    STORE *store, **unique_store;
985    CLIENT *client, **unique_client;
986    int i, j, found;
987
988    /* Director */
989    debug_level = level;
990
991    /* Count Storage items */
992    LockRes();
993    store = NULL;
994    for (i=0; (store = (STORE *)GetNextRes(R_STORAGE, (RES *)store)); i++)
995       { }
996    unique_store = (STORE **) malloc(i * sizeof(STORE));
997    /* Find Unique Storage address/port */         
998    store = (STORE *)GetNextRes(R_STORAGE, NULL);
999    i = 0;
1000    unique_store[i++] = store;
1001    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1002       found = 0;
1003       for (j=0; j<i; j++) {
1004          if (strcmp(unique_store[j]->address, store->address) == 0 &&
1005              unique_store[j]->SDport == store->SDport) {
1006             found = 1;
1007             break;
1008          }
1009       }
1010       if (!found) {
1011          unique_store[i++] = store;
1012          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1013       }
1014    }
1015    UnlockRes();
1016
1017    /* Call each unique Storage daemon */
1018    for (j=0; j<i; j++) {
1019       do_storage_setdebug(ua, unique_store[j], level);
1020    }
1021    free(unique_store);
1022
1023    /* Count Client items */
1024    LockRes();
1025    client = NULL;
1026    for (i=0; (client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client)); i++)
1027       { }
1028    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1029    /* Find Unique Client address/port */         
1030    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1031    i = 0;
1032    unique_client[i++] = client;
1033    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1034       found = 0;
1035       for (j=0; j<i; j++) {
1036          if (strcmp(unique_client[j]->address, client->address) == 0 &&
1037              unique_client[j]->FDport == client->FDport) {
1038             found = 1;
1039             break;
1040          }
1041       }
1042       if (!found) {
1043          unique_client[i++] = client;
1044          Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1045       }
1046    }
1047    UnlockRes();
1048
1049    /* Call each unique File daemon */
1050    for (j=0; j<i; j++) {
1051       do_client_setdebug(ua, unique_client[j], level);
1052    }
1053    free(unique_client);
1054 }
1055
1056 /*
1057  * setdebug level=nn all
1058  */
1059 static int setdebugcmd(UAContext *ua, char *cmd)
1060 {
1061    STORE *store;
1062    CLIENT *client;
1063    int level;
1064    int i;
1065
1066    if (!open_db(ua)) {
1067       return 1;
1068    }
1069    Dmsg1(120, "setdebug:%s:\n", cmd);
1070
1071    level = -1;
1072    i = find_arg_with_value(ua, _("level"));
1073    if (i >= 0) {
1074       level = atoi(ua->argv[i]);
1075    }
1076    if (level < 0) {
1077       if (!get_pint(ua, _("Enter new debug level: "))) {
1078          return 1;
1079       }
1080       level = ua->pint32_val;
1081    }
1082
1083    /* General debug? */
1084    for (i=1; i<ua->argc; i++) {
1085       if (strcasecmp(ua->argk[i], _("all")) == 0) {
1086          do_all_setdebug(ua, level);
1087          return 1;
1088       }
1089       if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
1090           strcasecmp(ua->argk[i], _("director")) == 0) {
1091          debug_level = level;
1092          return 1;
1093       }
1094       if (strcasecmp(ua->argk[i], _("client")) == 0) {
1095          client = NULL;
1096          if (ua->argv[i]) {
1097             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1098             if (client) {
1099                do_client_setdebug(ua, client, level);
1100                return 1;
1101             }
1102          }
1103          client = select_client_resource(ua);   
1104          if (client) {
1105             do_client_setdebug(ua, client, level);
1106             return 1;
1107          }
1108       }
1109
1110       if (strcasecmp(ua->argk[i], _("store")) == 0 ||
1111           strcasecmp(ua->argk[i], _("storage")) == 0) {
1112          store = NULL;
1113          if (ua->argv[i]) {
1114             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1115             if (store) {
1116                do_storage_setdebug(ua, store, level);
1117                return 1;
1118             }
1119          }
1120          store = get_storage_resource(ua, 0);
1121          if (store) {
1122             do_storage_setdebug(ua, store, level);
1123             return 1;
1124          }
1125       }
1126    } 
1127    /*
1128     * We didn't find an appropriate keyword above, so
1129     * prompt the user.
1130     */
1131    start_prompt(ua, _("Available daemons are: \n"));
1132    add_prompt(ua, _("Director"));
1133    add_prompt(ua, _("Storage"));
1134    add_prompt(ua, _("Client"));
1135    add_prompt(ua, _("All"));
1136    switch(do_prompt(ua, _("Select daemon type to set debug level"), NULL, 0)) {
1137       case 0:                         /* Director */
1138          debug_level = level;
1139          break;
1140       case 1:
1141          store = get_storage_resource(ua, 0);
1142          if (store) {
1143             do_storage_setdebug(ua, store, level);
1144          }
1145          break;
1146       case 2:
1147          client = select_client_resource(ua);
1148          if (client) {
1149             do_client_setdebug(ua, client, level);
1150          }
1151          break;
1152       case 3:
1153          do_all_setdebug(ua, level);
1154          break;
1155       default:
1156          break;
1157    }
1158    return 1;
1159 }
1160
1161 /*
1162  * print time
1163  */
1164 static int timecmd(UAContext *ua, char *cmd)
1165 {
1166    char sdt[50];
1167    time_t ttime = time(NULL);
1168    struct tm tm;
1169    localtime_r(&ttime, &tm);
1170    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1171    bsendmsg(ua, "%s\n", sdt);
1172    return 1;
1173 }
1174
1175
1176
1177 /*
1178  * Delete Pool records (should purge Media with it).
1179  *
1180  *  delete pool=<pool-name>
1181  *  delete media pool=<pool-name> volume=<name>
1182  */
1183 static int deletecmd(UAContext *ua, char *cmd)
1184 {
1185    static char *keywords[] = {
1186       N_("volume"),
1187       N_("pool"),
1188       NULL};
1189
1190    if (!open_db(ua)) {
1191       return 1;
1192    }
1193
1194    bsendmsg(ua, _(
1195 "In general it is not a good idea to delete either a\n"
1196 "Pool or a Volume since they may contain data.\n\n"));
1197      
1198    switch (find_arg_keyword(ua, keywords)) {
1199       case 0:
1200          delete_volume(ua);     
1201          return 1;
1202       case 1:
1203          delete_pool(ua);
1204          return 1;
1205       default:
1206          break;
1207    }
1208    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1209       case 0:
1210          delete_volume(ua);
1211          break;
1212       case 1:
1213          delete_pool(ua);
1214          break;
1215       default:
1216          bsendmsg(ua, _("Nothing done.\n"));
1217          break;
1218    }
1219    return 1;
1220 }
1221
1222 /*
1223  * Delete media records from database -- dangerous 
1224  */
1225 static int delete_volume(UAContext *ua)
1226 {
1227    MEDIA_DBR mr;
1228
1229    if (!select_media_dbr(ua, &mr)) {
1230       return 1;
1231    }
1232    bsendmsg(ua, _("\nThis command will delete volume %s\n"
1233       "and all Jobs saved on that volume from the Catalog\n"),
1234       mr.VolumeName);
1235
1236    if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1237       return 1;
1238    }
1239    if (ua->pint32_val) {
1240       db_delete_media_record(ua->jcr, ua->db, &mr);
1241    }
1242    return 1;
1243 }
1244
1245 /*
1246  * Delete a pool record from the database -- dangerous   
1247  */
1248 static int delete_pool(UAContext *ua)
1249 {
1250    POOL_DBR  pr;
1251    
1252    memset(&pr, 0, sizeof(pr));
1253
1254    if (!get_pool_dbr(ua, &pr)) {
1255       return 1;
1256    }
1257    if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1258       return 1;
1259    }
1260    if (ua->pint32_val) {
1261       db_delete_pool_record(ua->jcr, ua->db, &pr);
1262    }
1263    return 1;
1264 }
1265
1266
1267 static void do_mount_cmd(int mount, UAContext *ua, char *cmd)
1268 {
1269    STORE *store;
1270    BSOCK *sd;
1271    char dev_name[MAX_NAME_LENGTH];
1272
1273
1274    if (!open_db(ua)) {
1275       return;
1276    }
1277    Dmsg1(120, "mount: %s\n", ua->UA_sock->msg);
1278
1279    store = get_storage_resource(ua, 1);
1280    if (!store) {
1281       return;
1282    }
1283
1284    Dmsg2(120, "Found storage, MediaType=%s DevName=%s\n",
1285       store->media_type, store->dev_name);
1286
1287    ua->jcr->store = store;
1288    if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
1289       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1290       return;
1291    }
1292    sd = ua->jcr->store_bsock;
1293    strcpy(dev_name, store->dev_name);
1294    bash_spaces(dev_name);
1295    if (mount) {
1296       bnet_fsend(sd, "mount %s", dev_name);
1297    } else {
1298       bnet_fsend(sd, "unmount %s", dev_name);
1299    }
1300    while (bnet_recv(sd) >= 0) {
1301       bsendmsg(ua, "%s", sd->msg);
1302       if (strncmp(sd->msg, "3001 OK mount.", 14) == 0) {
1303           /***** ****FIXME**** fix JobStatus */
1304       }
1305    }
1306    bnet_sig(sd, BNET_TERMINATE);
1307    bnet_close(sd);
1308    ua->jcr->store_bsock = NULL;
1309 }
1310
1311 /*
1312  * mount [storage | device] <name>
1313  */
1314 static int mountcmd(UAContext *ua, char *cmd)
1315 {
1316    do_mount_cmd(1, ua, cmd);          /* mount */
1317    return 1;
1318 }
1319
1320
1321 /*
1322  * unmount [storage | device] <name>
1323  */
1324 static int unmountcmd(UAContext *ua, char *cmd)
1325 {
1326    do_mount_cmd(0, ua, cmd);          /* unmount */
1327    return 1;
1328 }
1329
1330
1331 /*
1332  * Switch databases
1333  *   use catalog=<name>
1334  */
1335 static int usecmd(UAContext *ua, char *cmd)
1336 {
1337    CAT *oldcatalog, *catalog;
1338
1339
1340    close_db(ua);                      /* close any previously open db */
1341    oldcatalog = ua->catalog;
1342
1343    if (!(catalog = get_catalog_resource(ua))) {
1344       ua->catalog = oldcatalog;
1345    } else {
1346       ua->catalog = catalog;
1347    }
1348    if (open_db(ua)) {
1349       bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1350          ua->catalog->hdr.name, ua->catalog->db_name);
1351    }
1352    return 1;
1353 }
1354
1355 int quitcmd(UAContext *ua, char *cmd) 
1356 {
1357    ua->quit = TRUE;
1358    return 1;
1359 }
1360
1361 /*
1362  * Wait until no job is running 
1363  */
1364 int waitcmd(UAContext *ua, char *cmd) 
1365 {
1366    bmicrosleep(0, 200000);            /* let job actually start */
1367    for (int running=1; running; ) {
1368       running = 0;
1369       lock_jcr_chain();
1370       for (JCR *jcr=NULL; (jcr=get_next_jcr(jcr)); ) {
1371          if (jcr->JobId != 0) {
1372             running = 1;
1373             free_locked_jcr(jcr);
1374             break;
1375          }
1376          free_locked_jcr(jcr);
1377       }
1378       unlock_jcr_chain();
1379       if (running) {
1380          bmicrosleep(1, 0);
1381       }
1382    }
1383    return 1;
1384 }
1385
1386
1387 static int helpcmd(UAContext *ua, char *cmd)
1388 {
1389    unsigned int i;
1390
1391 /* usage(); */
1392    bsendmsg(ua, _("  Command    Description\n  =======    ===========\n"));
1393    for (i=0; i<comsize; i++) {
1394       bsendmsg(ua, _("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1395    }
1396    bsendmsg(ua, "\n");
1397    return 1;
1398 }
1399
1400 static int versioncmd(UAContext *ua, char *cmd)
1401 {
1402    bsendmsg(ua, "%s Version: " VERSION " (" BDATE ")\n", my_name);
1403    return 1;
1404 }
1405
1406
1407 /* A bit brain damaged in that if the user has not done
1408  * a "use catalog xxx" command, we simply find the first
1409  * catalog resource and open it.
1410  */
1411 int open_db(UAContext *ua)
1412 {
1413    if (ua->db) {
1414       return 1;
1415    }
1416    if (!ua->catalog) {
1417       LockRes();
1418       ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1419       UnlockRes();
1420       if (!ua->catalog) {    
1421          bsendmsg(ua, _("Could not find a Catalog resource\n"));
1422          return 0;
1423       } else {
1424          bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"), 
1425             ua->catalog->hdr.name, ua->catalog->db_name);
1426       }
1427    }
1428
1429    Dmsg0(150, "Open database\n");
1430    ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1431                              ua->catalog->db_password, ua->catalog->db_address,
1432                              ua->catalog->db_port, ua->catalog->db_socket);
1433    if (!db_open_database(ua->jcr, ua->db)) {
1434       bsendmsg(ua, _("Could not open DB %s: ERR=%s"), 
1435          ua->catalog->db_name, db_strerror(ua->db));
1436       close_db(ua);
1437       return 0;
1438    }
1439    ua->jcr->db = ua->db;
1440    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1441    return 1;
1442 }
1443
1444 void close_db(UAContext *ua)
1445 {
1446    if (ua->db) {
1447       db_close_database(ua->jcr, ua->db);
1448    }
1449    ua->db = NULL;
1450    ua->jcr->db = NULL;
1451 }