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