]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_update.c
Partial modification of copyrights as per agreement with FSFE
[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 (pr.MaxVols > 0 && mr->Slot > (int)pr.MaxVols) {
295       ua->error_msg(_("Invalid slot, it must be between 0 and MaxVols=%d\n"),
296          pr.MaxVols);
297       return;
298    }
299    /*
300     * Make sure to use db_update... rather than doing this directly,
301     *   so that any Slot is handled correctly.
302     */
303    set_storageid_in_mr(NULL, mr);
304    if (!db_update_media_record(ua->jcr, ua->db, mr)) {
305       ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
306    } else {
307       ua->info_msg(_("New Slot is: %d\n"), mr->Slot);
308    }
309 }
310
311 /* Modify the Pool in which this Volume is located */
312 void update_vol_pool(UAContext *ua, char *val, MEDIA_DBR *mr, POOL_DBR *opr)
313 {
314    POOL_DBR pr;
315    POOL_MEM query(PM_MESSAGE);
316    char ed1[50], ed2[50];
317
318    memset(&pr, 0, sizeof(pr));
319    bstrncpy(pr.Name, val, sizeof(pr.Name));
320    if (!get_pool_dbr(ua, &pr)) {
321       return;
322    }
323    mr->PoolId = pr.PoolId;            /* set new PoolId */
324    /*
325     */
326    db_lock(ua->db);
327    Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
328       edit_int64(mr->PoolId, ed1), edit_int64(mr->MediaId, ed2));
329    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
330       ua->error_msg("%s", db_strerror(ua->db));
331    } else {
332       ua->info_msg(_("New Pool is: %s\n"), pr.Name);
333       opr->NumVols--;
334       if (!db_update_pool_record(ua->jcr, ua->db, opr)) {
335          ua->error_msg("%s", db_strerror(ua->db));
336       }
337       pr.NumVols++;
338       if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
339          ua->error_msg("%s", db_strerror(ua->db));
340       }
341    }
342    db_unlock(ua->db);
343 }
344
345 /* Modify the RecyclePool of a Volume */
346 void update_vol_recyclepool(UAContext *ua, char *val, MEDIA_DBR *mr)
347 {
348    POOL_DBR pr;
349    POOL_MEM query(PM_MESSAGE);
350    char ed1[50], ed2[50], *poolname;
351
352    if(val && *val) { /* update volume recyclepool="Scratch" */
353      /* If a pool name is given, look up the PoolId */
354      memset(&pr, 0, sizeof(pr));
355      bstrncpy(pr.Name, val, sizeof(pr.Name));
356      if (!get_pool_dbr(ua, &pr, NT_("recyclepool"))) {
357         return;
358      }
359      /* pool = select_pool_resource(ua);  */
360      mr->RecyclePoolId = pr.PoolId;            /* get the PoolId */
361      poolname = pr.Name;
362
363   } else { /* update volume recyclepool="" */
364     /* If no pool name is given, set the PoolId to 0 (the default) */
365      mr->RecyclePoolId = 0;
366      poolname = _("*None*");
367   }
368
369    db_lock(ua->db);
370    Mmsg(query, "UPDATE Media SET RecyclePoolId=%s WHERE MediaId=%s",
371       edit_int64(mr->RecyclePoolId, ed1), edit_int64(mr->MediaId, ed2));
372    if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
373       ua->error_msg("%s", db_strerror(ua->db));
374    } else {
375       ua->info_msg(_("New RecyclePool is: %s\n"), poolname);
376    }
377    db_unlock(ua->db);
378 }
379
380 /*
381  * Refresh the Volume information from the Pool record
382  */
383 static void update_vol_from_pool(UAContext *ua, MEDIA_DBR *mr)
384 {
385    POOL_DBR pr;
386
387    memset(&pr, 0, sizeof(pr));
388    pr.PoolId = mr->PoolId;
389    if (!db_get_pool_numvols(ua->jcr, ua->db, &pr) ||
390        !acl_access_ok(ua, Pool_ACL, pr.Name)) {
391       return;
392    }
393    set_pool_dbr_defaults_in_media_dbr(mr, &pr);
394    if (!db_update_media_defaults(ua->jcr, ua->db, mr)) {
395       ua->error_msg(_("Error updating Volume record: ERR=%s"), db_strerror(ua->db));
396    } else {
397       ua->info_msg(_("Volume defaults updated from \"%s\" Pool record.\n"),
398          pr.Name);
399    }
400 }
401
402 /*
403  * Refresh the Volume information from the Pool record
404  *   for all Volumes
405  */
406 static void update_all_vols_from_pool(UAContext *ua, const char *pool_name)
407 {
408    POOL_DBR pr;
409    MEDIA_DBR mr;
410
411    memset(&pr, 0, sizeof(pr));
412
413    bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
414    if (!get_pool_dbr(ua, &pr)) {
415       return;
416    }
417    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
418    mr.PoolId = pr.PoolId;
419    if (!db_update_media_defaults(ua->jcr, ua->db, &mr)) {
420       ua->error_msg(_("Error updating Volume records: ERR=%s"), db_strerror(ua->db));
421    } else {
422       ua->info_msg(_("All Volume defaults updated from \"%s\" Pool record.\n"),
423          pr.Name);
424    }
425 }
426
427 static void update_all_vols(UAContext *ua)
428 {
429    int i, num_pools;
430    uint32_t *ids;
431    POOL_DBR pr;
432    MEDIA_DBR mr;
433
434    memset(&pr, 0, sizeof(pr));
435
436    if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
437       ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db));
438       return;
439    }
440
441    for (i=0; i<num_pools; i++) {
442       pr.PoolId = ids[i];
443       if (!db_get_pool_numvols(ua->jcr, ua->db, &pr)) { /* ***FIXME*** use acl? */
444          ua->warning_msg(_("Updating all pools, but skipped PoolId=%d. ERR=%s\n"), db_strerror(ua->db));
445          continue;
446       }
447
448       set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
449       mr.PoolId = pr.PoolId;
450
451       if (!db_update_media_defaults(ua->jcr, ua->db, &mr)) {
452          ua->error_msg(_("Error updating Volume records: ERR=%s"), db_strerror(ua->db));
453       } else {
454          ua->info_msg(_("All Volume defaults updated from \"%s\" Pool record.\n"),
455             pr.Name);
456       }
457    }
458
459    free(ids);
460 }
461
462 static void update_volenabled(UAContext *ua, char *val, MEDIA_DBR *mr)
463 {
464    mr->Enabled = get_enabled(ua, val);
465    if (mr->Enabled < 0) {
466       return;
467    }
468    set_storageid_in_mr(NULL, mr);
469    if (!db_update_media_record(ua->jcr, ua->db, mr)) {
470       ua->error_msg(_("Error updating media record Enabled: ERR=%s"),
471                     db_strerror(ua->db));
472    } else {
473       ua->info_msg(_("New Enabled is: %d\n"), mr->Enabled);
474    }
475 }
476
477 static void update_vol_actiononpurge(UAContext *ua, char *val, MEDIA_DBR *mr)
478 {
479    POOL_MEM ret;
480    if (strcasecmp(val, "truncate") == 0) {
481       mr->ActionOnPurge = ON_PURGE_TRUNCATE;
482    } else {
483       mr->ActionOnPurge = 0;
484    }
485
486    set_storageid_in_mr(NULL, mr);
487    if (!db_update_media_record(ua->jcr, ua->db, mr)) {
488       ua->error_msg(_("Error updating media record ActionOnPurge: ERR=%s"),
489                     db_strerror(ua->db));
490    } else {
491       ua->info_msg(_("New ActionOnPurge is: %s\n"),
492                    action_on_purge_to_string(mr->ActionOnPurge, ret));
493    }
494 }
495
496 /*
497  * Update a media record -- allows you to change the
498  *  Volume status. E.g. if you want Bacula to stop
499  *  writing on the volume, set it to anything other
500  *  than Append.
501  */
502 static int update_volume(UAContext *ua)
503 {
504    MEDIA_DBR mr;
505    POOL *pool;
506    POOL_DBR pr;
507    POOLMEM *query;
508    POOL_MEM ret;
509    char buf[1000];
510    char ed1[130];
511    bool done = false;
512    int i;
513    const char *kw[] = {
514       NT_("VolStatus"),                /* 0 */
515       NT_("VolRetention"),             /* 1 */
516       NT_("VolUse"),                   /* 2 */
517       NT_("MaxVolJobs"),               /* 3 */
518       NT_("MaxVolFiles"),              /* 4 */
519       NT_("MaxVolBytes"),              /* 5 */
520       NT_("Recycle"),                  /* 6 */
521       NT_("InChanger"),                /* 7 */
522       NT_("Slot"),                     /* 8 */
523       NT_("Pool"),                     /* 9 */
524       NT_("FromPool"),                 /* 10 */
525       NT_("AllFromPool"),              /* 11 !!! see below !!! */
526       NT_("Enabled"),                  /* 12 */
527       NT_("RecyclePool"),              /* 13 */
528       NT_("ActionOnPurge"),            /* 14 */
529       NULL };
530
531 #define AllFromPool 11               /* keep this updated with above */
532
533    for (i=0; kw[i]; i++) {
534       int j;
535       POOL_DBR pr;
536
537       if ((j=find_arg_with_value(ua, kw[i])) > 0) {
538          /* If all from pool don't select a media record */
539          if (i != AllFromPool && !select_media_dbr(ua, &mr)) {
540             return 0;
541          }
542          switch (i) {
543          case 0:
544             update_volstatus(ua, ua->argv[j], &mr);
545             break;
546          case 1:
547             update_volretention(ua, ua->argv[j], &mr);
548             break;
549          case 2:
550             update_voluseduration(ua, ua->argv[j], &mr);
551             break;
552          case 3:
553             update_volmaxjobs(ua, ua->argv[j], &mr);
554             break;
555          case 4:
556             update_volmaxfiles(ua, ua->argv[j], &mr);
557             break;
558          case 5:
559             update_volmaxbytes(ua, ua->argv[j], &mr);
560             break;
561          case 6:
562             update_volrecycle(ua, ua->argv[j], &mr);
563             break;
564          case 7:
565             update_volinchanger(ua, ua->argv[j], &mr);
566             break;
567          case 8:
568             update_volslot(ua, ua->argv[j], &mr);
569             break;
570          case 9:
571             memset(&pr, 0, sizeof(POOL_DBR));
572             pr.PoolId = mr.PoolId;
573             if (!db_get_pool_numvols(ua->jcr, ua->db, &pr)) {
574                ua->error_msg("%s", db_strerror(ua->db));
575                break;
576             }
577             update_vol_pool(ua, ua->argv[j], &mr, &pr);
578             break;
579          case 10:
580             update_vol_from_pool(ua, &mr);
581             return 1;
582          case 11:
583             update_all_vols_from_pool(ua, ua->argv[j]);
584             return 1;
585          case 12:
586             update_volenabled(ua, ua->argv[j], &mr);
587             break;
588          case 13:
589             update_vol_recyclepool(ua, ua->argv[j], &mr);
590             break;
591          case 14:
592             update_vol_actiononpurge(ua, ua->argv[j], &mr);
593             break;
594          }
595          done = true;
596       }
597    }
598
599    /* Allow user to simply update all volumes */
600    if (find_arg(ua, NT_("fromallpools")) > 0) {
601       update_all_vols(ua);
602       return 1;
603    }
604
605    for ( ; !done; ) {
606       start_prompt(ua, _("Parameters to modify:\n"));
607       add_prompt(ua, _("Volume Status"));              /* 0 */
608       add_prompt(ua, _("Volume Retention Period"));    /* 1 */
609       add_prompt(ua, _("Volume Use Duration"));        /* 2 */
610       add_prompt(ua, _("Maximum Volume Jobs"));        /* 3 */
611       add_prompt(ua, _("Maximum Volume Files"));       /* 4 */
612       add_prompt(ua, _("Maximum Volume Bytes"));       /* 5 */
613       add_prompt(ua, _("Recycle Flag"));               /* 6 */
614       add_prompt(ua, _("Slot"));                       /* 7 */
615       add_prompt(ua, _("InChanger Flag"));             /* 8 */
616       add_prompt(ua, _("Volume Files"));               /* 9 */
617       add_prompt(ua, _("Pool"));                       /* 10 */
618       add_prompt(ua, _("Volume from Pool"));           /* 11 */
619       add_prompt(ua, _("All Volumes from Pool"));      /* 12 */
620       add_prompt(ua, _("All Volumes from all Pools")); /* 13 */
621       add_prompt(ua, _("Enabled")),                    /* 14 */
622       add_prompt(ua, _("RecyclePool")),                /* 15 */
623       add_prompt(ua, _("Action On Purge")),            /* 16 */
624       add_prompt(ua, _("Done"));                       /* 17 */
625       i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0);
626
627       /* For All Volumes, All Volumes from Pool, and Done, we don't need
628            * a Volume record */
629       if ( i != 12 && i != 13 && i != 17) {
630          if (!select_media_dbr(ua, &mr)) {  /* Get Volume record */
631             return 0;
632          }
633          ua->info_msg(_("Updating Volume \"%s\"\n"), mr.VolumeName);
634       }
635       switch (i) {
636       case 0:                         /* Volume Status */
637          /* Modify Volume Status */
638          ua->info_msg(_("Current Volume status is: %s\n"), mr.VolStatus);
639          start_prompt(ua, _("Possible Values are:\n"));
640          add_prompt(ua, NT_("Append"));
641          add_prompt(ua, NT_("Archive"));
642          add_prompt(ua, NT_("Disabled"));
643          add_prompt(ua, NT_("Full"));
644          add_prompt(ua, NT_("Used"));
645          add_prompt(ua, NT_("Cleaning"));
646          if (strcmp(mr.VolStatus, NT_("Purged")) == 0) {
647             add_prompt(ua, NT_("Recycle"));
648          }
649          add_prompt(ua, NT_("Read-Only"));
650          if (do_prompt(ua, "", _("Choose new Volume Status"), ua->cmd, sizeof(mr.VolStatus)) < 0) {
651             return 1;
652          }
653          update_volstatus(ua, ua->cmd, &mr);
654          break;
655       case 1:                         /* Retention */
656          ua->info_msg(_("Current retention period is: %s\n"),
657             edit_utime(mr.VolRetention, ed1, sizeof(ed1)));
658          if (!get_cmd(ua, _("Enter Volume Retention period: "))) {
659             return 0;
660          }
661          update_volretention(ua, ua->cmd, &mr);
662          break;
663
664       case 2:                         /* Use Duration */
665          ua->info_msg(_("Current use duration is: %s\n"),
666             edit_utime(mr.VolUseDuration, ed1, sizeof(ed1)));
667          if (!get_cmd(ua, _("Enter Volume Use Duration: "))) {
668             return 0;
669          }
670          update_voluseduration(ua, ua->cmd, &mr);
671          break;
672
673       case 3:                         /* Max Jobs */
674          ua->info_msg(_("Current max jobs is: %u\n"), mr.MaxVolJobs);
675          if (!get_pint(ua, _("Enter new Maximum Jobs: "))) {
676             return 0;
677          }
678          update_volmaxjobs(ua, ua->cmd, &mr);
679          break;
680
681       case 4:                         /* Max Files */
682          ua->info_msg(_("Current max files is: %u\n"), mr.MaxVolFiles);
683          if (!get_pint(ua, _("Enter new Maximum Files: "))) {
684             return 0;
685          }
686          update_volmaxfiles(ua, ua->cmd, &mr);
687          break;
688
689       case 5:                         /* Max Bytes */
690          ua->info_msg(_("Current value is: %s\n"), edit_uint64(mr.MaxVolBytes, ed1));
691          if (!get_cmd(ua, _("Enter new Maximum Bytes: "))) {
692             return 0;
693          }
694          update_volmaxbytes(ua, ua->cmd, &mr);
695          break;
696
697
698       case 6:                         /* Recycle */
699          ua->info_msg(_("Current recycle flag is: %s\n"),
700             mr.Recycle==1?_("yes"):_("no"));
701          if (!get_yesno(ua, _("Enter new Recycle status: "))) {
702             return 0;
703          }
704          update_volrecycle(ua, ua->cmd, &mr);
705          break;
706
707       case 7:                         /* Slot */
708          ua->info_msg(_("Current Slot is: %d\n"), mr.Slot);
709          if (!get_pint(ua, _("Enter new Slot: "))) {
710             return 0;
711          }
712          update_volslot(ua, ua->cmd, &mr);
713          break;
714
715       case 8:                         /* InChanger */
716          ua->info_msg(_("Current InChanger flag is: %d\n"), mr.InChanger);
717          bsnprintf(buf, sizeof(buf), _("Set InChanger flag for Volume \"%s\": yes/no: "),
718             mr.VolumeName);
719          if (!get_yesno(ua, buf)) {
720             return 0;
721          }
722          mr.InChanger = ua->pint32_val;
723          /*
724           * Make sure to use db_update... rather than doing this directly,
725           *   so that any Slot is handled correctly.
726           */
727          set_storageid_in_mr(NULL, &mr);
728          if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
729             ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
730          } else {
731             ua->info_msg(_("New InChanger flag is: %d\n"), mr.InChanger);
732          }
733          break;
734
735
736       case 9:                         /* Volume Files */
737          int32_t VolFiles;
738          ua->warning_msg(_("Warning changing Volume Files can result\n"
739                         "in loss of data on your Volume\n\n"));
740          ua->info_msg(_("Current Volume Files is: %u\n"), mr.VolFiles);
741          if (!get_pint(ua, _("Enter new number of Files for Volume: "))) {
742             return 0;
743          }
744          VolFiles = ua->pint32_val;
745          if (VolFiles != (int)(mr.VolFiles + 1)) {
746             ua->warning_msg(_("Normally, you should only increase Volume Files by one!\n"));
747             if (!get_yesno(ua, _("Increase Volume Files? (yes/no): ")) || ua->pint32_val == 0) {
748                break;
749             }
750          }
751          query = get_pool_memory(PM_MESSAGE);
752          Mmsg(query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%s",
753             VolFiles, edit_int64(mr.MediaId, ed1));
754          if (!db_sql_query(ua->db, query, NULL, NULL)) {
755             ua->error_msg("%s", db_strerror(ua->db));
756          } else {
757             ua->info_msg(_("New Volume Files is: %u\n"), VolFiles);
758          }
759          free_pool_memory(query);
760          break;
761
762       case 10:                        /* Volume's Pool */
763          memset(&pr, 0, sizeof(POOL_DBR));
764          pr.PoolId = mr.PoolId;
765          if (!db_get_pool_numvols(ua->jcr, ua->db, &pr)) {
766             ua->error_msg("%s", db_strerror(ua->db));
767             return 0;
768          }
769          ua->info_msg(_("Current Pool is: %s\n"), pr.Name);
770          if (!get_cmd(ua, _("Enter new Pool name: "))) {
771             return 0;
772          }
773          update_vol_pool(ua, ua->cmd, &mr, &pr);
774          return 1;
775
776       case 11:
777          update_vol_from_pool(ua, &mr);
778          return 1;
779       case 12:
780          pool = select_pool_resource(ua);
781          if (pool) {
782             update_all_vols_from_pool(ua, pool->name());
783          }
784          return 1;
785
786       case 13:
787          update_all_vols(ua);
788          return 1;
789
790       case 14:
791          ua->info_msg(_("Current Enabled is: %d\n"), mr.Enabled);
792          if (!get_cmd(ua, _("Enter new Enabled: "))) {
793             return 0;
794          }
795          update_volenabled(ua, ua->cmd, &mr);
796          break;
797
798       case 15:
799          memset(&pr, 0, sizeof(POOL_DBR));
800          pr.PoolId = mr.RecyclePoolId;
801          if (db_get_pool_numvols(ua->jcr, ua->db, &pr)) {
802             ua->info_msg(_("Current RecyclePool is: %s\n"), pr.Name);
803          } else {
804             ua->info_msg(_("No current RecyclePool\n"));
805          }
806          if (!select_pool_dbr(ua, &pr, NT_("recyclepool"))) {
807             return 0;
808          }
809          update_vol_recyclepool(ua, pr.Name, &mr);
810          return 1;
811
812       case 16:
813          pm_strcpy(ret, "");
814          ua->info_msg(_("Current ActionOnPurge is: %s\n"),
815                       action_on_purge_to_string(mr.ActionOnPurge, ret));
816          if (!get_cmd(ua, _("Enter new ActionOnPurge (one of: Truncate, None): "))) {
817             return 0;
818          }
819
820          update_vol_actiononpurge(ua, ua->cmd, &mr);
821          break;
822
823       default:                        /* Done or error */
824          ua->info_msg(_("Selection terminated.\n"));
825          return 1;
826       }
827    }
828    return 1;
829 }
830
831 /*
832  * Update long term statistics
833  */
834 static bool update_stats(UAContext *ua)
835 {
836    int i = find_arg_with_value(ua, NT_("days"));
837    utime_t since=0;
838
839    if (i >= 0) {
840       since = atoi(ua->argv[i]) * 24*60*60;
841    }
842
843    int nb = db_update_stats(ua->jcr, ua->db, since);
844    ua->info_msg(_("Updating %i job(s).\n"), nb);
845
846    return true;
847 }
848
849 /*
850  * Update pool record -- pull info from current POOL resource
851  */
852 static bool update_pool(UAContext *ua)
853 {
854    POOL_DBR  pr;
855    int id;
856    POOL *pool;
857    POOLMEM *query;
858    char ed1[50];
859
860    pool = get_pool_resource(ua);
861    if (!pool) {
862       return false;
863    }
864
865    memset(&pr, 0, sizeof(pr));
866    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
867    if (!get_pool_dbr(ua, &pr)) {
868       return false;
869    }
870
871    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE); /* update */
872    set_pooldbr_references(ua->jcr, ua->db, &pr, pool);
873
874    id = db_update_pool_record(ua->jcr, ua->db, &pr);
875    if (id <= 0) {
876       ua->error_msg(_("db_update_pool_record returned %d. ERR=%s\n"),
877          id, db_strerror(ua->db));
878    }
879    query = get_pool_memory(PM_MESSAGE);
880    Mmsg(query, list_pool, edit_int64(pr.PoolId, ed1));
881    db_list_sql_query(ua->jcr, ua->db, query, prtit, ua, 1, HORZ_LIST);
882    free_pool_memory(query);
883    ua->info_msg(_("Pool DB record updated from resource.\n"));
884    return true;
885 }
886
887 /*
888  * Update a Job record -- allows you to change the
889  *  date fields in a Job record. This helps when
890  *  providing migration from other vendors.
891  */
892 static bool update_job(UAContext *ua)
893 {
894    int i;
895    char ed1[50], ed2[50];
896    POOL_MEM cmd(PM_MESSAGE);
897    JOB_DBR jr;
898    CLIENT_DBR cr;
899    utime_t StartTime;
900    char *client_name = NULL;
901    char *start_time = NULL;
902    const char *kw[] = {
903       NT_("starttime"),                   /* 0 */
904       NT_("client"),                      /* 1 */
905       NULL };
906
907    Dmsg1(200, "cmd=%s\n", ua->cmd);
908    i = find_arg_with_value(ua, NT_("jobid"));
909    if (i < 0) {
910       ua->error_msg(_("Expect JobId keyword, not found.\n"));
911       return false;
912    }
913    memset(&jr, 0, sizeof(jr));
914    memset(&cr, 0, sizeof(cr));
915    jr.JobId = str_to_int64(ua->argv[i]);
916    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
917       ua->error_msg("%s", db_strerror(ua->db));
918       return false;
919    }
920
921    for (i=0; kw[i]; i++) {
922       int j;
923       if ((j=find_arg_with_value(ua, kw[i])) >= 0) {
924          switch (i) {
925          case 0:                         /* start time */
926             start_time = ua->argv[j];
927             break;
928          case 1:                         /* Client name */
929             client_name = ua->argv[j];
930             break;
931          }
932       }
933    }
934    if (!client_name && !start_time) {
935       ua->error_msg(_("Neither Client nor StartTime specified.\n"));
936       return 0;
937    }
938    if (client_name) {
939       if (!get_client_dbr(ua, &cr)) {
940          return false;
941       }
942       jr.ClientId = cr.ClientId;
943    }
944    if (start_time) {
945       utime_t delta_start;
946
947       StartTime = str_to_utime(start_time);
948       if (StartTime == 0) {
949          ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
950          return false;
951       }
952       delta_start = StartTime - jr.StartTime;
953       Dmsg3(200, "ST=%lld jr.ST=%lld delta=%lld\n", StartTime,
954             (utime_t)jr.StartTime, delta_start);
955       jr.StartTime = (time_t)StartTime;
956       jr.SchedTime += (time_t)delta_start;
957       jr.EndTime += (time_t)delta_start;
958       jr.JobTDate += delta_start;
959       /* Convert to DB times */
960       bstrutime(jr.cStartTime, sizeof(jr.cStartTime), jr.StartTime);
961       bstrutime(jr.cSchedTime, sizeof(jr.cSchedTime), jr.SchedTime);
962       bstrutime(jr.cEndTime, sizeof(jr.cEndTime), jr.EndTime);
963    }
964    Mmsg(cmd, "UPDATE Job SET ClientId=%s,StartTime='%s',SchedTime='%s',"
965              "EndTime='%s',JobTDate=%s WHERE JobId=%s",
966              edit_int64(jr.ClientId, ed1),
967              jr.cStartTime,
968              jr.cSchedTime,
969              jr.cEndTime,
970              edit_uint64(jr.JobTDate, ed1),
971              edit_int64(jr.JobId, ed2));
972    if (!db_sql_query(ua->db, cmd.c_str(), NULL, NULL)) {
973       ua->error_msg("%s", db_strerror(ua->db));
974       return false;
975    }
976    return true;
977 }