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