]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_update.c
Tweak restore path prompt
[bacula/bacula] / bacula / src / dird / ua_update.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- Update command processing
31  *     Split from ua_cmds.c March 2005
32  *
33  *     Kern Sibbald, September MM
34  *
35  */
36
37 #include "bacula.h"
38 #include "dird.h"
39
40 /* Forward referenced functions */
41 static int update_volume(UAContext *ua);
42 static bool update_pool(UAContext *ua);
43 static bool update_job(UAContext *ua);
44 static bool update_stats(UAContext *ua);
45
46 /*
47  * Update a Pool Record in the database.
48  *  It is always updated from the Resource record.
49  *
50  *    update pool=<pool-name>
51  *         updates pool from Pool resource
52  *    update media pool=<pool-name> volume=<volume-name>
53  *         changes pool info for volume
54  *    update slots [scan=...]
55  *         updates autochanger slots
56  *    update stats [days=...]
57  *         updates long term statistics
58  */
59 int update_cmd(UAContext *ua, const char *cmd)
60 {
61    static const char *kw[] = {
62       NT_("media"),  /* 0 */
63       NT_("volume"), /* 1 */
64       NT_("pool"),   /* 2 */
65       NT_("slots"),  /* 3 */
66       NT_("slot"),   /* 4 */
67       NT_("jobid"),  /* 5 */
68       NT_("stats"),  /* 6 */
69       NULL};
70
71    if (!open_client_db(ua)) {
72       return 1;
73    }
74
75    switch (find_arg_keyword(ua, kw)) {
76    case 0:
77    case 1:
78       update_volume(ua);
79       return 1;
80    case 2:
81       update_pool(ua);
82       return 1;
83    case 3:
84    case 4:
85       update_slots(ua);
86       return 1;
87    case 5:
88       update_job(ua);
89       return 1;
90    case 6:
91       update_stats(ua);
92       return 1;
93    default:
94       break;
95    }
96
97    start_prompt(ua, _("Update choice:\n"));
98    add_prompt(ua, _("Volume parameters"));
99    add_prompt(ua, _("Pool from resource"));
100    add_prompt(ua, _("Slots from autochanger"));
101    add_prompt(ua, _("Long term statistics"));
102    switch (do_prompt(ua, _("item"), _("Choose catalog item to update"), NULL, 0)) {
103    case 0:
104       update_volume(ua);
105       break;
106    case 1:
107       update_pool(ua);
108       break;
109    case 2:
110       update_slots(ua);
111       break;
112    case 3:
113       update_stats(ua);
114       break;
115    default:
116       break;
117    }
118    return 1;
119 }
120
121 static void update_volstatus(UAContext *ua, const char *val, MEDIA_DBR *mr)
122 {
123    POOL_MEM query(PM_MESSAGE);
124    const char *kw[] = {
125       NT_("Append"),
126       NT_("Archive"),
127       NT_("Disabled"),
128       NT_("Full"),
129       NT_("Used"),
130       NT_("Cleaning"),
131       NT_("Recycle"),
132       NT_("Read-Only"),
133       NT_("Error"),
134       NULL};
135    bool found = false;
136    int i;
137
138    for (i=0; kw[i]; i++) {
139       if (strcasecmp(val, kw[i]) == 0) {
140          found = true;
141          break;
142       }
143    }
144    if (!found) {
145       ua->error_msg(_("Invalid VolStatus specified: %s\n"), val);
146    } else {
147       char ed1[50];
148       bstrncpy(mr->VolStatus, kw[i], sizeof(mr->VolStatus));
149       Mmsg(query, "UPDATE Media SET VolStatus='%s' WHERE MediaId=%s",
150          mr->VolStatus, edit_int64(mr->MediaId,ed1));
151       if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
152          ua->error_msg("%s", db_strerror(ua->db));
153       } else {
154          ua->info_msg(_("New Volume status is: %s\n"), mr->VolStatus);
155       }
156    }
157 }
158
159 static void update_volretention(UAContext *ua, char *val, MEDIA_DBR *mr)
160 {
161    char ed1[150], ed2[50];
162    POOL_MEM query(PM_MESSAGE);
163    if (!duration_to_utime(val, &mr->VolRetention)) {
164       ua->error_msg(_("Invalid retention period specified: %s\n"), val);
165       return;
166    }
167    Mmsg(query, "UPDATE Media SET VolRetention=%s WHERE MediaId=%s",
168       edit_uint64(mr->VolRetention, ed1), edit_int64(mr->MediaId,ed2));
169    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
170       ua->error_msg("%s", db_strerror(ua->db));
171    } else {
172       ua->info_msg(_("New retention period is: %s\n"),
173          edit_utime(mr->VolRetention, ed1, sizeof(ed1)));
174    }
175 }
176
177 static void update_voluseduration(UAContext *ua, char *val, MEDIA_DBR *mr)
178 {
179    char ed1[150], ed2[50];
180    POOL_MEM query(PM_MESSAGE);
181
182    if (!duration_to_utime(val, &mr->VolUseDuration)) {
183       ua->error_msg(_("Invalid use duration specified: %s\n"), val);
184       return;
185    }
186    Mmsg(query, "UPDATE Media SET VolUseDuration=%s WHERE MediaId=%s",
187       edit_uint64(mr->VolUseDuration, ed1), edit_int64(mr->MediaId,ed2));
188    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
189       ua->error_msg("%s", db_strerror(ua->db));
190    } else {
191       ua->info_msg(_("New use duration is: %s\n"),
192          edit_utime(mr->VolUseDuration, ed1, sizeof(ed1)));
193    }
194 }
195
196 static void update_volmaxjobs(UAContext *ua, char *val, MEDIA_DBR *mr)
197 {
198    POOL_MEM query(PM_MESSAGE);
199    char ed1[50];
200    Mmsg(query, "UPDATE Media SET MaxVolJobs=%s WHERE MediaId=%s",
201       val, edit_int64(mr->MediaId,ed1));
202    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
203       ua->error_msg("%s", db_strerror(ua->db));
204    } else {
205       ua->info_msg(_("New max jobs is: %s\n"), val);
206    }
207 }
208
209 static void update_volmaxfiles(UAContext *ua, char *val, MEDIA_DBR *mr)
210 {
211    POOL_MEM query(PM_MESSAGE);
212    char ed1[50];
213    Mmsg(query, "UPDATE Media SET MaxVolFiles=%s WHERE MediaId=%s",
214       val, edit_int64(mr->MediaId, ed1));
215    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
216       ua->error_msg("%s", db_strerror(ua->db));
217    } else {
218       ua->info_msg(_("New max files is: %s\n"), val);
219    }
220 }
221
222 static void update_volmaxbytes(UAContext *ua, char *val, MEDIA_DBR *mr)
223 {
224    uint64_t maxbytes;
225    char ed1[50], ed2[50];
226    POOL_MEM query(PM_MESSAGE);
227
228    if (!size_to_uint64(val, strlen(val), &maxbytes)) {
229       ua->error_msg(_("Invalid max. bytes specification: %s\n"), val);
230       return;
231    }
232    Mmsg(query, "UPDATE Media SET MaxVolBytes=%s WHERE MediaId=%s",
233       edit_uint64(maxbytes, ed1), edit_int64(mr->MediaId, ed2));
234    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
235       ua->error_msg("%s", db_strerror(ua->db));
236    } else {
237       ua->info_msg(_("New Max bytes is: %s\n"), edit_uint64(maxbytes, ed1));
238    }
239 }
240
241 static void update_volrecycle(UAContext *ua, char *val, MEDIA_DBR *mr)
242 {
243    int recycle;
244    char ed1[50];
245
246    POOL_MEM query(PM_MESSAGE);
247    if (!is_yesno(val, &recycle)) {
248       ua->error_msg(_("Invalid value. It must be yes or no.\n"));
249       return;
250    }
251    Mmsg(query, "UPDATE Media SET Recycle=%d WHERE MediaId=%s",
252       recycle, edit_int64(mr->MediaId, ed1));
253    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
254       ua->error_msg("%s", db_strerror(ua->db));
255    } else {
256       ua->info_msg(_("New Recycle flag is: %s\n"),
257          recycle==1?_("yes"):_("no"));
258    }
259 }
260
261 static void update_volinchanger(UAContext *ua, char *val, MEDIA_DBR *mr)
262 {
263    int InChanger;
264    char ed1[50];
265
266    POOL_MEM query(PM_MESSAGE);
267    if (!is_yesno(val, &InChanger)) {
268       ua->error_msg(_("Invalid value. It must be yes or no.\n"));
269       return;
270    }
271    Mmsg(query, "UPDATE Media SET InChanger=%d WHERE MediaId=%s",
272       InChanger, edit_int64(mr->MediaId, ed1));
273    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
274       ua->error_msg("%s", db_strerror(ua->db));
275    } else {
276       ua->info_msg(_("New InChanger flag is: %s\n"),
277          InChanger==1?_("yes"):_("no"));
278    }
279 }
280
281
282 static void update_volslot(UAContext *ua, char *val, MEDIA_DBR *mr)
283 {
284    POOL_DBR pr;
285
286    memset(&pr, 0, sizeof(POOL_DBR));
287    pr.PoolId = mr->PoolId;
288    if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
289       ua->error_msg("%s", db_strerror(ua->db));
290       return;
291    }
292    mr->Slot = atoi(val);
293    if (pr.MaxVols > 0 && mr->Slot > (int)pr.MaxVols) {
294       ua->error_msg(_("Invalid slot, it must be between 0 and MaxVols=%d\n"),
295          pr.MaxVols);
296       return;
297    }
298    /*
299     * Make sure to use db_update... rather than doing this directly,
300     *   so that any Slot is handled correctly.
301     */
302    set_storageid_in_mr(NULL, mr);
303    if (!db_update_media_record(ua->jcr, ua->db, mr)) {
304       ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
305    } else {
306       ua->info_msg(_("New Slot is: %d\n"), mr->Slot);
307    }
308 }
309
310 /* Modify the Pool in which this Volume is located */
311 void update_vol_pool(UAContext *ua, char *val, MEDIA_DBR *mr, POOL_DBR *opr)
312 {
313    POOL_DBR pr;
314    POOL_MEM query(PM_MESSAGE);
315    char ed1[50], ed2[50];
316
317    memset(&pr, 0, sizeof(pr));
318    bstrncpy(pr.Name, val, sizeof(pr.Name));
319    if (!get_pool_dbr(ua, &pr)) {
320       return;
321    }
322    mr->PoolId = pr.PoolId;            /* set new PoolId */
323    /*
324     */
325    db_lock(ua->db);
326    Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
327       edit_int64(mr->PoolId, ed1), edit_int64(mr->MediaId, ed2));
328    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
329       ua->error_msg("%s", db_strerror(ua->db));
330    } else {
331       ua->info_msg(_("New Pool is: %s\n"), pr.Name);
332       opr->NumVols--;
333       if (!db_update_pool_record(ua->jcr, ua->db, opr)) {
334          ua->error_msg("%s", db_strerror(ua->db));
335       }
336       pr.NumVols++;
337       if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
338          ua->error_msg("%s", db_strerror(ua->db));
339       }
340    }
341    db_unlock(ua->db);
342 }
343
344 /* Modify the RecyclePool of a Volume */
345 void update_vol_recyclepool(UAContext *ua, char *val, MEDIA_DBR *mr)
346 {
347    POOL_DBR pr;
348    POOL_MEM query(PM_MESSAGE);
349    char ed1[50], ed2[50], *poolname;
350
351    if(val && *val) { /* update volume recyclepool="Scratch" */
352      /* If a pool name is given, look up the PoolId */
353      memset(&pr, 0, sizeof(pr));
354      bstrncpy(pr.Name, val, sizeof(pr.Name));
355      if (!get_pool_dbr(ua, &pr, NT_("recyclepool"))) {
356         return;
357      }
358      /* pool = select_pool_resource(ua);  */
359      mr->RecyclePoolId = pr.PoolId;            /* get the PoolId */
360      poolname = pr.Name;
361
362   } else { /* update volume recyclepool="" */
363     /* If no pool name is given, set the PoolId to 0 (the default) */
364      mr->RecyclePoolId = 0;
365      poolname = _("*None*");
366   }
367
368    db_lock(ua->db);
369    Mmsg(query, "UPDATE Media SET RecyclePoolId=%s WHERE MediaId=%s",
370       edit_int64(mr->RecyclePoolId, ed1), edit_int64(mr->MediaId, ed2));
371    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
372       ua->error_msg("%s", db_strerror(ua->db));
373    } else {
374       ua->info_msg(_("New RecyclePool is: %s\n"), poolname);
375    }
376    db_unlock(ua->db);
377 }
378
379 /*
380  * Refresh the Volume information from the Pool record
381  */
382 static void update_vol_from_pool(UAContext *ua, MEDIA_DBR *mr)
383 {
384    POOL_DBR pr;
385
386    memset(&pr, 0, sizeof(pr));
387    pr.PoolId = mr->PoolId;
388    if (!db_get_pool_record(ua->jcr, ua->db, &pr) ||
389        !acl_access_ok(ua, Pool_ACL, pr.Name)) {
390       return;
391    }
392    set_pool_dbr_defaults_in_media_dbr(mr, &pr);
393    if (!db_update_media_defaults(ua->jcr, ua->db, mr)) {
394       ua->error_msg(_("Error updating Volume record: ERR=%s"), db_strerror(ua->db));
395    } else {
396       ua->info_msg(_("Volume defaults updated from \"%s\" Pool record.\n"),
397          pr.Name);
398    }
399 }
400
401 /*
402  * Refresh the Volume information from the Pool record
403  *   for all Volumes
404  */
405 static void update_all_vols_from_pool(UAContext *ua, const char *pool_name) 
406 {
407    POOL_DBR pr;
408    MEDIA_DBR mr;
409
410    memset(&pr, 0, sizeof(pr));
411
412    bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
413    if (!get_pool_dbr(ua, &pr)) {
414       return;
415    }
416    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
417    mr.PoolId = pr.PoolId;
418    if (!db_update_media_defaults(ua->jcr, ua->db, &mr)) {
419       ua->error_msg(_("Error updating Volume records: ERR=%s"), db_strerror(ua->db));
420    } else {
421       ua->info_msg(_("All Volume defaults updated from \"%s\" Pool record.\n"),
422          pr.Name);
423    }
424 }
425
426 static void update_all_vols(UAContext *ua)
427 {
428    int i, num_pools;
429    uint32_t *ids;
430    POOL_DBR pr;
431    MEDIA_DBR mr;
432
433    memset(&pr, 0, sizeof(pr));
434    
435    if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
436       ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
437       return;
438    }
439
440    for (i=0; i<num_pools; i++) {
441       pr.PoolId = ids[i];
442       if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { /* ***FIXME*** use acl? */
443          ua->warning_msg(_("Updating all pools, but skipped PoolId=%d. ERR=%s\n"), db_strerror(ua->db));
444          continue;
445       }
446
447       set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
448       mr.PoolId = pr.PoolId;
449
450       if (!db_update_media_defaults(ua->jcr, ua->db, &mr)) {
451          ua->error_msg(_("Error updating Volume records: ERR=%s"), db_strerror(ua->db));
452       } else {
453          ua->info_msg(_("All Volume defaults updated from \"%s\" Pool record.\n"),
454             pr.Name);
455       }
456    }
457
458    free(ids);
459 }
460
461 static void update_volenabled(UAContext *ua, char *val, MEDIA_DBR *mr)
462 {
463    mr->Enabled = get_enabled(ua, val);
464    if (mr->Enabled < 0) {
465       return;
466    }
467    set_storageid_in_mr(NULL, mr);
468    if (!db_update_media_record(ua->jcr, ua->db, mr)) {
469       ua->error_msg(_("Error updating media record Enabled: ERR=%s"),
470                     db_strerror(ua->db));
471    } else {
472       ua->info_msg(_("New Enabled is: %d\n"), mr->Enabled);
473    }
474 }
475
476 static void update_vol_actiononpurge(UAContext *ua, char *val, MEDIA_DBR *mr)
477 {
478    POOL_MEM ret;
479    if (strcasecmp(val, "truncate") == 0) {
480       mr->ActionOnPurge = ON_PURGE_TRUNCATE;
481    } else {
482       mr->ActionOnPurge = 0;
483    }
484    
485    set_storageid_in_mr(NULL, mr);
486    if (!db_update_media_record(ua->jcr, ua->db, mr)) {
487       ua->error_msg(_("Error updating media record ActionOnPurge: ERR=%s"),
488                     db_strerror(ua->db));
489    } else {
490       ua->info_msg(_("New ActionOnPurge is: %s\n"), 
491                    action_on_purge_to_string(mr->ActionOnPurge, ret));
492    }
493 }
494
495 /*
496  * Update a media record -- allows you to change the
497  *  Volume status. E.g. if you want Bacula to stop
498  *  writing on the volume, set it to anything other
499  *  than Append.
500  */
501 static int update_volume(UAContext *ua)
502 {
503    MEDIA_DBR mr;
504    POOL *pool;
505    POOL_DBR pr;
506    POOLMEM *query;
507    POOL_MEM ret;
508    char buf[1000];
509    char ed1[130];
510    bool done = false;
511    int i;
512    const char *kw[] = {
513       NT_("VolStatus"),                /* 0 */
514       NT_("VolRetention"),             /* 1 */
515       NT_("VolUse"),                   /* 2 */
516       NT_("MaxVolJobs"),               /* 3 */
517       NT_("MaxVolFiles"),              /* 4 */
518       NT_("MaxVolBytes"),              /* 5 */
519       NT_("Recycle"),                  /* 6 */
520       NT_("InChanger"),                /* 7 */
521       NT_("Slot"),                     /* 8 */
522       NT_("Pool"),                     /* 9 */
523       NT_("FromPool"),                 /* 10 */
524       NT_("AllFromPool"),              /* 11 !!! see below !!! */
525       NT_("Enabled"),                  /* 12 */
526       NT_("RecyclePool"),              /* 13 */
527       NT_("ActionOnPurge"),            /* 14 */
528       NULL };
529
530 #define AllFromPool 11               /* keep this updated with above */
531
532    for (i=0; kw[i]; i++) {
533       int j;
534       POOL_DBR pr;
535
536       if ((j=find_arg_with_value(ua, kw[i])) > 0) {
537          /* If all from pool don't select a media record */
538          if (i != AllFromPool && !select_media_dbr(ua, &mr)) {
539             return 0;
540          }
541          switch (i) {
542          case 0:
543             update_volstatus(ua, ua->argv[j], &mr);
544             break;
545          case 1:
546             update_volretention(ua, ua->argv[j], &mr);
547             break;
548          case 2:
549             update_voluseduration(ua, ua->argv[j], &mr);
550             break;
551          case 3:
552             update_volmaxjobs(ua, ua->argv[j], &mr);
553             break;
554          case 4:
555             update_volmaxfiles(ua, ua->argv[j], &mr);
556             break;
557          case 5:
558             update_volmaxbytes(ua, ua->argv[j], &mr);
559             break;
560          case 6:
561             update_volrecycle(ua, ua->argv[j], &mr);
562             break;
563          case 7:
564             update_volinchanger(ua, ua->argv[j], &mr);
565             break;
566          case 8:
567             update_volslot(ua, ua->argv[j], &mr);
568             break;
569          case 9:
570             memset(&pr, 0, sizeof(POOL_DBR));
571             pr.PoolId = mr.PoolId;
572             if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
573                ua->error_msg("%s", db_strerror(ua->db));
574                break;
575             }
576             update_vol_pool(ua, ua->argv[j], &mr, &pr);
577             break;
578          case 10:
579             update_vol_from_pool(ua, &mr);
580             return 1;
581          case 11:
582             update_all_vols_from_pool(ua, ua->argv[j]);
583             return 1;
584          case 12:
585             update_volenabled(ua, ua->argv[j], &mr);
586             break;
587          case 13:
588             update_vol_recyclepool(ua, ua->argv[j], &mr);
589             break;
590          case 14:
591             update_vol_actiononpurge(ua, ua->argv[j], &mr);
592             break;
593          }
594          done = true;
595       }
596    }
597
598    /* Allow user to simply update all volumes */
599    if (find_arg(ua, NT_("fromallpools")) > 0) {
600       update_all_vols(ua);
601       return 1;
602    }
603
604    for ( ; !done; ) {
605       start_prompt(ua, _("Parameters to modify:\n"));
606       add_prompt(ua, _("Volume Status"));              /* 0 */
607       add_prompt(ua, _("Volume Retention Period"));    /* 1 */
608       add_prompt(ua, _("Volume Use Duration"));        /* 2 */
609       add_prompt(ua, _("Maximum Volume Jobs"));        /* 3 */
610       add_prompt(ua, _("Maximum Volume Files"));       /* 4 */
611       add_prompt(ua, _("Maximum Volume Bytes"));       /* 5 */
612       add_prompt(ua, _("Recycle Flag"));               /* 6 */
613       add_prompt(ua, _("Slot"));                       /* 7 */
614       add_prompt(ua, _("InChanger Flag"));             /* 8 */
615       add_prompt(ua, _("Volume Files"));               /* 9 */
616       add_prompt(ua, _("Pool"));                       /* 10 */
617       add_prompt(ua, _("Volume from Pool"));           /* 11 */
618       add_prompt(ua, _("All Volumes from Pool"));      /* 12 */
619       add_prompt(ua, _("All Volumes from all Pools")); /* 13 */
620       add_prompt(ua, _("Enabled")),                    /* 14 */
621       add_prompt(ua, _("RecyclePool")),                /* 15 */
622       add_prompt(ua, _("Action On Purge")),            /* 16 */
623       add_prompt(ua, _("Done"));                       /* 17 */
624       i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0);  
625
626       /* For All Volumes, All Volumes from Pool, and Done, we don't need
627            * a Volume record */
628       if ( i != 12 && i != 13 && i != 17) {
629          if (!select_media_dbr(ua, &mr)) {  /* Get Volume record */
630             return 0;
631          }
632          ua->info_msg(_("Updating Volume \"%s\"\n"), mr.VolumeName);
633       }
634       switch (i) {
635       case 0:                         /* Volume Status */
636          /* Modify Volume Status */
637          ua->info_msg(_("Current Volume status is: %s\n"), mr.VolStatus);
638          start_prompt(ua, _("Possible Values are:\n"));
639          add_prompt(ua, NT_("Append")); 
640          add_prompt(ua, NT_("Archive"));
641          add_prompt(ua, NT_("Disabled"));
642          add_prompt(ua, NT_("Full"));
643          add_prompt(ua, NT_("Used"));
644          add_prompt(ua, NT_("Cleaning"));
645          if (strcmp(mr.VolStatus, NT_("Purged")) == 0) {
646             add_prompt(ua, NT_("Recycle"));
647          }
648          add_prompt(ua, NT_("Read-Only"));
649          if (do_prompt(ua, "", _("Choose new Volume Status"), ua->cmd, sizeof(mr.VolStatus)) < 0) {
650             return 1;
651          }
652          update_volstatus(ua, ua->cmd, &mr);
653          break;
654       case 1:                         /* Retention */
655          ua->info_msg(_("Current retention period is: %s\n"),
656             edit_utime(mr.VolRetention, ed1, sizeof(ed1)));
657          if (!get_cmd(ua, _("Enter Volume Retention period: "))) {
658             return 0;
659          }
660          update_volretention(ua, ua->cmd, &mr);
661          break;
662
663       case 2:                         /* Use Duration */
664          ua->info_msg(_("Current use duration is: %s\n"),
665             edit_utime(mr.VolUseDuration, ed1, sizeof(ed1)));
666          if (!get_cmd(ua, _("Enter Volume Use Duration: "))) {
667             return 0;
668          }
669          update_voluseduration(ua, ua->cmd, &mr);
670          break;
671
672       case 3:                         /* Max Jobs */
673          ua->info_msg(_("Current max jobs is: %u\n"), mr.MaxVolJobs);
674          if (!get_pint(ua, _("Enter new Maximum Jobs: "))) {
675             return 0;
676          }
677          update_volmaxjobs(ua, ua->cmd, &mr);
678          break;
679
680       case 4:                         /* Max Files */
681          ua->info_msg(_("Current max files is: %u\n"), mr.MaxVolFiles);
682          if (!get_pint(ua, _("Enter new Maximum Files: "))) {
683             return 0;
684          }
685          update_volmaxfiles(ua, ua->cmd, &mr);
686          break;
687
688       case 5:                         /* Max Bytes */
689          ua->info_msg(_("Current value is: %s\n"), edit_uint64(mr.MaxVolBytes, ed1));
690          if (!get_cmd(ua, _("Enter new Maximum Bytes: "))) {
691             return 0;
692          }
693          update_volmaxbytes(ua, ua->cmd, &mr);
694          break;
695
696
697       case 6:                         /* Recycle */
698          ua->info_msg(_("Current recycle flag is: %s\n"),
699             mr.Recycle==1?_("yes"):_("no"));
700          if (!get_yesno(ua, _("Enter new Recycle status: "))) {
701             return 0;
702          }
703          update_volrecycle(ua, ua->cmd, &mr);
704          break;
705
706       case 7:                         /* Slot */
707          ua->info_msg(_("Current Slot is: %d\n"), mr.Slot);
708          if (!get_pint(ua, _("Enter new Slot: "))) {
709             return 0;
710          }
711          update_volslot(ua, ua->cmd, &mr);
712          break;
713          
714       case 8:                         /* InChanger */
715          ua->info_msg(_("Current InChanger flag is: %d\n"), mr.InChanger);
716          bsnprintf(buf, sizeof(buf), _("Set InChanger flag for Volume \"%s\": yes/no: "),
717             mr.VolumeName);
718          if (!get_yesno(ua, buf)) {
719             return 0;
720          }
721          mr.InChanger = ua->pint32_val;
722          /*
723           * Make sure to use db_update... rather than doing this directly,
724           *   so that any Slot is handled correctly.
725           */
726          set_storageid_in_mr(NULL, &mr);
727          if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
728             ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
729          } else {
730             ua->info_msg(_("New InChanger flag is: %d\n"), mr.InChanger);
731          }
732          break;
733
734
735       case 9:                         /* Volume Files */
736          int32_t VolFiles;
737          ua->warning_msg(_("Warning changing Volume Files can result\n"
738                         "in loss of data on your Volume\n\n"));
739          ua->info_msg(_("Current Volume Files is: %u\n"), mr.VolFiles);
740          if (!get_pint(ua, _("Enter new number of Files for Volume: "))) {
741             return 0;
742          }
743          VolFiles = ua->pint32_val;
744          if (VolFiles != (int)(mr.VolFiles + 1)) {
745             ua->warning_msg(_("Normally, you should only increase Volume Files by one!\n"));
746             if (!get_yesno(ua, _("Increase Volume Files? (yes/no): ")) || ua->pint32_val == 0) {
747                break;
748             }
749          }
750          query = get_pool_memory(PM_MESSAGE);
751          Mmsg(query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%s",
752             VolFiles, edit_int64(mr.MediaId, ed1));
753          if (!db_sql_query(ua->db, query, NULL, NULL)) {
754             ua->error_msg("%s", db_strerror(ua->db));
755          } else {
756             ua->info_msg(_("New Volume Files is: %u\n"), VolFiles);
757          }
758          free_pool_memory(query);
759          break;
760
761       case 10:                        /* Volume's Pool */
762          memset(&pr, 0, sizeof(POOL_DBR));
763          pr.PoolId = mr.PoolId;
764          if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
765             ua->error_msg("%s", db_strerror(ua->db));
766             return 0;
767          }
768          ua->info_msg(_("Current Pool is: %s\n"), pr.Name);
769          if (!get_cmd(ua, _("Enter new Pool name: "))) {
770             return 0;
771          }
772          update_vol_pool(ua, ua->cmd, &mr, &pr);
773          return 1;
774
775       case 11:
776          update_vol_from_pool(ua, &mr);
777          return 1;
778       case 12:
779          pool = select_pool_resource(ua);
780          if (pool) {
781             update_all_vols_from_pool(ua, pool->name());
782          }
783          return 1;
784
785       case 13:
786          update_all_vols(ua);
787          return 1;
788
789       case 14:
790          ua->info_msg(_("Current Enabled is: %d\n"), mr.Enabled);
791          if (!get_cmd(ua, _("Enter new Enabled: "))) {
792             return 0;
793          }
794          if (strcasecmp(ua->cmd, "yes") == 0 || strcasecmp(ua->cmd, "true") == 0) {
795             mr.Enabled = 1;
796          } else if (strcasecmp(ua->cmd, "no") == 0 || strcasecmp(ua->cmd, "false") == 0) {
797             mr.Enabled = 0;
798          } else if (strcasecmp(ua->cmd, "archived") == 0) { 
799             mr.Enabled = 2;
800          } else {
801             mr.Enabled = atoi(ua->cmd);
802          }
803          update_volenabled(ua, ua->cmd, &mr);
804          break;
805
806       case 15:
807          memset(&pr, 0, sizeof(POOL_DBR));
808          pr.PoolId = mr.RecyclePoolId;
809          if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
810             ua->info_msg(_("Current RecyclePool is: %s\n"), pr.Name);
811          } else {
812             ua->info_msg(_("No current RecyclePool\n"));
813          }
814          if (!select_pool_dbr(ua, &pr, NT_("recyclepool"))) {
815             return 0;
816          }
817          update_vol_recyclepool(ua, pr.Name, &mr);
818          return 1;
819
820       case 16:
821          pm_strcpy(ret, "");
822          ua->info_msg(_("Current ActionOnPurge is: %s\n"), 
823                       action_on_purge_to_string(mr.ActionOnPurge, ret));
824          if (!get_cmd(ua, _("Enter new ActionOnPurge (one of: Truncate, None): "))) {
825             return 0;
826          }
827
828          update_vol_actiononpurge(ua, ua->cmd, &mr);
829          break;
830
831       default:                        /* Done or error */
832          ua->info_msg(_("Selection terminated.\n"));
833          return 1;
834       }
835    }
836    return 1;
837 }
838
839 /*
840  * Update long term statistics
841  */
842 static bool update_stats(UAContext *ua)
843 {
844    int i = find_arg_with_value(ua, NT_("days"));
845    utime_t since=0;
846
847    if (i >= 0) {
848       since = atoi(ua->argv[i]) * 24*60*60;
849    }
850
851    int nb = db_update_stats(ua->jcr, ua->db, since);
852    ua->info_msg(_("Updating %i job(s).\n"), nb);
853
854    return true;
855 }
856
857 /*
858  * Update pool record -- pull info from current POOL resource
859  */
860 static bool update_pool(UAContext *ua)
861 {
862    POOL_DBR  pr;
863    int id;
864    POOL *pool;
865    POOLMEM *query;
866    char ed1[50];
867
868    pool = get_pool_resource(ua);
869    if (!pool) {
870       return false;
871    }
872
873    memset(&pr, 0, sizeof(pr));
874    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
875    if (!get_pool_dbr(ua, &pr)) {
876       return false;
877    }
878
879    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE); /* update */
880    set_pooldbr_references(ua->jcr, ua->db, &pr, pool);
881
882    id = db_update_pool_record(ua->jcr, ua->db, &pr);
883    if (id <= 0) {
884       ua->error_msg(_("db_update_pool_record returned %d. ERR=%s\n"),
885          id, db_strerror(ua->db));
886    }
887    query = get_pool_memory(PM_MESSAGE);
888    Mmsg(query, list_pool, edit_int64(pr.PoolId, ed1));
889    db_list_sql_query(ua->jcr, ua->db, query, prtit, ua, 1, HORZ_LIST);
890    free_pool_memory(query);
891    ua->info_msg(_("Pool DB record updated from resource.\n"));
892    return true;
893 }
894
895 /*
896  * Update a Job record -- allows you to change the
897  *  date fields in a Job record. This helps when
898  *  providing migration from other vendors.
899  */
900 static bool update_job(UAContext *ua)
901 {
902    int i;
903    char ed1[50], ed2[50];
904    POOL_MEM cmd(PM_MESSAGE);
905    JOB_DBR jr;
906    CLIENT_DBR cr;
907    utime_t StartTime;
908    char *client_name = NULL;
909    char *start_time = NULL;
910    const char *kw[] = {
911       NT_("starttime"),                   /* 0 */
912       NT_("client"),                      /* 1 */
913       NULL };
914
915    Dmsg1(200, "cmd=%s\n", ua->cmd);
916    i = find_arg_with_value(ua, NT_("jobid"));
917    if (i < 0) {
918       ua->error_msg(_("Expect JobId keyword, not found.\n"));
919       return false;
920    }
921    memset(&jr, 0, sizeof(jr));
922    memset(&cr, 0, sizeof(cr));
923    jr.JobId = str_to_int64(ua->argv[i]);
924    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
925       ua->error_msg("%s", db_strerror(ua->db));
926       return false;
927    }
928
929    for (i=0; kw[i]; i++) {
930       int j;
931       if ((j=find_arg_with_value(ua, kw[i])) >= 0) {
932          switch (i) {
933          case 0:                         /* start time */
934             start_time = ua->argv[j];
935             break;
936          case 1:                         /* Client name */
937             client_name = ua->argv[j];
938             break;
939          }
940       }
941    }
942    if (!client_name && !start_time) {
943       ua->error_msg(_("Neither Client nor StartTime specified.\n"));
944       return 0;
945    }
946    if (client_name) {
947       if (!get_client_dbr(ua, &cr)) {
948          return false;
949       }
950       jr.ClientId = cr.ClientId;
951    }
952    if (start_time) {
953       utime_t delta_start;
954
955       StartTime = str_to_utime(start_time);
956       if (StartTime == 0) {
957          ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
958          return false;
959       }
960       delta_start = StartTime - jr.StartTime;
961       Dmsg3(200, "ST=%lld jr.ST=%lld delta=%lld\n", StartTime, 
962             (utime_t)jr.StartTime, delta_start);
963       jr.StartTime = (time_t)StartTime;
964       jr.SchedTime += (time_t)delta_start;
965       jr.EndTime += (time_t)delta_start;
966       jr.JobTDate += delta_start;
967       /* Convert to DB times */
968       bstrutime(jr.cStartTime, sizeof(jr.cStartTime), jr.StartTime);
969       bstrutime(jr.cSchedTime, sizeof(jr.cSchedTime), jr.SchedTime);
970       bstrutime(jr.cEndTime, sizeof(jr.cEndTime), jr.EndTime);
971    }
972    Mmsg(cmd, "UPDATE Job SET ClientId=%s,StartTime='%s',SchedTime='%s',"
973              "EndTime='%s',JobTDate=%s WHERE JobId=%s", 
974              edit_int64(jr.ClientId, ed1), 
975              jr.cStartTime,
976              jr.cSchedTime,
977              jr.cEndTime,
978              edit_uint64(jr.JobTDate, ed1), 
979              edit_int64(jr.JobId, ed2));
980    if (!db_sql_query(ua->db, cmd.c_str(), NULL, NULL)) {
981       ua->error_msg("%s", db_strerror(ua->db));
982       return false;
983    }
984    return true;
985 }