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