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