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