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