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