]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_update.c
Fix update Volume=x Slot=nn when Slot > MaxVols
[bacula/bacula] / bacula / src / dird / ua_update.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *   Bacula Director -- Update command processing
22  *     Split from ua_cmds.c March 2005
23  *
24  *     Kern Sibbald, September MM
25  *
26  */
27
28 #include "bacula.h"
29 #include "dird.h"
30
31 /* Forward referenced functions */
32 static int update_volume(UAContext *ua);
33 static bool update_pool(UAContext *ua);
34 static bool update_job(UAContext *ua);
35 static bool update_stats(UAContext *ua);
36
37 /*
38  * Update a Pool Record in the database.
39  *  It is always updated from the Resource record.
40  *
41  *    update pool=<pool-name>
42  *         updates pool from Pool resource
43  *    update media pool=<pool-name> volume=<volume-name>
44  *         changes pool info for volume
45  *    update slots [scan=...]
46  *         updates autochanger slots
47  *    update stats [days=...]
48  *         updates long term statistics
49  */
50 int update_cmd(UAContext *ua, const char *cmd)
51 {
52    static const char *kw[] = {
53       NT_("media"),  /* 0 */
54       NT_("volume"), /* 1 */
55       NT_("pool"),   /* 2 */
56       NT_("slots"),  /* 3 */
57       NT_("slot"),   /* 4 */
58       NT_("jobid"),  /* 5 */
59       NT_("stats"),  /* 6 */
60       NT_("snap"),   /* 7 */
61       NT_("snapshot"),/* 8 */
62       NULL};
63
64    if (!open_client_db(ua)) {
65       return 1;
66    }
67
68    switch (find_arg_keyword(ua, kw)) {
69    case 0:
70    case 1:
71       update_volume(ua);
72       return 1;
73    case 2:
74       update_pool(ua);
75       return 1;
76    case 3:
77    case 4:
78       update_slots(ua);
79       return 1;
80    case 5:
81       update_job(ua);
82       return 1;
83    case 6:
84       update_stats(ua);
85       return 1;
86    case 7:
87    case 8:
88       update_snapshot(ua);
89       return 1;
90    default:
91       break;
92    }
93
94    start_prompt(ua, _("Update choice:\n"));
95    add_prompt(ua, _("Volume parameters"));
96    add_prompt(ua, _("Pool from resource"));
97    add_prompt(ua, _("Slots from autochanger"));
98    add_prompt(ua, _("Long term statistics"));
99    add_prompt(ua, _("Snapshot parameters"));
100    switch (do_prompt(ua, _("item"), _("Choose catalog item to update"), NULL, 0)) {
101    case 0:
102       update_volume(ua);
103       break;
104    case 1:
105       update_pool(ua);
106       break;
107    case 2:
108       update_slots(ua);
109       break;
110    case 3:
111       update_stats(ua);
112       break;
113    case 4:
114       update_snapshot(ua);
115       break;
116    default:
117       break;
118    }
119    return 1;
120 }
121
122 static void update_volstatus(UAContext *ua, const char *val, MEDIA_DBR *mr)
123 {
124    POOL_MEM query(PM_MESSAGE);
125    const char *kw[] = {
126       NT_("Append"),
127       NT_("Archive"),
128       NT_("Disabled"),
129       NT_("Full"),
130       NT_("Used"),
131       NT_("Cleaning"),
132       NT_("Recycle"),
133       NT_("Read-Only"),
134       NT_("Error"),
135       NULL};
136    bool found = false;
137    int i;
138
139    for (i=0; kw[i]; i++) {
140       if (strcasecmp(val, kw[i]) == 0) {
141          found = true;
142          break;
143       }
144    }
145    if (!found) {
146       ua->error_msg(_("Invalid VolStatus specified: %s\n"), val);
147    } else {
148       char ed1[50];
149       bstrncpy(mr->VolStatus, kw[i], sizeof(mr->VolStatus));
150       Mmsg(query, "UPDATE Media SET VolStatus='%s' WHERE MediaId=%s",
151          mr->VolStatus, edit_int64(mr->MediaId,ed1));
152       if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
153          ua->error_msg("%s", db_strerror(ua->db));
154       } else {
155          ua->info_msg(_("New Volume status is: %s\n"), mr->VolStatus);
156       }
157    }
158 }
159
160 static void update_volretention(UAContext *ua, char *val, MEDIA_DBR *mr)
161 {
162    char ed1[150], ed2[50];
163    POOL_MEM query(PM_MESSAGE);
164    if (!duration_to_utime(val, &mr->VolRetention)) {
165       ua->error_msg(_("Invalid retention period specified: %s\n"), val);
166       return;
167    }
168    Mmsg(query, "UPDATE Media SET VolRetention=%s WHERE MediaId=%s",
169       edit_uint64(mr->VolRetention, ed1), edit_int64(mr->MediaId,ed2));
170    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
171       ua->error_msg("%s", db_strerror(ua->db));
172    } else {
173       ua->info_msg(_("New retention period is: %s\n"),
174          edit_utime(mr->VolRetention, ed1, sizeof(ed1)));
175    }
176 }
177
178 static void update_voluseduration(UAContext *ua, char *val, MEDIA_DBR *mr)
179 {
180    char ed1[150], ed2[50];
181    POOL_MEM query(PM_MESSAGE);
182
183    if (!duration_to_utime(val, &mr->VolUseDuration)) {
184       ua->error_msg(_("Invalid use duration specified: %s\n"), val);
185       return;
186    }
187    Mmsg(query, "UPDATE Media SET VolUseDuration=%s WHERE MediaId=%s",
188       edit_uint64(mr->VolUseDuration, ed1), edit_int64(mr->MediaId,ed2));
189    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
190       ua->error_msg("%s", db_strerror(ua->db));
191    } else {
192       ua->info_msg(_("New use duration is: %s\n"),
193          edit_utime(mr->VolUseDuration, ed1, sizeof(ed1)));
194    }
195 }
196
197 static void update_volmaxjobs(UAContext *ua, char *val, MEDIA_DBR *mr)
198 {
199    POOL_MEM query(PM_MESSAGE);
200    char ed1[50];
201    Mmsg(query, "UPDATE Media SET MaxVolJobs=%s WHERE MediaId=%s",
202       val, edit_int64(mr->MediaId,ed1));
203    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
204       ua->error_msg("%s", db_strerror(ua->db));
205    } else {
206       ua->info_msg(_("New max jobs is: %s\n"), val);
207    }
208 }
209
210 static void update_volmaxfiles(UAContext *ua, char *val, MEDIA_DBR *mr)
211 {
212    POOL_MEM query(PM_MESSAGE);
213    char ed1[50];
214    Mmsg(query, "UPDATE Media SET MaxVolFiles=%s WHERE MediaId=%s",
215       val, edit_int64(mr->MediaId, ed1));
216    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
217       ua->error_msg("%s", db_strerror(ua->db));
218    } else {
219       ua->info_msg(_("New max files is: %s\n"), val);
220    }
221 }
222
223 static void update_volmaxbytes(UAContext *ua, char *val, MEDIA_DBR *mr)
224 {
225    uint64_t maxbytes;
226    char ed1[50], ed2[50];
227    POOL_MEM query(PM_MESSAGE);
228
229    if (!size_to_uint64(val, strlen(val), &maxbytes)) {
230       ua->error_msg(_("Invalid max. bytes specification: %s\n"), val);
231       return;
232    }
233    Mmsg(query, "UPDATE Media SET MaxVolBytes=%s WHERE MediaId=%s",
234       edit_uint64(maxbytes, ed1), edit_int64(mr->MediaId, ed2));
235    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
236       ua->error_msg("%s", db_strerror(ua->db));
237    } else {
238       ua->info_msg(_("New Max bytes is: %s\n"), edit_uint64(maxbytes, ed1));
239    }
240 }
241
242 static void update_volrecycle(UAContext *ua, char *val, MEDIA_DBR *mr)
243 {
244    int recycle;
245    char ed1[50];
246
247    POOL_MEM query(PM_MESSAGE);
248    if (!is_yesno(val, &recycle)) {
249       ua->error_msg(_("Invalid value. It must be yes or no.\n"));
250       return;
251    }
252    Mmsg(query, "UPDATE Media SET Recycle=%d WHERE MediaId=%s",
253       recycle, edit_int64(mr->MediaId, ed1));
254    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
255       ua->error_msg("%s", db_strerror(ua->db));
256    } else {
257       ua->info_msg(_("New Recycle flag is: %s\n"),
258          recycle==1?_("yes"):_("no"));
259    }
260 }
261
262 static void update_volinchanger(UAContext *ua, char *val, MEDIA_DBR *mr)
263 {
264    int InChanger;
265    char ed1[50];
266
267    POOL_MEM query(PM_MESSAGE);
268    if (!is_yesno(val, &InChanger)) {
269       ua->error_msg(_("Invalid value. It must be yes or no.\n"));
270       return;
271    }
272    Mmsg(query, "UPDATE Media SET InChanger=%d WHERE MediaId=%s",
273       InChanger, edit_int64(mr->MediaId, ed1));
274    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
275       ua->error_msg("%s", db_strerror(ua->db));
276    } else {
277       ua->info_msg(_("New InChanger flag is: %s\n"),
278          InChanger==1?_("yes"):_("no"));
279    }
280 }
281
282
283 static void update_volslot(UAContext *ua, char *val, MEDIA_DBR *mr)
284 {
285    POOL_DBR pr;
286
287    memset(&pr, 0, sizeof(POOL_DBR));
288    pr.PoolId = mr->PoolId;
289    if (!db_get_pool_numvols(ua->jcr, ua->db, &pr)) {
290       ua->error_msg("%s", db_strerror(ua->db));
291       return;
292    }
293    mr->Slot = atoi(val);
294    if (mr->Slot < 0) {
295       ua->error_msg(_("Invalid slot, it must be greater than zero\n"));
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_numvols(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_numvols(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_numvols(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_numvols(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          update_volenabled(ua, ua->cmd, &mr);
795          break;
796
797       case 15:
798          memset(&pr, 0, sizeof(POOL_DBR));
799          pr.PoolId = mr.RecyclePoolId;
800          if (db_get_pool_numvols(ua->jcr, ua->db, &pr)) {
801             ua->info_msg(_("Current RecyclePool is: %s\n"), pr.Name);
802          } else {
803             ua->info_msg(_("No current RecyclePool\n"));
804          }
805          if (!select_pool_dbr(ua, &pr, NT_("recyclepool"))) {
806             return 0;
807          }
808          update_vol_recyclepool(ua, pr.Name, &mr);
809          return 1;
810
811       case 16:
812          pm_strcpy(ret, "");
813          ua->info_msg(_("Current ActionOnPurge is: %s\n"),
814                       action_on_purge_to_string(mr.ActionOnPurge, ret));
815          if (!get_cmd(ua, _("Enter new ActionOnPurge (one of: Truncate, None): "))) {
816             return 0;
817          }
818
819          update_vol_actiononpurge(ua, ua->cmd, &mr);
820          break;
821
822       default:                        /* Done or error */
823          ua->info_msg(_("Selection terminated.\n"));
824          return 1;
825       }
826    }
827    return 1;
828 }
829
830 /*
831  * Update long term statistics
832  */
833 static bool update_stats(UAContext *ua)
834 {
835    int i = find_arg_with_value(ua, NT_("days"));
836    utime_t since=0;
837
838    if (i >= 0) {
839       since = atoi(ua->argv[i]) * 24*60*60;
840    }
841
842    int nb = db_update_stats(ua->jcr, ua->db, since);
843    ua->info_msg(_("Updating %i job(s).\n"), nb);
844
845    return true;
846 }
847
848 /*
849  * Update pool record -- pull info from current POOL resource
850  */
851 static bool update_pool(UAContext *ua)
852 {
853    POOL_DBR  pr;
854    int id;
855    POOL *pool;
856    POOLMEM *query;
857    char ed1[50];
858
859    pool = get_pool_resource(ua);
860    if (!pool) {
861       return false;
862    }
863
864    memset(&pr, 0, sizeof(pr));
865    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
866    if (!get_pool_dbr(ua, &pr)) {
867       return false;
868    }
869
870    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE); /* update */
871    set_pooldbr_references(ua->jcr, ua->db, &pr, pool);
872
873    id = db_update_pool_record(ua->jcr, ua->db, &pr);
874    if (id <= 0) {
875       ua->error_msg(_("db_update_pool_record returned %d. ERR=%s\n"),
876          id, db_strerror(ua->db));
877    }
878    query = get_pool_memory(PM_MESSAGE);
879    Mmsg(query, list_pool, edit_int64(pr.PoolId, ed1));
880    db_list_sql_query(ua->jcr, ua->db, query, prtit, ua, 1, HORZ_LIST);
881    free_pool_memory(query);
882    ua->info_msg(_("Pool DB record updated from resource.\n"));
883    return true;
884 }
885
886 /*
887  * Update a Job record -- allows you to change the
888  *  date fields in a Job record. This helps when
889  *  providing migration from other vendors.
890  */
891 static bool update_job(UAContext *ua)
892 {
893    int i;
894    char ed1[50], ed2[50];
895    POOL_MEM cmd(PM_MESSAGE);
896    JOB_DBR jr;
897    CLIENT_DBR cr;
898    utime_t StartTime;
899    char *client_name = NULL;
900    char *start_time = NULL;
901    const char *kw[] = {
902       NT_("starttime"),                   /* 0 */
903       NT_("client"),                      /* 1 */
904       NULL };
905
906    Dmsg1(200, "cmd=%s\n", ua->cmd);
907    i = find_arg_with_value(ua, NT_("jobid"));
908    if (i < 0) {
909       ua->error_msg(_("Expect JobId keyword, not found.\n"));
910       return false;
911    }
912    memset(&jr, 0, sizeof(jr));
913    memset(&cr, 0, sizeof(cr));
914    jr.JobId = str_to_int64(ua->argv[i]);
915    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
916       ua->error_msg("%s", db_strerror(ua->db));
917       return false;
918    }
919
920    for (i=0; kw[i]; i++) {
921       int j;
922       if ((j=find_arg_with_value(ua, kw[i])) >= 0) {
923          switch (i) {
924          case 0:                         /* start time */
925             start_time = ua->argv[j];
926             break;
927          case 1:                         /* Client name */
928             client_name = ua->argv[j];
929             break;
930          }
931       }
932    }
933    if (!client_name && !start_time) {
934       ua->error_msg(_("Neither Client nor StartTime specified.\n"));
935       return 0;
936    }
937    if (client_name) {
938       if (!get_client_dbr(ua, &cr)) {
939          return false;
940       }
941       jr.ClientId = cr.ClientId;
942    }
943    if (start_time) {
944       utime_t delta_start;
945
946       StartTime = str_to_utime(start_time);
947       if (StartTime == 0) {
948          ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
949          return false;
950       }
951       delta_start = StartTime - jr.StartTime;
952       Dmsg3(200, "ST=%lld jr.ST=%lld delta=%lld\n", StartTime,
953             (utime_t)jr.StartTime, delta_start);
954       jr.StartTime = (time_t)StartTime;
955       jr.SchedTime += (time_t)delta_start;
956       jr.EndTime += (time_t)delta_start;
957       jr.JobTDate += delta_start;
958       /* Convert to DB times */
959       bstrutime(jr.cStartTime, sizeof(jr.cStartTime), jr.StartTime);
960       bstrutime(jr.cSchedTime, sizeof(jr.cSchedTime), jr.SchedTime);
961       bstrutime(jr.cEndTime, sizeof(jr.cEndTime), jr.EndTime);
962    }
963    Mmsg(cmd, "UPDATE Job SET ClientId=%s,StartTime='%s',SchedTime='%s',"
964              "EndTime='%s',JobTDate=%s WHERE JobId=%s",
965              edit_int64(jr.ClientId, ed1),
966              jr.cStartTime,
967              jr.cSchedTime,
968              jr.cEndTime,
969              edit_uint64(jr.JobTDate, ed1),
970              edit_int64(jr.JobId, ed2));
971    if (!db_sql_query(ua->db, cmd.c_str(), NULL, NULL)) {
972       ua->error_msg("%s", db_strerror(ua->db));
973       return false;
974    }
975    return true;
976 }