]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
Retention period updates
[bacula/bacula] / bacula / src / dird / ua_cmds.c
1 /*
2  *
3  *   Bacula Director -- User Agent Commands
4  *
5  *     Kern Sibbald, September MM
6  */
7
8 /*
9    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License as
13    published by the Free Software Foundation; either version 2 of
14    the License, or (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19    General Public License for more details.
20
21    You should have received a copy of the GNU General Public
22    License along with this program; if not, write to the Free
23    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24    MA 02111-1307, USA.
25
26  */
27
28 #include "bacula.h"
29 #include "dird.h"
30 #include "ua.h"
31
32 /* Imported subroutines */
33 extern void run_job(JCR *jcr);
34
35 /* Imported variables */
36 extern int r_first;
37 extern int r_last;
38 extern struct s_res resources[];
39 extern int console_msg_pending;
40 extern FILE *con_fd;
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
56 /* Forward referenced functions */
57 static int addcmd(UAContext *ua, char *cmd),  createcmd(UAContext *ua, char *cmd), cancelcmd(UAContext *ua, char *cmd);
58 static int setdebugcmd(UAContext *ua, char *cmd);
59 static int helpcmd(UAContext *ua, char *cmd);
60 static int deletecmd(UAContext *ua, char *cmd);
61 static int usecmd(UAContext *ua, char *cmd),  unmountcmd(UAContext *ua, char *cmd);
62 static int labelcmd(UAContext *ua, char *cmd), mountcmd(UAContext *ua, char *cmd), updatecmd(UAContext *ua, char *cmd);
63 static int versioncmd(UAContext *ua, char *cmd), automountcmd(UAContext *ua, char *cmd);
64 static int update_media(UAContext *ua);
65 static int update_pool(UAContext *ua);
66 static int delete_media(UAContext *ua);
67 static int delete_pool(UAContext *ua);
68
69 int quitcmd(UAContext *ua, char *cmd);
70
71
72 struct cmdstruct { char *key; int (*func)(UAContext *ua, char *cmd); char *help; }; 
73 static struct cmdstruct commands[] = {
74  { N_("add"),        addcmd,       _("add media to a pool")},
75  { N_("autodisplay"), autodisplaycmd, _("autodisplay [on/off] -- console messages")},
76  { N_("automount"),   automountcmd,   _("automount [on/off] -- after label")},
77  { N_("cancel"),     cancelcmd,    _("cancel job=nnn -- cancel a job")},
78  { N_("create"),     createcmd,    _("create DB Pool from resource")},  
79  { N_("delete"),     deletecmd,    _("delete [pool=<pool-name> | media volume=<volume-name>]")},    
80  { N_("help"),       helpcmd,      _("print this command")},
81  { N_("label"),      labelcmd,     _("label a tape")},
82  { N_("list"),       listcmd,      _("list [pools | jobs | jobtotals | media <pool> | files job=<nn>]; from catalog")},
83  { N_("messages"),   messagescmd,  _("messages")},
84  { N_("mount"),      mountcmd,     _("mount <storage-name>")},
85  { N_("prune"),      prunecmd,     _("prune expired records from catalog")},
86  { N_("purge"),      purgecmd,     _("purge records from catalog")},
87  { N_("run"),        runcmd,       _("run <job-name>")},
88  { N_("setdebug"),   setdebugcmd,  _("sets debug level")},
89  { N_("show"),       showcmd,      _("show (resource records) [jobs | pools | ... | all]")},
90  { N_("sqlquery"),   sqlquerycmd,  _("use SQL to query catalog")}, 
91  { N_("status"),     statuscmd,    _("status [storage | client]=<name>")},
92  { N_("unmount"),    unmountcmd,   _("unmount <storage-name>")},
93  { N_("update"),     updatecmd,    _("update DB Pool from resource")},
94  { N_("use"),        usecmd,       _("use catalog xxx")},
95  { N_("version"),    versioncmd,   _("print Director version")},
96  { N_("quit"),       quitcmd,      _("quit")},
97  { N_("query"),      querycmd,     _("query catalog")},
98  { N_("exit"),       quitcmd,      _("exit = quit")},
99              };
100 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
101
102 /*
103  * Execute a command from the UA
104  */
105 int do_a_command(UAContext *ua, char *cmd)
106 {
107    unsigned int i;
108    int len, stat;
109    int found;
110
111    found = 0;
112    stat = 1;
113
114    Dmsg1(20, "Command: %s\n", ua->UA_sock->msg);
115    if (ua->argc == 0) {
116       return 1;
117    }
118
119    len = strlen(ua->argk[0]);
120    for (i=0; i<comsize; i++)       /* search for command */
121       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
122          stat = (*commands[i].func)(ua, cmd);   /* go execute command */
123          found = 1;
124          break;
125       }
126    if (!found) {
127       strcat(ua->UA_sock->msg, _(": is an illegal command\n"));
128       ua->UA_sock->msglen = strlen(ua->UA_sock->msg);
129       bnet_send(ua->UA_sock);
130    }
131    return stat;
132 }
133
134
135 /*
136  *  Add Volumes to an existing Pool
137  *
138  */
139 static int addcmd(UAContext *ua, char *cmd) 
140 {
141    POOL_DBR pr;
142    MEDIA_DBR mr;
143    int num, i, max, startnum;
144    int first_id = 0;
145    char name[MAX_NAME_LENGTH];
146
147    bsendmsg(ua, _(
148 "You probably don't want to be using this command since it\n"
149 "creates database records without labeling the Volumes.\n"
150 "You probably want to use the label command.\n\n"));
151
152    if (!open_db(ua)) {
153       return 1;
154    }
155
156    memset(&pr, 0, sizeof(pr));
157    memset(&mr, 0, sizeof(mr));
158
159    if (!get_pool_dbr(ua, &pr)) {
160       return 1;
161    }
162
163    Dmsg4(20, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
164       pr.MaxVols, pr.PoolType);
165
166    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
167       bsendmsg(ua, _("Pool already has maximum volumes = %d\n"), pr.MaxVols);
168       for (;;) {
169          if (!get_cmd(ua, _("Enter new maximum (zero for unlimited): "))) {
170             return 1;
171          }
172          pr.MaxVols = atoi(ua->cmd);
173          if (pr.MaxVols < 0) {
174             bsendmsg(ua, _("Max vols must be zero or greater.\n"));
175             continue;
176          }
177          break;
178       }
179    }
180
181    if (!get_media_type(ua, mr.MediaType)) {
182       return 1;
183    }
184
185    if (pr.MaxVols == 0) {
186       max = 1000;
187    } else {
188       max = pr.MaxVols - pr.NumVols;
189    }
190    for (;;) {
191       char buf[100]; 
192       sprintf(buf, _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
193       if (!get_cmd(ua, buf)) {
194          return 1;
195       }
196       num = atoi(ua->cmd);
197       if (num < 0 || num > max) {
198          bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
199          continue;
200       }
201       break;
202    }
203 getVolName:
204    if (num == 0) {
205       if (!get_cmd(ua, _("Enter Volume name: "))) {
206          return 1;
207       }
208    } else {
209       if (!get_cmd(ua, _("Enter base volume name: "))) {
210          return 1;
211       }
212    }
213    if (strchr(ua->cmd, '|')) {
214       bsendmsg(ua, _("Illegal character | in a volume name.\n"));
215       goto getVolName;
216    }
217    if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
218       bsendmsg(ua, _("Volume name too long.\n"));
219       goto getVolName;
220    }
221
222    strcpy(name, ua->cmd);
223    if (num > 0) {
224       strcat(name, "%04d");
225
226       for (;;) {
227          if (!get_cmd(ua, _("Enter the starting number: "))) {
228             return 1;
229          }
230          startnum = atoi(ua->cmd);
231          if (startnum < 1) {
232             bsendmsg(ua, _("Start number must be greater than zero.\n"));
233             continue;
234          }
235          break;
236       }
237    } else {
238       startnum = 1;
239       num = 1;
240    }
241            
242    mr.PoolId = pr.PoolId;
243    strcpy(mr.VolStatus, "Append");
244    mr.Recycle = pr.Recycle;
245    mr.VolRetention = pr.VolumeRetention;
246    for (i=startnum; i < num+startnum; i++) { 
247       sprintf(mr.VolumeName, name, i);
248       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
249       if (!db_create_media_record(ua->db, &mr)) {
250          bsendmsg(ua, db_strerror(ua->db));
251          return 1;
252       }
253       if (i == startnum) {
254          first_id = mr.PoolId;
255       }
256    }
257    pr.NumVols += num;
258    Dmsg0(200, "Update pool record.\n"); 
259    if (db_update_pool_record(ua->db, &pr) != 1) {
260       bsendmsg(ua, db_strerror(ua->db));
261       return 1;
262    }
263    bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
264
265    return 1;
266 }
267
268 /*
269  * Turn auto mount on/off  
270  * 
271  *  automount on 
272  *  automount off
273  */
274 int automountcmd(UAContext *ua, char *cmd)
275 {
276    char *onoff;
277
278    if (ua->argc != 2) {
279       if (!get_cmd(ua, _("Turn on or off? "))) {
280             return 1;
281       }
282       onoff = ua->cmd;
283    } else {
284       onoff = ua->argk[1];
285    }
286
287    ua->automount = (strcasecmp(onoff, _("off")) == 0) ? 0 : 1;
288    return 1; 
289 }
290
291
292 /*
293  * Cancel a job
294  */
295 static int cancelcmd(UAContext *ua, char *cmd)
296 {
297    int i;
298    int njobs = 0;
299    BSOCK *sd, *fd;
300    JCR *jcr = NULL;
301    char JobName[MAX_NAME_LENGTH];
302
303    if (!open_db(ua)) {
304       return 1;
305    }
306
307    for (i=1; i<ua->argc; i++) {
308       if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
309          if (!ua->argv[i]) {
310             break;
311          }
312          if (!(jcr=get_jcr_by_id(atoi(ua->argv[i])))) {
313             bsendmsg(ua, _("JobId %d is not running.\n"), atoi(ua->argv[i]));
314             return 1;
315          }
316          break;
317       } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
318          if (!ua->argv[i]) {
319             break;
320          }
321          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
322             bsendmsg(ua, _("Job %s is not running.\n"), ua->argv[i]);
323             return 1;
324          }
325          break;
326       }
327    }
328    /* If we still do not have a jcr,
329     *   throw up a list and ask the user to select one.
330     */
331    if (!jcr) {
332       /* Count Jobs running */
333       lock_jcr_chain();
334       for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) {
335          if (jcr->JobId == 0) {      /* this is us */
336             free_locked_jcr(jcr);
337             njobs--;
338             continue;
339          }
340          free_locked_jcr(jcr);
341       }
342       unlock_jcr_chain();
343       if (njobs == 0) {
344          bsendmsg(ua, _("No Jobs running.\n"));
345          return 1;
346       }
347       start_prompt(ua, _("Select Job:\n"));
348       lock_jcr_chain();
349       for (jcr=NULL; (jcr=get_next_jcr(jcr)); ) {
350          if (jcr->JobId == 0) {      /* this is us */
351             free_locked_jcr(jcr);
352             continue;
353          }
354          add_prompt(ua, jcr->Job);
355          free_locked_jcr(jcr);
356       }
357       unlock_jcr_chain();
358       if (do_prompt(ua, _("Choose Job to cancel"), JobName) < 0) {
359          return 1;
360       }
361       if (njobs == 1) {
362          if (!get_cmd(ua, _("Confirm cancel (yes/no): "))) {
363             return 1;
364          }
365          if (strcasecmp(ua->cmd, _("yes")) != 0) {
366             return 1;
367          }
368       }
369       jcr = get_jcr_by_full_name(JobName);
370       if (!jcr) {
371          bsendmsg(ua, _("Job %s not found.\n"), JobName);
372          return 1;
373       }
374    }
375      
376    switch (jcr->JobStatus) {
377    case JS_Created:
378       jcr->JobStatus = JS_Cancelled;
379       bsendmsg(ua, _("JobId %d, Job %s marked to be cancelled.\n"),
380               jcr->JobId, jcr->Job);
381       free_jcr(jcr);
382       return 1;
383          
384    case JS_Running:
385    case JS_WaitSD:
386    case JS_WaitFD:
387    case JS_WaitMedia:
388    case JS_WaitMount:
389    case JS_Blocked:
390
391       jcr->JobStatus = JS_Cancelled;
392       /* Cancel File daemon */
393       ua->jcr->client = jcr->client;
394       if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
395          bsendmsg(ua, _("Failed to connect to File daemon.\n"));
396          free_jcr(jcr);
397          return 1;
398       }
399       Dmsg0(200, "Connected to file daemon\n");
400       fd = ua->jcr->file_bsock;
401       bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
402       while (bnet_recv(fd) > 0) {
403          bsendmsg(ua, "%s", fd->msg);
404       }
405       bnet_sig(fd, BNET_TERMINATE);
406       bnet_close(fd);
407       ua->jcr->file_bsock = NULL;
408
409       /* Cancel Storage daemon */
410       ua->jcr->store = jcr->store;
411       if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
412          bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
413          free_jcr(jcr);
414          return 1;
415       }
416       Dmsg0(200, "Connected to storage daemon\n");
417       sd = ua->jcr->store_bsock;
418       bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
419       while (bnet_recv(sd) > 0) {
420          bsendmsg(ua, "%s", sd->msg);
421       }
422       bnet_sig(sd, BNET_TERMINATE);
423       bnet_close(sd);
424       ua->jcr->store_bsock = NULL;
425
426    default:
427       bsendmsg(ua, _("JobId %d Job %s Status=%c cannot be cancelled.\n"), 
428           jcr->JobId, jcr->Job, jcr->JobStatus);
429    }
430    free_jcr(jcr);
431
432    return 1; 
433 }
434
435 /*
436  * Create a pool record from a given Pool resource
437  *   Also called from backup.c
438  * Returns: -1  on error
439  *           0  record already exists
440  *           1  record created
441  */
442
443 int create_pool(B_DB *db, POOL *pool)
444 {
445    POOL_DBR  pr;
446
447    memset(&pr, 0, sizeof(POOL_DBR));
448
449    strcpy(pr.Name, pool->hdr.name);
450
451    if (db_get_pool_record(db, &pr)) {
452       return 0;                       /* exists */
453    }
454
455    strcpy(pr.PoolType, pool->pool_type);
456    pr.MaxVols = pool->max_volumes;
457    pr.NumVols = 0;
458    pr.UseOnce = pool->use_volume_once;
459    pr.UseCatalog = pool->use_catalog;
460    pr.AcceptAnyVolume = pool->accept_any_volume;
461    if (pool->label_format) {
462       strcpy(pr.LabelFormat, pool->label_format);
463    } else {
464       strcpy(pr.LabelFormat, "*");    /* none */
465    }
466
467    if (!db_create_pool_record(db, &pr)) {
468       return -1;                      /* error */
469    }
470    return 1;
471 }
472
473
474
475 /*
476  * Create a Pool Record in the database.
477  *  It is always created from the Resource record.
478  */
479 static int createcmd(UAContext *ua, char *cmd) 
480 {
481    POOL *pool;
482
483    if (!open_db(ua)) {
484       return 1;
485    }
486
487    pool = get_pool_resource(ua);
488    if (!pool) {
489       return 1;
490    }
491
492    switch (create_pool(ua->db, pool)) {
493    case 0:
494       bsendmsg(ua, _("Error: Pool %s already exists.\n\
495 Use update to change it.\n"), pool->hdr.name);
496       break;
497
498    case -1:
499       bsendmsg(ua, db_strerror(ua->db));
500       break;
501
502    default:
503      break;
504    }
505    return 1;
506 }
507
508
509
510
511 /*
512  * Update a Pool Record in the database.
513  *  It is always updated from the Resource record.
514  *
515  *    update pool=<pool-name>
516  *         updates pool from Pool resource
517  *    update media pool=<pool-name> volume=<volume-name>
518  *         changes pool info for volume
519  */
520 static int updatecmd(UAContext *ua, char *cmd) 
521 {
522    static char *kw[] = {
523       N_("media"),
524       N_("volume"),
525       N_("pool"),
526       NULL};
527
528    if (!open_db(ua)) {
529       return 1;
530    }
531
532    switch (find_arg_keyword(ua, kw)) {
533       case 0:
534       case 1:
535          update_media(ua);
536          return 1;
537       case 2:
538          update_pool(ua);
539          return 1;
540       default:
541          break;
542    }
543     
544    start_prompt(ua, _("Update choice:\n"));
545    add_prompt(ua, _("pool"));
546    add_prompt(ua, _("media"));
547    switch (do_prompt(ua, _("Choose catalog item to update"), NULL)) {
548       case 0:
549          update_pool(ua);
550          break;
551       case 1:
552          update_media(ua);
553          break;
554       default:
555          break;
556    }
557    return 1;
558 }
559
560 /*
561  * Update a media record -- allows you to change the
562  *  Volume status. E.g. if you want Bacula to stop
563  *  writing on the volume, set it to anything other
564  *  than Append.
565  */              
566 static int update_media(UAContext *ua)
567 {
568    POOL_DBR pr;
569    MEDIA_DBR mr;
570    int i;
571    static char *kw[] = {
572       "volume",
573       NULL};
574
575    memset(&pr, 0, sizeof(pr));
576    memset(&mr, 0, sizeof(mr));
577    if (!get_pool_dbr(ua, &pr)) {
578       return 1;
579    }
580    mr.PoolId = pr.PoolId;
581    mr.VolumeName[0] = 0;
582
583    i = find_arg_keyword(ua, kw);
584    if (i == 0 && ua->argv[i]) {
585       strcpy(mr.VolumeName, ua->argv[i]);
586    }
587    if (mr.VolumeName[0] == 0) {
588       db_list_media_records(ua->db, &mr, prtit, ua);
589       if (!get_cmd(ua, _("Enter Volume name to update: "))) {
590          return 1;
591       }
592       strcpy(mr.VolumeName, ua->cmd);
593    }
594    mr.MediaId = 0;
595    if (!db_get_media_record(ua->db, &mr)) {
596       bsendmsg(ua, _("Media record for %s not found.\n"), mr.VolumeName);
597       return 1;
598    }
599    start_prompt(ua, _("Volume Status Values:\n"));
600    add_prompt(ua, "Append");
601    add_prompt(ua, "Archive");
602    add_prompt(ua, "Disabled");
603    add_prompt(ua, "Full");
604    add_prompt(ua, "Recycle");
605    add_prompt(ua, "Read-Only");
606    if (do_prompt(ua, _("Choose new Volume Status"), ua->cmd) < 0) {
607       return 1;
608    }
609    strcpy(mr.VolStatus, ua->cmd);
610    db_update_media_record(ua->db, &mr);
611    return 1;
612 }
613
614 /* 
615  * Update pool record -- pull info from current POOL resource
616  */
617 static int update_pool(UAContext *ua)
618 {
619    POOL_DBR  pr;
620    int id;
621    POOL *pool;
622    
623    memset(&pr, 0, sizeof(pr));
624    if (!get_pool_dbr(ua, &pr)) {
625       return 1;
626    }
627
628    strcpy(pr.PoolType, pool->pool_type);
629    if (pr.MaxVols != (uint32_t) (pool->max_volumes)) {
630       pr.MaxVols = pool->max_volumes;
631    }
632    if (pr.MaxVols != 0 && pr.MaxVols < pr.NumVols) {
633       pr.MaxVols = pr.NumVols;
634    }
635    pr.UseOnce = pool->use_volume_once;
636    pr.UseCatalog = pool->use_catalog;
637    pr.AcceptAnyVolume = pool->accept_any_volume;
638    if (pool->label_format) {
639       strcpy(pr.LabelFormat, pool->label_format);
640    } else {
641       strcpy(pr.LabelFormat, "*");    /* none */
642    }
643    id = db_update_pool_record(ua->db, &pr);
644    if (id <= 0) {
645       bsendmsg(ua, "Error: db_update_pool_record returned %d\n", id);
646    }
647    return 1;
648 }
649
650
651 static void do_storage_setdebug(UAContext *ua, STORE *store, int level)
652 {
653    BSOCK *sd;
654
655    ua->jcr->store = store;
656    /* Try connecting for up to 15 seconds */
657    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"), 
658       store->hdr.name, store->address, store->SDport);
659    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
660       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
661       return;
662    }
663    Dmsg0(20, _("Connected to storage daemon\n"));
664    sd = ua->jcr->store_bsock;
665    bnet_fsend(sd, "setdebug=%d\n", level);
666    if (bnet_recv(sd) > 0) {
667       bsendmsg(ua, "%s", sd->msg);
668    }
669    bnet_sig(sd, BNET_TERMINATE);
670    bnet_close(sd);
671    ua->jcr->store_bsock = NULL;
672    return;  
673 }
674    
675 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level)
676 {
677    BSOCK *fd;
678
679    /* Connect to File daemon */
680
681    ua->jcr->client = client;
682    /* Try to connect for 15 seconds */
683    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"), 
684       client->hdr.name, client->address, client->FDport);
685    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
686       bsendmsg(ua, _("Failed to connect to Client.\n"));
687       return;
688    }
689    Dmsg0(20, "Connected to file daemon\n");
690    fd = ua->jcr->file_bsock;
691    bnet_fsend(fd, "setdebug=%d\n", level);
692    if (bnet_recv(fd) > 0) {
693       bsendmsg(ua, "%s", fd->msg);
694    }
695    bnet_sig(fd, BNET_TERMINATE);
696    bnet_close(fd);
697    ua->jcr->file_bsock = NULL;
698
699    return;  
700 }
701
702
703 static void do_all_setdebug(UAContext *ua, int level)
704 {
705    STORE *store, **unique_store;
706    CLIENT *client, **unique_client;
707    int i, j, found;
708
709    /* Director */
710    debug_level = level;
711
712    /* Count Storage items */
713    LockRes();
714    store = NULL;
715    for (i=0; (store = (STORE *)GetNextRes(R_STORAGE, (RES *)store)); i++)
716       { }
717    unique_store = (STORE **) malloc(i * sizeof(STORE));
718    /* Find Unique Storage address/port */         
719    store = (STORE *)GetNextRes(R_STORAGE, NULL);
720    i = 0;
721    unique_store[i++] = store;
722    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
723       found = 0;
724       for (j=0; j<i; j++) {
725          if (strcmp(unique_store[j]->address, store->address) == 0 &&
726              unique_store[j]->SDport == store->SDport) {
727             found = 1;
728             break;
729          }
730       }
731       if (!found) {
732          unique_store[i++] = store;
733          Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
734       }
735    }
736    UnlockRes();
737
738    /* Call each unique Storage daemon */
739    for (j=0; j<i; j++) {
740       do_storage_setdebug(ua, unique_store[j], level);
741    }
742    free(unique_store);
743
744    /* Count Client items */
745    LockRes();
746    client = NULL;
747    for (i=0; (client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client)); i++)
748       { }
749    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
750    /* Find Unique Client address/port */         
751    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
752    i = 0;
753    unique_client[i++] = client;
754    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
755       found = 0;
756       for (j=0; j<i; j++) {
757          if (strcmp(unique_client[j]->address, client->address) == 0 &&
758              unique_client[j]->FDport == client->FDport) {
759             found = 1;
760             break;
761          }
762       }
763       if (!found) {
764          unique_client[i++] = client;
765          Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
766       }
767    }
768    UnlockRes();
769
770    /* Call each unique File daemon */
771    for (j=0; j<i; j++) {
772       do_client_setdebug(ua, unique_client[j], level);
773    }
774    free(unique_client);
775 }
776
777 /*
778  * setdebug level=nn all
779  */
780 static int setdebugcmd(UAContext *ua, char *cmd)
781 {
782    STORE *store;
783    CLIENT *client;
784    int level;
785    int i;
786
787    if (!open_db(ua)) {
788       return 1;
789    }
790    Dmsg1(20, "setdebug:%s:\n", cmd);
791
792    level = -1;
793    for (i=1; i<ua->argc; i++) {
794       if (strcasecmp(ua->argk[i], _("level")) == 0 && ua->argv[i]) {
795          level = atoi(ua->argv[i]);
796          break;
797       }
798    }
799    if (level < 0) {
800       if (!get_cmd(ua, _("Enter new debug level: "))) {
801          return 1;
802       }
803       level = atoi(ua->cmd);
804    }
805    if (level < 0) {
806       bsendmsg(ua, _("level cannot be negative.\n"));
807       return 1;
808    }
809
810    /* General debug? */
811    for (i=1; i<ua->argc; i++) {
812       if (strcasecmp(ua->argk[i], _("all")) == 0) {
813          do_all_setdebug(ua, level);
814          return 1;
815       }
816       if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
817           strcasecmp(ua->argk[i], _("director")) == 0) {
818          debug_level = level;
819          return 1;
820       }
821       if (strcasecmp(ua->argk[i], _("client")) == 0) {
822          client = NULL;
823          if (ua->argv[i]) {
824             client = (CLIENT *) GetResWithName(R_CLIENT, ua->argv[i]);
825             if (client) {
826                do_client_setdebug(ua, client, level);
827                return 1;
828             }
829          }
830          client = select_client_resource(ua);   
831          if (client) {
832             do_client_setdebug(ua, client, level);
833             return 1;
834          }
835
836          store = get_storage_resource(ua, cmd);
837          if (store) {
838             do_storage_setdebug(ua, store, level);
839             return 1;
840          }
841       }
842    } 
843    /*
844     * We didn't find an appropriate keyword above, so
845     * prompt the user.
846     */
847    start_prompt(ua, _("Available daemons are: \n"));
848    add_prompt(ua, _("Director"));
849    add_prompt(ua, _("Storage"));
850    add_prompt(ua, _("Client"));
851    add_prompt(ua, _("All"));
852    switch(do_prompt(ua, _("Select daemon type to set debug level"), NULL)) {
853       case 0:                         /* Director */
854          debug_level = level;
855          break;
856       case 1:
857          store = get_storage_resource(ua, cmd);
858          if (store) {
859             do_storage_setdebug(ua, store, level);
860          }
861          break;
862       case 2:
863          client = select_client_resource(ua);
864          if (client) {
865             do_client_setdebug(ua, client, level);
866          }
867          break;
868       case 3:
869          do_all_setdebug(ua, level);
870          break;
871       default:
872          break;
873    }
874    return 1;
875 }
876
877
878
879 /*
880  * Delete Pool records (should purge Media with it).
881  *
882  *  delete pool=<pool-name>
883  *  delete media pool=<pool-name> volume=<name>
884  */
885 static int deletecmd(UAContext *ua, char *cmd)
886 {
887    static char *keywords[] = {
888       N_("media"),
889       N_("pool"),
890       NULL};
891
892    if (!open_db(ua)) {
893       return 1;
894    }
895
896    bsendmsg(ua, _(
897 "In general it is not a good idea to delete either a\n"
898 "Pool or Media since in both cases, you may delete Media\n"
899 "that contain data.\n\n"));
900      
901    switch (find_arg_keyword(ua, keywords)) {
902       case 0:
903          delete_media(ua);     
904          return 1;
905       case 1:
906          delete_pool(ua);
907          return 1;
908       default:
909          break;
910    }
911    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
912       case 0:
913          delete_media(ua);
914          break;
915       case 1:
916          delete_pool(ua);
917          break;
918       default:
919          bsendmsg(ua, _("Nothing done.\n"));
920          break;
921    }
922    return 1;
923 }
924
925 /*
926  * Delete media records from database -- dangerous 
927  */
928 static int delete_media(UAContext *ua)
929 {
930    POOL_DBR pr;
931    MEDIA_DBR mr;
932
933    if (!select_pool_and_media_dbr(ua, &pr, &mr)) {
934       return 1;
935    }
936    bsendmsg(ua, _("\nThis command will delete volume %s\n"
937       "and all Jobs saved on that volume from the Catalog\n"),
938       mr.VolumeName);
939
940    if (!get_cmd(ua, _("If you want to continue enter pretty please: "))) {
941       return 1;
942    }
943    if (strcmp(ua->cmd, _("pretty please")) == 0) {
944       db_delete_media_record(ua->db, &mr);
945    }
946    return 1;
947 }
948
949 /*
950  * Delete a pool record from the database -- dangerous   
951  */
952 static int delete_pool(UAContext *ua)
953 {
954    POOL_DBR  pr;
955    
956    memset(&pr, 0, sizeof(pr));
957
958    if (!get_pool_dbr(ua, &pr)) {
959       return 1;
960    }
961    if (!get_cmd(ua, _("If you want to continue enter pretty please: "))) {
962       return 1;
963    }
964    if (strcmp(ua->cmd, _("pretty please")) == 0) {
965       db_delete_pool_record(ua->db, &pr);
966    }
967    return 1;
968 }
969
970
971 /*
972  * Label a tape 
973  *  
974  *   label storage=xxx volume=vvv
975  */
976 static int labelcmd(UAContext *ua, char *cmd)
977 {
978    STORE *store;
979    BSOCK *sd;
980    char dev_name[MAX_NAME_LENGTH];
981    MEDIA_DBR mr;
982    POOL_DBR pr;
983    int ok = FALSE;
984    int mounted = FALSE;
985    int i;
986    static char *keyword[] = {
987       "volume",
988       NULL};
989
990    if (!open_db(ua)) {
991       return 1;
992    }
993    store = get_storage_resource(ua, cmd);
994    if (!store) {
995       return 1;
996    }
997
998    i = find_arg_keyword(ua, keyword);
999    if (i >=0 && ua->argv[i]) {
1000       strcpy(ua->cmd, ua->argv[i]);
1001       goto gotVol;
1002    }
1003
1004 getVol:
1005    if (!get_cmd(ua, _("Enter new Volume name: "))) {
1006       return 1;
1007    }
1008 gotVol:
1009    if (strchr(ua->cmd, '|')) {
1010       bsendmsg(ua, _("Illegal character | in a volume name.\n"));
1011       goto getVol;
1012    }
1013    if (strlen(ua->cmd) >= MAX_NAME_LENGTH) {
1014       bsendmsg(ua, _("Volume name too long.\n"));
1015       goto getVol;
1016    }
1017
1018    memset(&mr, 0, sizeof(mr));
1019    strcpy(mr.VolumeName, ua->cmd);
1020    if (db_get_media_record(ua->db, &mr)) {
1021        bsendmsg(ua, _("Media record for Volume %s already exists.\n"), 
1022           mr.VolumeName);
1023        return 1;
1024    }
1025    strcpy(mr.MediaType, store->media_type);
1026
1027    memset(&pr, 0, sizeof(pr));
1028    if (!select_pool_dbr(ua, &pr)) {
1029       return 1;
1030    }
1031    mr.PoolId = pr.PoolId;
1032    strcpy(mr.VolStatus, "Append");
1033    mr.Recycle = pr.Recycle;
1034    mr.VolRetention = pr.VolumeRetention;
1035
1036    ua->jcr->store = store;
1037    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"), 
1038       store->hdr.name, store->address, store->SDport);
1039    if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
1040       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1041       return 1;   
1042    }
1043    sd = ua->jcr->store_bsock;
1044    strcpy(dev_name, store->dev_name);
1045    bash_spaces(dev_name);
1046    bash_spaces(mr.VolumeName);
1047    bash_spaces(mr.MediaType);
1048    bash_spaces(pr.Name);
1049    bnet_fsend(sd, _("label %s VolumeName=%s PoolName=%s MediaType=%s"), 
1050       dev_name, mr.VolumeName, pr.Name, mr.MediaType);
1051    bsendmsg(ua, "Sending label command ...\n");
1052    while (bget_msg(sd, 0) > 0) {
1053       bsendmsg(ua, "%s", sd->msg);
1054       if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
1055          ok = TRUE;
1056       }
1057    }
1058    ua->jcr->store_bsock = NULL;
1059    unbash_spaces(dev_name);
1060    unbash_spaces(mr.VolumeName);
1061    unbash_spaces(mr.MediaType);
1062    unbash_spaces(pr.Name);
1063    if (ok) {
1064       if (db_create_media_record(ua->db, &mr)) {
1065          bsendmsg(ua, _("Media record for Volume=%s successfully created.\n"),
1066             mr.VolumeName);
1067          if (ua->automount) {
1068             bsendmsg(ua, _("Requesting mount %s ...\n"), dev_name);
1069             bash_spaces(dev_name);
1070             bnet_fsend(sd, "mount %s", dev_name);
1071             unbash_spaces(dev_name);
1072             while (bnet_recv(sd) > 0) {
1073                bsendmsg(ua, "%s", sd->msg);
1074                /* Here we can get
1075                 *  3001 OK mount. Device=xxx      or
1076                 *  3001 Mounted Volume vvvv
1077                 */
1078                if (strncmp(sd->msg, "3001 ", 5) == 0) {
1079                   mounted = TRUE;
1080                   /***** ****FIXME***** find job waiting for  
1081                    ***** mount, and change to waiting for SD  
1082                    */
1083                }
1084             }
1085          }
1086       } else {
1087          bsendmsg(ua, "%s", db_strerror(ua->db));
1088       }
1089    }
1090    if (!mounted) {
1091       bsendmsg(ua, _("Do not forget to mount the drive!!!\n"));
1092    }
1093    bnet_sig(sd, BNET_TERMINATE);
1094    bnet_close(sd);
1095    return 1;
1096 }
1097
1098 static void do_mount_cmd(int mount, UAContext *ua, char *cmd)
1099 {
1100    STORE *store;
1101    BSOCK *sd;
1102    char dev_name[MAX_NAME_LENGTH];
1103
1104
1105    if (!open_db(ua)) {
1106       return;
1107    }
1108    Dmsg1(20, "mount: %s\n", ua->UA_sock->msg);
1109
1110    store = get_storage_resource(ua, cmd);
1111    if (!store) {
1112       return;
1113    }
1114
1115    Dmsg2(20, "Found storage, MediaType=%s DevName=%s\n",
1116       store->media_type, store->dev_name);
1117
1118    ua->jcr->store = store;
1119    if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
1120       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1121       return;
1122    }
1123    sd = ua->jcr->store_bsock;
1124    strcpy(dev_name, store->dev_name);
1125    bash_spaces(dev_name);
1126    if (mount) {
1127       bnet_fsend(sd, "mount %s", dev_name);
1128    } else {
1129       bnet_fsend(sd, "unmount %s", dev_name);
1130    }
1131    while (bnet_recv(sd) > 0) {
1132       bsendmsg(ua, "%s", sd->msg);
1133       if (strncmp(sd->msg, "3001 OK mount.", 14) == 0) {
1134           /***** ****FIXME**** fix JobStatus */
1135       }
1136    }
1137    bnet_sig(sd, BNET_TERMINATE);
1138    bnet_close(sd);
1139    ua->jcr->store_bsock = NULL;
1140 }
1141
1142 /*
1143  * mount [storage | device] <name>
1144  */
1145 static int mountcmd(UAContext *ua, char *cmd)
1146 {
1147    do_mount_cmd(1, ua, cmd);          /* mount */
1148    return 1;
1149 }
1150
1151
1152 /*
1153  * unmount [storage | device] <name>
1154  */
1155 static int unmountcmd(UAContext *ua, char *cmd)
1156 {
1157    do_mount_cmd(0, ua, cmd);          /* unmount */
1158    return 1;
1159 }
1160
1161
1162 /*
1163  * Switch databases
1164  *   use catalog=<name>
1165  */
1166 static int usecmd(UAContext *ua, char *cmd)
1167 {
1168    CAT *oldcatalog, *catalog;
1169
1170
1171    close_db(ua);                      /* close any previously open db */
1172    oldcatalog = ua->catalog;
1173
1174    if (!(catalog = get_catalog_resource(ua))) {
1175       ua->catalog = oldcatalog;
1176    } else {
1177       ua->catalog = catalog;
1178    }
1179    if (open_db(ua)) {
1180       bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1181          ua->catalog->hdr.name, ua->catalog->db_name);
1182    }
1183    return 1;
1184 }
1185
1186 int quitcmd(UAContext *ua, char *cmd) 
1187 {
1188    ua->quit = TRUE;
1189    return 1;
1190 }
1191
1192 static int helpcmd(UAContext *ua, char *cmd)
1193 {
1194    unsigned int i;
1195
1196 /* usage(); */
1197    bnet_fsend(ua->UA_sock, _("  Command    Description\n  =======    ===========\n"));
1198    for (i=0; i<comsize; i++) {
1199       bnet_fsend(ua->UA_sock, _("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1200    }
1201    bnet_fsend(ua->UA_sock, "\n");
1202    return 1;
1203 }
1204
1205 static int versioncmd(UAContext *ua, char *cmd)
1206 {
1207    bsendmsg(ua, "%s Version: " VERSION " (" DATE ")\n", my_name);
1208    return 1;
1209 }
1210
1211
1212 /* A bit brain damaged in that if the user has not done
1213  * a "use catalog xxx" command, we simply find the first
1214  * catalog resource and open it.
1215  */
1216 int open_db(UAContext *ua)
1217 {
1218    if (ua->db) {
1219       return 1;
1220    }
1221    if (!ua->catalog) {
1222       LockRes();
1223       ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1224       UnlockRes();
1225       if (!ua->catalog) {    
1226          bnet_fsend(ua->UA_sock, _("Could not find a Catalog resource\n"));
1227          return 0;
1228       } else {
1229          bnet_fsend(ua->UA_sock, _("Using default Catalog name=%s DB=%s\n"), 
1230             ua->catalog->hdr.name, ua->catalog->db_name);
1231       }
1232    }
1233
1234    Dmsg0(50, "Open database\n");
1235    ua->db = db_init_database(ua->catalog->db_name, ua->catalog->db_user,
1236                              ua->catalog->db_password);
1237    if (!db_open_database(ua->db)) {
1238       bnet_fsend(ua->UA_sock, _("Could not open DB %s: ERR=%s"), 
1239          ua->catalog->db_name, db_strerror(ua->db));
1240       close_db(ua);
1241       return 0;
1242    }
1243    ua->jcr->db = ua->db;
1244    Dmsg1(50, "DB %s opened\n", ua->catalog->db_name);
1245    return 1;
1246 }
1247
1248 void close_db(UAContext *ua)
1249 {
1250    if (ua->db) {
1251       db_close_database(ua->db);
1252    }
1253    ua->db = NULL;
1254    ua->jcr->db = NULL;
1255 }