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