]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_update.c
Merge branch 'master' of ssh://bacula.git.sourceforge.net/gitroot/bacula/bacula
[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_("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"),
469                     db_strerror(ua->db));
470    } else {
471       ua->info_msg(_("New Enabled is: %d\n"), mr->Enabled);
472    }
473 }
474
475 static void update_vol_actiononpurge(UAContext *ua, char *val, MEDIA_DBR *mr)
476 {
477    POOL_MEM ret;
478    if (strcasecmp(val, "truncate") == 0) {
479       mr->ActionOnPurge = AOP_TRUNCATE;
480    } else {
481       mr->ActionOnPurge = 0;
482    }
483    
484    if (!db_update_media_record(ua->jcr, ua->db, mr)) {
485       ua->error_msg(_("Error updating media record ActionOnPurge: ERR=%s"),
486                     db_strerror(ua->db));
487    } else {
488       ua->info_msg(_("New ActionOnPurge is: %s\n"), 
489                    aop_to_str(mr->ActionOnPurge, ret));
490    }
491 }
492
493 /*
494  * Update a media record -- allows you to change the
495  *  Volume status. E.g. if you want Bacula to stop
496  *  writing on the volume, set it to anything other
497  *  than Append.
498  */
499 static int update_volume(UAContext *ua)
500 {
501    MEDIA_DBR mr;
502    POOL *pool;
503    POOL_DBR pr;
504    POOLMEM *query;
505    POOL_MEM ret;
506    char buf[1000];
507    char ed1[130];
508    bool done = false;
509    int i;
510    const char *kw[] = {
511       NT_("VolStatus"),                /* 0 */
512       NT_("VolRetention"),             /* 1 */
513       NT_("VolUse"),                   /* 2 */
514       NT_("MaxVolJobs"),               /* 3 */
515       NT_("MaxVolFiles"),              /* 4 */
516       NT_("MaxVolBytes"),              /* 5 */
517       NT_("Recycle"),                  /* 6 */
518       NT_("InChanger"),                /* 7 */
519       NT_("Slot"),                     /* 8 */
520       NT_("Pool"),                     /* 9 */
521       NT_("FromPool"),                 /* 10 */
522       NT_("AllFromPool"),              /* 11 !!! see below !!! */
523       NT_("Enabled"),                  /* 12 */
524       NT_("RecyclePool"),              /* 13 */
525       NT_("ActionOnPurge"),            /* 14 */
526       NULL };
527
528 #define AllFromPool 11               /* keep this updated with above */
529
530    for (i=0; kw[i]; i++) {
531       int j;
532       POOL_DBR pr;
533
534       if ((j=find_arg_with_value(ua, kw[i])) > 0) {
535          /* If all from pool don't select a media record */
536          if (i != AllFromPool && !select_media_dbr(ua, &mr)) {
537             return 0;
538          }
539          switch (i) {
540          case 0:
541             update_volstatus(ua, ua->argv[j], &mr);
542             break;
543          case 1:
544             update_volretention(ua, ua->argv[j], &mr);
545             break;
546          case 2:
547             update_voluseduration(ua, ua->argv[j], &mr);
548             break;
549          case 3:
550             update_volmaxjobs(ua, ua->argv[j], &mr);
551             break;
552          case 4:
553             update_volmaxfiles(ua, ua->argv[j], &mr);
554             break;
555          case 5:
556             update_volmaxbytes(ua, ua->argv[j], &mr);
557             break;
558          case 6:
559             update_volrecycle(ua, ua->argv[j], &mr);
560             break;
561          case 7:
562             update_volinchanger(ua, ua->argv[j], &mr);
563             break;
564          case 8:
565             update_volslot(ua, ua->argv[j], &mr);
566             break;
567          case 9:
568             memset(&pr, 0, sizeof(POOL_DBR));
569             pr.PoolId = mr.PoolId;
570             if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
571                ua->error_msg("%s", db_strerror(ua->db));
572                break;
573             }
574             update_vol_pool(ua, ua->argv[j], &mr, &pr);
575             break;
576          case 10:
577             update_vol_from_pool(ua, &mr);
578             return 1;
579          case 11:
580             update_all_vols_from_pool(ua, ua->argv[j]);
581             return 1;
582          case 12:
583             update_volenabled(ua, ua->argv[j], &mr);
584             break;
585          case 13:
586             update_vol_recyclepool(ua, ua->argv[j], &mr);
587             break;
588          case 14:
589             update_vol_actiononpurge(ua, ua->argv[j], &mr);
590             break;
591          }
592          done = true;
593       }
594    }
595
596    /* Allow user to simply update all volumes */
597    if (find_arg(ua, NT_("fromallpools")) > 0) {
598       update_all_vols(ua);
599       return 1;
600    }
601
602    for ( ; !done; ) {
603       start_prompt(ua, _("Parameters to modify:\n"));
604       add_prompt(ua, _("Volume Status"));              /* 0 */
605       add_prompt(ua, _("Volume Retention Period"));    /* 1 */
606       add_prompt(ua, _("Volume Use Duration"));        /* 2 */
607       add_prompt(ua, _("Maximum Volume Jobs"));        /* 3 */
608       add_prompt(ua, _("Maximum Volume Files"));       /* 4 */
609       add_prompt(ua, _("Maximum Volume Bytes"));       /* 5 */
610       add_prompt(ua, _("Recycle Flag"));               /* 6 */
611       add_prompt(ua, _("Slot"));                       /* 7 */
612       add_prompt(ua, _("InChanger Flag"));             /* 8 */
613       add_prompt(ua, _("Volume Files"));               /* 9 */
614       add_prompt(ua, _("Pool"));                       /* 10 */
615       add_prompt(ua, _("Volume from Pool"));           /* 11 */
616       add_prompt(ua, _("All Volumes from Pool"));      /* 12 */
617       add_prompt(ua, _("All Volumes from all Pools")); /* 13 */
618       add_prompt(ua, _("Enabled")),                    /* 14 */
619       add_prompt(ua, _("RecyclePool")),                /* 15 */
620       add_prompt(ua, _("Action On Purge")),            /* 16 */
621       add_prompt(ua, _("Done"));                       /* 17 */
622       i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0);  
623
624       /* For All Volumes, All Volumes from Pool, and Done, we don't need
625            * a Volume record */
626       if ( i != 12 && i != 13 && i != 17) {
627          if (!select_media_dbr(ua, &mr)) {  /* Get Volume record */
628             return 0;
629          }
630          ua->info_msg(_("Updating Volume \"%s\"\n"), mr.VolumeName);
631       }
632       switch (i) {
633       case 0:                         /* Volume Status */
634          /* Modify Volume Status */
635          ua->info_msg(_("Current Volume status is: %s\n"), mr.VolStatus);
636          start_prompt(ua, _("Possible Values are:\n"));
637          add_prompt(ua, NT_("Append")); 
638          add_prompt(ua, NT_("Archive"));
639          add_prompt(ua, NT_("Disabled"));
640          add_prompt(ua, NT_("Full"));
641          add_prompt(ua, NT_("Used"));
642          add_prompt(ua, NT_("Cleaning"));
643          if (strcmp(mr.VolStatus, NT_("Purged")) == 0) {
644             add_prompt(ua, NT_("Recycle"));
645          }
646          add_prompt(ua, NT_("Read-Only"));
647          if (do_prompt(ua, "", _("Choose new Volume Status"), ua->cmd, sizeof(mr.VolStatus)) < 0) {
648             return 1;
649          }
650          update_volstatus(ua, ua->cmd, &mr);
651          break;
652       case 1:                         /* Retention */
653          ua->info_msg(_("Current retention period is: %s\n"),
654             edit_utime(mr.VolRetention, ed1, sizeof(ed1)));
655          if (!get_cmd(ua, _("Enter Volume Retention period: "))) {
656             return 0;
657          }
658          update_volretention(ua, ua->cmd, &mr);
659          break;
660
661       case 2:                         /* Use Duration */
662          ua->info_msg(_("Current use duration is: %s\n"),
663             edit_utime(mr.VolUseDuration, ed1, sizeof(ed1)));
664          if (!get_cmd(ua, _("Enter Volume Use Duration: "))) {
665             return 0;
666          }
667          update_voluseduration(ua, ua->cmd, &mr);
668          break;
669
670       case 3:                         /* Max Jobs */
671          ua->info_msg(_("Current max jobs is: %u\n"), mr.MaxVolJobs);
672          if (!get_pint(ua, _("Enter new Maximum Jobs: "))) {
673             return 0;
674          }
675          update_volmaxjobs(ua, ua->cmd, &mr);
676          break;
677
678       case 4:                         /* Max Files */
679          ua->info_msg(_("Current max files is: %u\n"), mr.MaxVolFiles);
680          if (!get_pint(ua, _("Enter new Maximum Files: "))) {
681             return 0;
682          }
683          update_volmaxfiles(ua, ua->cmd, &mr);
684          break;
685
686       case 5:                         /* Max Bytes */
687          ua->info_msg(_("Current value is: %s\n"), edit_uint64(mr.MaxVolBytes, ed1));
688          if (!get_cmd(ua, _("Enter new Maximum Bytes: "))) {
689             return 0;
690          }
691          update_volmaxbytes(ua, ua->cmd, &mr);
692          break;
693
694
695       case 6:                         /* Recycle */
696          ua->info_msg(_("Current recycle flag is: %s\n"),
697             mr.Recycle==1?_("yes"):_("no"));
698          if (!get_yesno(ua, _("Enter new Recycle status: "))) {
699             return 0;
700          }
701          update_volrecycle(ua, ua->cmd, &mr);
702          break;
703
704       case 7:                         /* Slot */
705          ua->info_msg(_("Current Slot is: %d\n"), mr.Slot);
706          if (!get_pint(ua, _("Enter new Slot: "))) {
707             return 0;
708          }
709          update_volslot(ua, ua->cmd, &mr);
710          break;
711          
712       case 8:                         /* InChanger */
713          ua->info_msg(_("Current InChanger flag is: %d\n"), mr.InChanger);
714          bsnprintf(buf, sizeof(buf), _("Set InChanger flag for Volume \"%s\": yes/no: "),
715             mr.VolumeName);
716          if (!get_yesno(ua, buf)) {
717             return 0;
718          }
719          mr.InChanger = ua->pint32_val;
720          /*
721           * Make sure to use db_update... rather than doing this directly,
722           *   so that any Slot is handled correctly.
723           */
724          if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
725             ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
726          } else {
727             ua->info_msg(_("New InChanger flag is: %d\n"), mr.InChanger);
728          }
729          break;
730
731
732       case 9:                         /* Volume Files */
733          int32_t VolFiles;
734          ua->warning_msg(_("Warning changing Volume Files can result\n"
735                         "in loss of data on your Volume\n\n"));
736          ua->info_msg(_("Current Volume Files is: %u\n"), mr.VolFiles);
737          if (!get_pint(ua, _("Enter new number of Files for Volume: "))) {
738             return 0;
739          }
740          VolFiles = ua->pint32_val;
741          if (VolFiles != (int)(mr.VolFiles + 1)) {
742             ua->warning_msg(_("Normally, you should only increase Volume Files by one!\n"));
743             if (!get_yesno(ua, _("Increase Volume Files? (yes/no): ")) || ua->pint32_val == 0) {
744                break;
745             }
746          }
747          query = get_pool_memory(PM_MESSAGE);
748          Mmsg(query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%s",
749             VolFiles, edit_int64(mr.MediaId, ed1));
750          if (!db_sql_query(ua->db, query, NULL, NULL)) {
751             ua->error_msg("%s", db_strerror(ua->db));
752          } else {
753             ua->info_msg(_("New Volume Files is: %u\n"), VolFiles);
754          }
755          free_pool_memory(query);
756          break;
757
758       case 10:                        /* Volume's Pool */
759          memset(&pr, 0, sizeof(POOL_DBR));
760          pr.PoolId = mr.PoolId;
761          if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
762             ua->error_msg("%s", db_strerror(ua->db));
763             return 0;
764          }
765          ua->info_msg(_("Current Pool is: %s\n"), pr.Name);
766          if (!get_cmd(ua, _("Enter new Pool name: "))) {
767             return 0;
768          }
769          update_vol_pool(ua, ua->cmd, &mr, &pr);
770          return 1;
771
772       case 11:
773          update_vol_from_pool(ua, &mr);
774          return 1;
775       case 12:
776          pool = select_pool_resource(ua);
777          if (pool) {
778             update_all_vols_from_pool(ua, pool->name());
779          }
780          return 1;
781
782       case 13:
783          update_all_vols(ua);
784          return 1;
785
786       case 14:
787          ua->info_msg(_("Current Enabled is: %d\n"), mr.Enabled);
788          if (!get_cmd(ua, _("Enter new Enabled: "))) {
789             return 0;
790          }
791          if (strcasecmp(ua->cmd, "yes") == 0 || strcasecmp(ua->cmd, "true") == 0) {
792             mr.Enabled = 1;
793          } else if (strcasecmp(ua->cmd, "no") == 0 || strcasecmp(ua->cmd, "false") == 0) {
794             mr.Enabled = 0;
795          } else if (strcasecmp(ua->cmd, "archived") == 0) { 
796             mr.Enabled = 2;
797          } else {
798             mr.Enabled = atoi(ua->cmd);
799          }
800          update_volenabled(ua, ua->cmd, &mr);
801          break;
802
803       case 15:
804          memset(&pr, 0, sizeof(POOL_DBR));
805          pr.PoolId = mr.RecyclePoolId;
806          if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
807             ua->info_msg(_("Current RecyclePool is: %s\n"), pr.Name);
808          } else {
809             ua->info_msg(_("No current RecyclePool\n"));
810          }
811          if (!select_pool_dbr(ua, &pr, NT_("recyclepool"))) {
812             return 0;
813          }
814          update_vol_recyclepool(ua, pr.Name, &mr);
815          return 1;
816
817       case 16:
818          pm_strcpy(ret, "");
819          ua->info_msg(_("Current ActionOnPurge is: %s\n"), 
820                       aop_to_str(mr.ActionOnPurge, ret));
821          if (!get_cmd(ua, _("Enter new ActionOnPurge: (one of: Truncate, None) "))) {
822             return 0;
823          }
824
825          update_vol_actiononpurge(ua, ua->cmd, &mr);
826          break;
827
828       default:                        /* Done or error */
829          ua->info_msg(_("Selection terminated.\n"));
830          return 1;
831       }
832    }
833    return 1;
834 }
835
836 /*
837  * Update long term statistics
838  */
839 static bool update_stats(UAContext *ua)
840 {
841    int i = find_arg_with_value(ua, NT_("days"));
842    utime_t since=0;
843
844    if (i >= 0) {
845       since = atoi(ua->argv[i]) * 24*60*60;
846    }
847
848    int nb = db_update_stats(ua->jcr, ua->db, since);
849    ua->info_msg(_("Updating %i job(s).\n"), nb);
850
851    return true;
852 }
853
854 /*
855  * Update pool record -- pull info from current POOL resource
856  */
857 static bool update_pool(UAContext *ua)
858 {
859    POOL_DBR  pr;
860    int id;
861    POOL *pool;
862    POOLMEM *query;
863    char ed1[50];
864
865    pool = get_pool_resource(ua);
866    if (!pool) {
867       return false;
868    }
869
870    memset(&pr, 0, sizeof(pr));
871    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
872    if (!get_pool_dbr(ua, &pr)) {
873       return false;
874    }
875
876    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE); /* update */
877    set_pooldbr_references(ua->jcr, ua->db, &pr, pool);
878
879    id = db_update_pool_record(ua->jcr, ua->db, &pr);
880    if (id <= 0) {
881       ua->error_msg(_("db_update_pool_record returned %d. ERR=%s\n"),
882          id, db_strerror(ua->db));
883    }
884    query = get_pool_memory(PM_MESSAGE);
885    Mmsg(query, list_pool, edit_int64(pr.PoolId, ed1));
886    db_list_sql_query(ua->jcr, ua->db, query, prtit, ua, 1, HORZ_LIST);
887    free_pool_memory(query);
888    ua->info_msg(_("Pool DB record updated from resource.\n"));
889    return true;
890 }
891
892 /*
893  * Update a Job record -- allows you to change the
894  *  date fields in a Job record. This helps when
895  *  providing migration from other vendors.
896  */
897 static bool update_job(UAContext *ua)
898 {
899    int i;
900    char ed1[50], ed2[50];
901    POOL_MEM cmd(PM_MESSAGE);
902    JOB_DBR jr;
903    CLIENT_DBR cr;
904    utime_t StartTime;
905    char *client_name = NULL;
906    char *start_time = NULL;
907    const char *kw[] = {
908       NT_("starttime"),                   /* 0 */
909       NT_("client"),                      /* 1 */
910       NULL };
911
912    Dmsg1(200, "cmd=%s\n", ua->cmd);
913    i = find_arg_with_value(ua, NT_("jobid"));
914    if (i < 0) {
915       ua->error_msg(_("Expect JobId keyword, not found.\n"));
916       return false;
917    }
918    memset(&jr, 0, sizeof(jr));
919    memset(&cr, 0, sizeof(cr));
920    jr.JobId = str_to_int64(ua->argv[i]);
921    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
922       ua->error_msg("%s", db_strerror(ua->db));
923       return false;
924    }
925
926    for (i=0; kw[i]; i++) {
927       int j;
928       if ((j=find_arg_with_value(ua, kw[i])) >= 0) {
929          switch (i) {
930          case 0:                         /* start time */
931             start_time = ua->argv[j];
932             break;
933          case 1:                         /* Client name */
934             client_name = ua->argv[j];
935             break;
936          }
937       }
938    }
939    if (!client_name && !start_time) {
940       ua->error_msg(_("Neither Client nor StartTime specified.\n"));
941       return 0;
942    }
943    if (client_name) {
944       if (!get_client_dbr(ua, &cr)) {
945          return false;
946       }
947       jr.ClientId = cr.ClientId;
948    }
949    if (start_time) {
950       utime_t delta_start;
951
952       StartTime = str_to_utime(start_time);
953       if (StartTime == 0) {
954          ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
955          return false;
956       }
957       delta_start = StartTime - jr.StartTime;
958       Dmsg3(200, "ST=%lld jr.ST=%lld delta=%lld\n", StartTime, 
959             (utime_t)jr.StartTime, delta_start);
960       jr.StartTime = (time_t)StartTime;
961       jr.SchedTime += (time_t)delta_start;
962       jr.EndTime += (time_t)delta_start;
963       jr.JobTDate += delta_start;
964       /* Convert to DB times */
965       bstrutime(jr.cStartTime, sizeof(jr.cStartTime), jr.StartTime);
966       bstrutime(jr.cSchedTime, sizeof(jr.cSchedTime), jr.SchedTime);
967       bstrutime(jr.cEndTime, sizeof(jr.cEndTime), jr.EndTime);
968    }
969    Mmsg(cmd, "UPDATE Job SET ClientId=%s,StartTime='%s',SchedTime='%s',"
970              "EndTime='%s',JobTDate=%s WHERE JobId=%s", 
971              edit_int64(jr.ClientId, ed1), 
972              jr.cStartTime,
973              jr.cSchedTime,
974              jr.cEndTime,
975              edit_uint64(jr.JobTDate, ed1), 
976              edit_int64(jr.JobId, ed2));
977    if (!db_sql_query(ua->db, cmd.c_str(), NULL, NULL)) {
978       ua->error_msg("%s", db_strerror(ua->db));
979       return false;
980    }
981    return true;
982 }