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