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