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