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