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