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