1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2016 Planets Communications B.V.
6    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Split from ua_cmds.c March 2005
25  * Kern Sibbald, September MM
26  */
27 /**
28  * @file
29  * Update command processing
30  */
31 
32 #include "include/bareos.h"
33 #include "dird.h"
34 #include "dird/jcr_private.h"
35 #include "dird/next_vol.h"
36 #include "dird/sd_cmds.h"
37 #include "dird/storage.h"
38 #include "dird/ua_db.h"
39 #include "dird/ua_input.h"
40 #include "dird/ua_select.h"
41 #include "lib/edit.h"
42 #include "lib/util.h"
43 
44 namespace directordaemon {
45 
46 /* Forward referenced functions */
47 static bool UpdateVolume(UaContext* ua);
48 static bool UpdatePool(UaContext* ua);
49 static bool UpdateJob(UaContext* ua);
50 static bool UpdateStats(UaContext* ua);
51 static void UpdateSlots(UaContext* ua);
52 
53 /**
54  * Update a Pool Record in the database.
55  *  It is always updated from the Resource record.
56  *
57  *    update volume pool=<pool-name> volume=<volume-name>
58  *         changes pool info for volume
59  *    update pool=<pool-name>
60  *         updates pool from Pool resource
61  *    update slots[=..] [scan]
62  *         updates autochanger slots
63  *    update jobid[=..] ...
64  *         update job information
65  *    update stats [days=...]
66  *         updates long term statistics
67  */
UpdateCmd(UaContext * ua,const char * cmd)68 bool UpdateCmd(UaContext* ua, const char* cmd)
69 {
70   static const char* kw[] = {NT_("media"),  /* 0 */
71                              NT_("volume"), /* 1 */
72                              NT_("pool"),   /* 2 */
73                              NT_("slots"),  /* 3 */
74                              NT_("slot"),   /* 4 */
75                              NT_("jobid"),  /* 5 */
76                              NT_("stats"),  /* 6 */
77                              NULL};
78 
79   if (!OpenClientDb(ua)) { return true; }
80 
81   switch (FindArgKeyword(ua, kw)) {
82     case 0:
83     case 1:
84       UpdateVolume(ua);
85       return true;
86     case 2:
87       UpdatePool(ua);
88       return true;
89     case 3:
90     case 4:
91       UpdateSlots(ua);
92       return true;
93     case 5:
94       UpdateJob(ua);
95       return true;
96     case 6:
97       UpdateStats(ua);
98       return true;
99     default:
100       break;
101   }
102 
103   StartPrompt(ua, _("Update choice:\n"));
104   AddPrompt(ua, _("Volume parameters"));
105   AddPrompt(ua, _("Pool from resource"));
106   AddPrompt(ua, _("Slots from autochanger"));
107   AddPrompt(ua, _("Long term statistics"));
108   switch (
109       DoPrompt(ua, _("item"), _("Choose catalog item to update"), NULL, 0)) {
110     case 0:
111       UpdateVolume(ua);
112       break;
113     case 1:
114       UpdatePool(ua);
115       break;
116     case 2:
117       UpdateSlots(ua);
118       break;
119     case 3:
120       UpdateStats(ua);
121       break;
122     default:
123       break;
124   }
125   return true;
126 }
127 
UpdateVolstatus(UaContext * ua,const char * val,MediaDbRecord * mr)128 static void UpdateVolstatus(UaContext* ua, const char* val, MediaDbRecord* mr)
129 {
130   PoolMem query(PM_MESSAGE);
131   const char* kw[] = {NT_("Append"),   NT_("Archive"),
132                       NT_("Disabled"), NT_("Full"),
133                       NT_("Used"),     NT_("Cleaning"),
134                       NT_("Recycle"),  NT_("Read-Only"),
135                       NT_("Error"),    NULL};
136   bool found = false;
137   int i;
138 
139   for (i = 0; kw[i]; i++) {
140     if (Bstrcasecmp(val, kw[i])) {
141       found = true;
142       break;
143     }
144   }
145   if (!found) {
146     ua->ErrorMsg(_("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 (!ua->db->SqlQuery(query.c_str())) {
153       ua->ErrorMsg("%s", ua->db->strerror());
154     } else {
155       ua->InfoMsg(_("New Volume status is: %s\n"), mr->VolStatus);
156     }
157   }
158 }
159 
UpdateVolretention(UaContext * ua,char * val,MediaDbRecord * mr)160 static void UpdateVolretention(UaContext* ua, char* val, MediaDbRecord* mr)
161 {
162   char ed1[150], ed2[50];
163   PoolMem query(PM_MESSAGE);
164 
165   if (!DurationToUtime(val, &mr->VolRetention)) {
166     ua->ErrorMsg(_("Invalid retention period specified: %s\n"), val);
167     return;
168   }
169   Mmsg(query, "UPDATE Media SET VolRetention=%s WHERE MediaId=%s",
170        edit_uint64(mr->VolRetention, ed1), edit_int64(mr->MediaId, ed2));
171   if (!ua->db->SqlQuery(query.c_str())) {
172     ua->ErrorMsg("%s", ua->db->strerror());
173   } else {
174     ua->InfoMsg(_("New retention period is: %s\n"),
175                 edit_utime(mr->VolRetention, ed1, sizeof(ed1)));
176   }
177 }
178 
UpdateVoluseduration(UaContext * ua,char * val,MediaDbRecord * mr)179 static void UpdateVoluseduration(UaContext* ua, char* val, MediaDbRecord* mr)
180 {
181   char ed1[150], ed2[50];
182   PoolMem query(PM_MESSAGE);
183 
184   if (!DurationToUtime(val, &mr->VolUseDuration)) {
185     ua->ErrorMsg(_("Invalid use duration specified: %s\n"), val);
186     return;
187   }
188   Mmsg(query, "UPDATE Media SET VolUseDuration=%s WHERE MediaId=%s",
189        edit_uint64(mr->VolUseDuration, ed1), edit_int64(mr->MediaId, ed2));
190   if (!ua->db->SqlQuery(query.c_str())) {
191     ua->ErrorMsg("%s", ua->db->strerror());
192   } else {
193     ua->InfoMsg(_("New use duration is: %s\n"),
194                 edit_utime(mr->VolUseDuration, ed1, sizeof(ed1)));
195   }
196 }
197 
UpdateVolmaxjobs(UaContext * ua,char * val,MediaDbRecord * mr)198 static void UpdateVolmaxjobs(UaContext* ua, char* val, MediaDbRecord* mr)
199 {
200   PoolMem query(PM_MESSAGE);
201   char ed1[50];
202 
203   Mmsg(query, "UPDATE Media SET MaxVolJobs=%s WHERE MediaId=%s", val,
204        edit_int64(mr->MediaId, ed1));
205   if (!ua->db->SqlQuery(query.c_str())) {
206     ua->ErrorMsg("%s", ua->db->strerror());
207   } else {
208     ua->InfoMsg(_("New max jobs is: %s\n"), val);
209   }
210 }
211 
UpdateVolmaxfiles(UaContext * ua,char * val,MediaDbRecord * mr)212 static void UpdateVolmaxfiles(UaContext* ua, char* val, MediaDbRecord* mr)
213 {
214   PoolMem query(PM_MESSAGE);
215   char ed1[50];
216 
217   Mmsg(query, "UPDATE Media SET MaxVolFiles=%s WHERE MediaId=%s", val,
218        edit_int64(mr->MediaId, ed1));
219   if (!ua->db->SqlQuery(query.c_str())) {
220     ua->ErrorMsg("%s", ua->db->strerror());
221   } else {
222     ua->InfoMsg(_("New max files is: %s\n"), val);
223   }
224 }
225 
UpdateVolmaxbytes(UaContext * ua,char * val,MediaDbRecord * mr)226 static void UpdateVolmaxbytes(UaContext* ua, char* val, MediaDbRecord* mr)
227 {
228   uint64_t maxbytes;
229   char ed1[50], ed2[50];
230   PoolMem query(PM_MESSAGE);
231 
232   if (!size_to_uint64(val, &maxbytes)) {
233     ua->ErrorMsg(_("Invalid max. bytes specification: %s\n"), val);
234     return;
235   }
236   Mmsg(query, "UPDATE Media SET MaxVolBytes=%s WHERE MediaId=%s",
237        edit_uint64(maxbytes, ed1), edit_int64(mr->MediaId, ed2));
238   if (!ua->db->SqlQuery(query.c_str())) {
239     ua->ErrorMsg("%s", ua->db->strerror());
240   } else {
241     ua->InfoMsg(_("New Max bytes is: %s\n"), edit_uint64(maxbytes, ed1));
242   }
243 }
244 
UpdateVolrecycle(UaContext * ua,char * val,MediaDbRecord * mr)245 static void UpdateVolrecycle(UaContext* ua, char* val, MediaDbRecord* mr)
246 {
247   bool recycle;
248   char ed1[50];
249   PoolMem query(PM_MESSAGE);
250 
251   if (!IsYesno(val, &recycle)) {
252     ua->ErrorMsg(_("Invalid value. It must be yes or no.\n"));
253     return;
254   }
255 
256   Mmsg(query, "UPDATE Media SET Recycle=%d WHERE MediaId=%s", recycle ? 1 : 0,
257        edit_int64(mr->MediaId, ed1));
258 
259   if (!ua->db->SqlQuery(query.c_str())) {
260     ua->ErrorMsg("%s", ua->db->strerror());
261   } else {
262     ua->InfoMsg(_("New Recycle flag is: %s\n"), recycle ? _("yes") : _("no"));
263   }
264 }
265 
UpdateVolinchanger(UaContext * ua,char * val,MediaDbRecord * mr)266 static void UpdateVolinchanger(UaContext* ua, char* val, MediaDbRecord* mr)
267 {
268   char ed1[50];
269   bool InChanger;
270   PoolMem query(PM_MESSAGE);
271 
272   if (!IsYesno(val, &InChanger)) {
273     ua->ErrorMsg(_("Invalid value. It must be yes or no.\n"));
274     return;
275   }
276 
277   Mmsg(query, "UPDATE Media SET InChanger=%d WHERE MediaId=%s",
278        InChanger ? 1 : 0, edit_int64(mr->MediaId, ed1));
279 
280   if (!ua->db->SqlQuery(query.c_str())) {
281     ua->ErrorMsg("%s", ua->db->strerror());
282   } else {
283     ua->InfoMsg(_("New InChanger flag is: %s\n"),
284                 InChanger ? _("yes") : _("no"));
285   }
286 }
287 
UpdateVolslot(UaContext * ua,char * val,MediaDbRecord * mr)288 static void UpdateVolslot(UaContext* ua, char* val, MediaDbRecord* mr)
289 {
290   PoolDbRecord pr;
291 
292   pr.PoolId = mr->PoolId;
293   if (!ua->db->GetPoolRecord(ua->jcr, &pr)) {
294     ua->ErrorMsg("%s", ua->db->strerror());
295     return;
296   }
297   mr->Slot = atoi(val);
298   if (pr.MaxVols > 0 && mr->Slot > (int)pr.MaxVols) {
299     ua->ErrorMsg(_("Invalid slot, it must be between 0 and MaxVols=%d\n"),
300                  pr.MaxVols);
301     return;
302   }
303   /*
304    * Make sure to use db_update... rather than doing this directly,
305    * so that any Slot is handled correctly.
306    */
307   SetStorageidInMr(NULL, mr);
308   if (!ua->db->UpdateMediaRecord(ua->jcr, mr)) {
309     ua->ErrorMsg(_("Error updating media record Slot: ERR=%s"),
310                  ua->db->strerror());
311   } else {
312     ua->InfoMsg(_("New Slot is: %d\n"), mr->Slot);
313   }
314 }
315 
316 /**
317  * Modify the Pool in which this Volume is located
318  */
UpdateVolPool(UaContext * ua,char * val,MediaDbRecord * mr,PoolDbRecord * opr)319 void UpdateVolPool(UaContext* ua,
320                    char* val,
321                    MediaDbRecord* mr,
322                    PoolDbRecord* opr)
323 {
324   PoolDbRecord pr;
325   PoolMem query(PM_MESSAGE);
326   char ed1[50], ed2[50];
327 
328   bstrncpy(pr.Name, val, sizeof(pr.Name));
329   if (!GetPoolDbr(ua, &pr)) { return; }
330   mr->PoolId = pr.PoolId; /* set new PoolId */
331 
332   DbLock(ua->db);
333   Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
334        edit_int64(mr->PoolId, ed1), edit_int64(mr->MediaId, ed2));
335   if (!ua->db->SqlQuery(query.c_str())) {
336     ua->ErrorMsg("%s", ua->db->strerror());
337   } else {
338     ua->InfoMsg(_("New Pool is: %s\n"), pr.Name);
339     opr->NumVols--;
340     if (!ua->db->UpdatePoolRecord(ua->jcr, opr)) {
341       ua->ErrorMsg("%s", ua->db->strerror());
342     }
343     pr.NumVols++;
344     if (!ua->db->UpdatePoolRecord(ua->jcr, &pr)) {
345       ua->ErrorMsg("%s", ua->db->strerror());
346     }
347   }
348   DbUnlock(ua->db);
349 }
350 
351 /**
352  * Modify the RecyclePool of a Volume
353  */
UpdateVolRecyclepool(UaContext * ua,char * val,MediaDbRecord * mr)354 void UpdateVolRecyclepool(UaContext* ua, char* val, MediaDbRecord* mr)
355 {
356   PoolDbRecord pr;
357   PoolMem query(PM_MESSAGE);
358   char ed1[50], ed2[50];
359   const char* poolname;
360 
361   if (val && *val) { /* update volume recyclepool="Scratch" */
362     /*
363      * If a pool name is given, look up the PoolId
364      */
365     bstrncpy(pr.Name, val, sizeof(pr.Name));
366     if (!GetPoolDbr(ua, &pr, NT_("recyclepool"))) { return; }
367     mr->RecyclePoolId = pr.PoolId; /* get the PoolId */
368     poolname = pr.Name;
369   } else { /* update volume recyclepool="" */
370     /*
371      * If no pool name is given, set the PoolId to 0 (the default)
372      */
373     mr->RecyclePoolId = 0;
374     poolname = _("*None*");
375   }
376 
377   DbLock(ua->db);
378   Mmsg(query, "UPDATE Media SET RecyclePoolId=%s WHERE MediaId=%s",
379        edit_int64(mr->RecyclePoolId, ed1), edit_int64(mr->MediaId, ed2));
380   if (!ua->db->SqlQuery(query.c_str())) {
381     ua->ErrorMsg("%s", ua->db->strerror());
382   } else {
383     ua->InfoMsg(_("New RecyclePool is: %s\n"), poolname);
384   }
385   DbUnlock(ua->db);
386 }
387 
388 /**
389  * Modify the Storage in which this Volume is located
390  */
UpdateVolStorage(UaContext * ua,char * val,MediaDbRecord * mr)391 void UpdateVolStorage(UaContext* ua, char* val, MediaDbRecord* mr)
392 {
393   StorageDbRecord sr;
394   PoolMem query(PM_MESSAGE);
395   char ed1[50], ed2[50];
396 
397   bstrncpy(sr.Name, val, sizeof(sr.Name));
398   if (!GetStorageDbr(ua, &sr)) { return; }
399   mr->StorageId = sr.StorageId; /* set new StorageId */
400 
401   DbLock(ua->db);
402   Mmsg(query, "UPDATE Media SET StorageId=%s WHERE MediaId=%s",
403        edit_int64(mr->StorageId, ed1), edit_int64(mr->MediaId, ed2));
404   if (!ua->db->SqlQuery(query.c_str())) {
405     ua->ErrorMsg("%s", ua->db->strerror());
406   }
407 
408   DbUnlock(ua->db);
409 }
410 
411 /**
412  * Refresh the Volume information from the Pool record
413  */
UpdateVolFromPool(UaContext * ua,MediaDbRecord * mr)414 static void UpdateVolFromPool(UaContext* ua, MediaDbRecord* mr)
415 {
416   PoolDbRecord pr;
417 
418   pr.PoolId = mr->PoolId;
419   if (!ua->db->GetPoolRecord(ua->jcr, &pr) ||
420       !ua->AclAccessOk(Pool_ACL, pr.Name, true)) {
421     return;
422   }
423   SetPoolDbrDefaultsInMediaDbr(mr, &pr);
424   if (!ua->db->UpdateMediaDefaults(ua->jcr, mr)) {
425     ua->ErrorMsg(_("Error updating Volume record: ERR=%s"), ua->db->strerror());
426   } else {
427     ua->InfoMsg(_("Volume defaults updated from \"%s\" Pool record.\n"),
428                 pr.Name);
429   }
430 }
431 
432 /**
433  * Refresh the Volume information from the Pool record for all Volumes
434  */
UpdateAllVolsFromPool(UaContext * ua,const char * pool_name)435 static void UpdateAllVolsFromPool(UaContext* ua, const char* pool_name)
436 {
437   PoolDbRecord pr;
438   MediaDbRecord mr;
439 
440   bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
441   if (!GetPoolDbr(ua, &pr)) { return; }
442   SetPoolDbrDefaultsInMediaDbr(&mr, &pr);
443   mr.PoolId = pr.PoolId;
444   if (!ua->db->UpdateMediaDefaults(ua->jcr, &mr)) {
445     ua->ErrorMsg(_("Error updating Volume records: ERR=%s"),
446                  ua->db->strerror());
447   } else {
448     ua->InfoMsg(_("All Volume defaults updated from \"%s\" Pool record.\n"),
449                 pr.Name);
450   }
451 }
452 
UpdateAllVols(UaContext * ua)453 static void UpdateAllVols(UaContext* ua)
454 {
455   int i, num_pools;
456   uint32_t* ids;
457   PoolDbRecord pr;
458   MediaDbRecord mr;
459 
460   if (!ua->db->GetPoolIds(ua->jcr, &num_pools, &ids)) {
461     ua->ErrorMsg(_("Error obtaining pool ids. ERR=%s\n"), ua->db->strerror());
462     return;
463   }
464 
465   for (i = 0; i < num_pools; i++) {
466     pr.PoolId = ids[i];
467     if (!ua->db->GetPoolRecord(ua->jcr, &pr)) {
468       ua->WarningMsg(_("Updating all pools, but skipped PoolId=%d. ERR=%s\n"),
469                      ua->db->strerror());
470       continue;
471     }
472 
473     /*
474      * Check access to pool.
475      */
476     if (!ua->AclAccessOk(Pool_ACL, pr.Name, false)) { continue; }
477 
478     SetPoolDbrDefaultsInMediaDbr(&mr, &pr);
479     mr.PoolId = pr.PoolId;
480 
481     if (!ua->db->UpdateMediaDefaults(ua->jcr, &mr)) {
482       ua->ErrorMsg(_("Error updating Volume records: ERR=%s"),
483                    ua->db->strerror());
484     } else {
485       ua->InfoMsg(_("All Volume defaults updated from \"%s\" Pool record.\n"),
486                   pr.Name);
487     }
488   }
489 
490   free(ids);
491 }
492 
UpdateVolenabled(UaContext * ua,char * val,MediaDbRecord * mr)493 static void UpdateVolenabled(UaContext* ua, char* val, MediaDbRecord* mr)
494 {
495   mr->Enabled = GetEnabled(ua, val);
496   if (mr->Enabled < 0) { return; }
497   SetStorageidInMr(NULL, mr);
498   if (!ua->db->UpdateMediaRecord(ua->jcr, mr)) {
499     ua->ErrorMsg(_("Error updating media record Enabled: ERR=%s"),
500                  ua->db->strerror());
501   } else {
502     ua->InfoMsg(_("New Enabled is: %d\n"), mr->Enabled);
503   }
504 }
505 
UpdateVolActiononpurge(UaContext * ua,char * val,MediaDbRecord * mr)506 static void UpdateVolActiononpurge(UaContext* ua, char* val, MediaDbRecord* mr)
507 {
508   PoolMem ret;
509   if (Bstrcasecmp(val, "truncate")) {
510     mr->ActionOnPurge = ON_PURGE_TRUNCATE;
511   } else {
512     mr->ActionOnPurge = 0;
513   }
514 
515   SetStorageidInMr(NULL, mr);
516   if (!ua->db->UpdateMediaRecord(ua->jcr, mr)) {
517     ua->ErrorMsg(_("Error updating media record ActionOnPurge: ERR=%s"),
518                  ua->db->strerror());
519   } else {
520     ua->InfoMsg(_("New ActionOnPurge is: %s\n"),
521                 action_on_purge_to_string(mr->ActionOnPurge, ret));
522   }
523 }
524 
525 /**
526  * Update a media record -- allows you to change the
527  *  Volume status. E.g. if you want BAREOS to stop
528  *  writing on the volume, set it to anything other
529  *  than Append.
530  */
UpdateVolume(UaContext * ua)531 static bool UpdateVolume(UaContext* ua)
532 {
533   PoolResource* pool;
534   POOLMEM* query;
535   PoolMem ret;
536   char buf[1000];
537   char ed1[130];
538   bool done = false;
539   int i;
540   const char* kw[] = {NT_("VolStatus"),     /* 0 */
541                       NT_("VolRetention"),  /* 1 */
542                       NT_("VolUse"),        /* 2 */
543                       NT_("MaxVolJobs"),    /* 3 */
544                       NT_("MaxVolFiles"),   /* 4 */
545                       NT_("MaxVolBytes"),   /* 5 */
546                       NT_("Recycle"),       /* 6 */
547                       NT_("InChanger"),     /* 7 */
548                       NT_("Slot"),          /* 8 */
549                       NT_("Pool"),          /* 9 */
550                       NT_("FromPool"),      /* 10 */
551                       NT_("AllFromPool"),   /* 11 !!! see below !!! */
552                       NT_("Enabled"),       /* 12 */
553                       NT_("RecyclePool"),   /* 13 */
554                       NT_("ActionOnPurge"), /* 14 */
555                       NT_("Storage"),       /* 15 */
556                       NULL};
557 
558 #define AllFromPool 11 /* keep this updated with above */
559 
560   for (i = 0; kw[i]; i++) {
561     int j;
562     PoolDbRecord pr;
563     MediaDbRecord mr;
564 
565     if ((j = FindArgWithValue(ua, kw[i])) > 0) {
566       /* If all from pool don't select a media record */
567       if (i != AllFromPool && !SelectMediaDbr(ua, &mr)) { return false; }
568       switch (i) {
569         case 0:
570           UpdateVolstatus(ua, ua->argv[j], &mr);
571           break;
572         case 1:
573           UpdateVolretention(ua, ua->argv[j], &mr);
574           break;
575         case 2:
576           UpdateVoluseduration(ua, ua->argv[j], &mr);
577           break;
578         case 3:
579           UpdateVolmaxjobs(ua, ua->argv[j], &mr);
580           break;
581         case 4:
582           UpdateVolmaxfiles(ua, ua->argv[j], &mr);
583           break;
584         case 5:
585           UpdateVolmaxbytes(ua, ua->argv[j], &mr);
586           break;
587         case 6:
588           UpdateVolrecycle(ua, ua->argv[j], &mr);
589           break;
590         case 7:
591           UpdateVolinchanger(ua, ua->argv[j], &mr);
592           break;
593         case 8:
594           UpdateVolslot(ua, ua->argv[j], &mr);
595           break;
596         case 9:
597           pr.PoolId = mr.PoolId;
598           if (!ua->db->GetPoolRecord(ua->jcr, &pr)) {
599             ua->ErrorMsg("%s", ua->db->strerror());
600             break;
601           }
602           UpdateVolPool(ua, ua->argv[j], &mr, &pr);
603           break;
604         case 10:
605           UpdateVolFromPool(ua, &mr);
606           return true;
607         case 11:
608           UpdateAllVolsFromPool(ua, ua->argv[j]);
609           return true;
610         case 12:
611           UpdateVolenabled(ua, ua->argv[j], &mr);
612           break;
613         case 13:
614           UpdateVolRecyclepool(ua, ua->argv[j], &mr);
615           break;
616         case 14:
617           UpdateVolActiononpurge(ua, ua->argv[j], &mr);
618           break;
619         case 15:
620           UpdateVolStorage(ua, ua->argv[j], &mr);
621           break;
622       }
623       done = true;
624     }
625   }
626 
627   /* Allow user to simply update all volumes */
628   if (FindArg(ua, NT_("fromallpools")) > 0) {
629     UpdateAllVols(ua);
630     return true;
631   }
632 
633   for (; !done;) {
634     PoolDbRecord pr;
635     MediaDbRecord mr;
636     StorageDbRecord sr;
637 
638     StartPrompt(ua, _("Parameters to modify:\n"));
639     AddPrompt(ua, _("Volume Status"));              /* 0 */
640     AddPrompt(ua, _("Volume Retention Period"));    /* 1 */
641     AddPrompt(ua, _("Volume Use Duration"));        /* 2 */
642     AddPrompt(ua, _("Maximum Volume Jobs"));        /* 3 */
643     AddPrompt(ua, _("Maximum Volume Files"));       /* 4 */
644     AddPrompt(ua, _("Maximum Volume Bytes"));       /* 5 */
645     AddPrompt(ua, _("Recycle Flag"));               /* 6 */
646     AddPrompt(ua, _("Slot"));                       /* 7 */
647     AddPrompt(ua, _("InChanger Flag"));             /* 8 */
648     AddPrompt(ua, _("Volume Files"));               /* 9 */
649     AddPrompt(ua, _("Pool"));                       /* 10 */
650     AddPrompt(ua, _("Volume from Pool"));           /* 11 */
651     AddPrompt(ua, _("All Volumes from Pool"));      /* 12 */
652     AddPrompt(ua, _("All Volumes from all Pools")); /* 13 */
653     AddPrompt(ua, _("Enabled")),                    /* 14 */
654         AddPrompt(ua, _("RecyclePool")),            /* 15 */
655         AddPrompt(ua, _("Action On Purge")),        /* 16 */
656         AddPrompt(ua, _("Storage")),                /* 17 */
657         AddPrompt(ua, _("Done"));                   /* 18 */
658     i = DoPrompt(ua, "", _("Select parameter to modify"), NULL, 0);
659 
660     /* For All Volumes, All Volumes from Pool, and Done, we don't need
661      * a Volume record */
662     if (i != 12 && i != 13 && i != 18) {
663       if (!SelectMediaDbr(ua, &mr)) { /* Get Volume record */
664         return false;
665       }
666       ua->InfoMsg(_("Updating Volume \"%s\"\n"), mr.VolumeName);
667     }
668     switch (i) {
669       case 0: /* Volume Status */
670         /* Modify Volume Status */
671         ua->InfoMsg(_("Current Volume status is: %s\n"), mr.VolStatus);
672         StartPrompt(ua, _("Possible Values are:\n"));
673         AddPrompt(ua, NT_("Append"));
674         AddPrompt(ua, NT_("Archive"));
675         AddPrompt(ua, NT_("Disabled"));
676         AddPrompt(ua, NT_("Full"));
677         AddPrompt(ua, NT_("Used"));
678         AddPrompt(ua, NT_("Cleaning"));
679         if (bstrcmp(mr.VolStatus, NT_("Purged"))) {
680           AddPrompt(ua, NT_("Recycle"));
681         }
682         AddPrompt(ua, NT_("Read-Only"));
683         if (DoPrompt(ua, "", _("Choose new Volume Status"), ua->cmd,
684                      sizeof(mr.VolStatus)) < 0) {
685           return true;
686         }
687         UpdateVolstatus(ua, ua->cmd, &mr);
688         break;
689       case 1: /* Retention */
690         ua->InfoMsg(_("Current retention period is: %s\n"),
691                     edit_utime(mr.VolRetention, ed1, sizeof(ed1)));
692         if (!GetCmd(ua, _("Enter Volume Retention period: "))) { return false; }
693         UpdateVolretention(ua, ua->cmd, &mr);
694         break;
695 
696       case 2: /* Use Duration */
697         ua->InfoMsg(_("Current use duration is: %s\n"),
698                     edit_utime(mr.VolUseDuration, ed1, sizeof(ed1)));
699         if (!GetCmd(ua, _("Enter Volume Use Duration: "))) { return false; }
700         UpdateVoluseduration(ua, ua->cmd, &mr);
701         break;
702 
703       case 3: /* Max Jobs */
704         ua->InfoMsg(_("Current max jobs is: %u\n"), mr.MaxVolJobs);
705         if (!GetPint(ua, _("Enter new Maximum Jobs: "))) { return false; }
706         UpdateVolmaxjobs(ua, ua->cmd, &mr);
707         break;
708 
709       case 4: /* Max Files */
710         ua->InfoMsg(_("Current max files is: %u\n"), mr.MaxVolFiles);
711         if (!GetPint(ua, _("Enter new Maximum Files: "))) { return false; }
712         UpdateVolmaxfiles(ua, ua->cmd, &mr);
713         break;
714 
715       case 5: /* Max Bytes */
716         ua->InfoMsg(_("Current value is: %s\n"),
717                     edit_uint64(mr.MaxVolBytes, ed1));
718         if (!GetCmd(ua, _("Enter new Maximum Bytes: "))) { return false; }
719         UpdateVolmaxbytes(ua, ua->cmd, &mr);
720         break;
721 
722 
723       case 6: /* Recycle */
724         ua->InfoMsg(_("Current recycle flag is: %s\n"),
725                     (mr.Recycle == 1) ? _("yes") : _("no"));
726         if (!GetYesno(ua, _("Enter new Recycle status: "))) { return false; }
727         UpdateVolrecycle(ua, ua->cmd, &mr);
728         break;
729 
730       case 7: /* Slot */
731         ua->InfoMsg(_("Current Slot is: %d\n"), mr.Slot);
732         if (!GetPint(ua, _("Enter new Slot: "))) { return false; }
733         UpdateVolslot(ua, ua->cmd, &mr);
734         break;
735 
736       case 8: /* InChanger */
737         ua->InfoMsg(_("Current InChanger flag is: %d\n"), mr.InChanger);
738         Bsnprintf(buf, sizeof(buf),
739                   _("Set InChanger flag for Volume \"%s\": yes/no: "),
740                   mr.VolumeName);
741         if (!GetYesno(ua, buf)) { return false; }
742         mr.InChanger = ua->pint32_val;
743         /*
744          * Make sure to use db_update... rather than doing this directly,
745          *   so that any Slot is handled correctly.
746          */
747         SetStorageidInMr(NULL, &mr);
748         if (!ua->db->UpdateMediaRecord(ua->jcr, &mr)) {
749           ua->ErrorMsg(_("Error updating media record Slot: ERR=%s"),
750                        ua->db->strerror());
751         } else {
752           ua->InfoMsg(_("New InChanger flag is: %d\n"), mr.InChanger);
753         }
754         break;
755 
756 
757       case 9: /* Volume Files */
758         int32_t VolFiles;
759         ua->WarningMsg(
760             _("Warning changing Volume Files can result\n"
761               "in loss of data on your Volume\n\n"));
762         ua->InfoMsg(_("Current Volume Files is: %u\n"), mr.VolFiles);
763         if (!GetPint(ua, _("Enter new number of Files for Volume: "))) {
764           return false;
765         }
766         VolFiles = ua->pint32_val;
767         if (VolFiles != (int)(mr.VolFiles + 1)) {
768           ua->WarningMsg(
769               _("Normally, you should only increase Volume Files by one!\n"));
770           if (!GetYesno(ua, _("Increase Volume Files? (yes/no): ")) ||
771               !ua->pint32_val) {
772             break;
773           }
774         }
775         query = GetPoolMemory(PM_MESSAGE);
776         Mmsg(query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%s", VolFiles,
777              edit_int64(mr.MediaId, ed1));
778         if (!ua->db->SqlQuery(query)) {
779           ua->ErrorMsg("%s", ua->db->strerror());
780         } else {
781           ua->InfoMsg(_("New Volume Files is: %u\n"), VolFiles);
782         }
783         FreePoolMemory(query);
784         break;
785 
786       case 10: /* Volume's Pool */
787         pr.PoolId = mr.PoolId;
788         if (!ua->db->GetPoolRecord(ua->jcr, &pr)) {
789           ua->ErrorMsg("%s", ua->db->strerror());
790           return false;
791         }
792         ua->InfoMsg(_("Current Pool is: %s\n"), pr.Name);
793         if (!GetCmd(ua, _("Enter new Pool name: "))) { return false; }
794         UpdateVolPool(ua, ua->cmd, &mr, &pr);
795         return true;
796 
797       case 11:
798         UpdateVolFromPool(ua, &mr);
799         return true;
800       case 12:
801         pool = select_pool_resource(ua);
802         if (pool) { UpdateAllVolsFromPool(ua, pool->resource_name_); }
803         return true;
804 
805       case 13:
806         UpdateAllVols(ua);
807         return true;
808 
809       case 14:
810         ua->InfoMsg(_("Current Enabled is: %d\n"), mr.Enabled);
811         if (!GetCmd(ua, _("Enter new Enabled: "))) { return false; }
812 
813         UpdateVolenabled(ua, ua->cmd, &mr);
814         break;
815 
816       case 15:
817         pr.PoolId = mr.RecyclePoolId;
818         if (ua->db->GetPoolRecord(ua->jcr, &pr)) {
819           ua->InfoMsg(_("Current RecyclePool is: %s\n"), pr.Name);
820         } else {
821           ua->InfoMsg(_("No current RecyclePool\n"));
822         }
823         if (!SelectPoolDbr(ua, &pr, NT_("recyclepool"))) { return false; }
824         UpdateVolRecyclepool(ua, pr.Name, &mr);
825         return true;
826 
827       case 16:
828         PmStrcpy(ret, "");
829         ua->InfoMsg(_("Current ActionOnPurge is: %s\n"),
830                     action_on_purge_to_string(mr.ActionOnPurge, ret));
831         if (!GetCmd(ua,
832                     _("Enter new ActionOnPurge (one of: Truncate, None): "))) {
833           return false;
834         }
835 
836         UpdateVolActiononpurge(ua, ua->cmd, &mr);
837         break;
838 
839       case 17:
840         sr.StorageId = mr.StorageId;
841         if (ua->db->GetStorageRecord(ua->jcr, &sr)) {
842           ua->InfoMsg(_("Current Storage is: %s\n"), sr.Name);
843         } else {
844           ua->InfoMsg(_("Warning, could not find current Storage\n"));
845         }
846         if (!SelectStorageDbr(ua, &sr, NT_("storage"))) { return false; }
847         UpdateVolStorage(ua, sr.Name, &mr);
848         ua->InfoMsg(_("New Storage is: %s\n"), sr.Name);
849         return true;
850 
851       default: /* Done or error */
852         ua->InfoMsg(_("Selection terminated.\n"));
853         return true;
854     }
855   }
856   return true;
857 }
858 
859 /**
860  * Update long term statistics
861  */
UpdateStats(UaContext * ua)862 static bool UpdateStats(UaContext* ua)
863 {
864   int i = FindArgWithValue(ua, NT_("days"));
865   utime_t since = 0;
866 
867   if (i >= 0) { since = ((int64_t)atoi(ua->argv[i]) * 24 * 60 * 60); }
868 
869   int nb = ua->db->UpdateStats(ua->jcr, since);
870   ua->InfoMsg(_("Updating %i job(s).\n"), nb);
871 
872   return true;
873 }
874 
875 /**
876  * Update pool record -- pull info from current POOL resource
877  */
UpdatePool(UaContext * ua)878 static bool UpdatePool(UaContext* ua)
879 {
880   int id;
881   PoolDbRecord pr;
882   char ed1[50];
883   PoolResource* pool;
884   PoolMem query(PM_MESSAGE);
885 
886   pool = get_pool_resource(ua);
887   if (!pool) { return false; }
888 
889   bstrncpy(pr.Name, pool->resource_name_, sizeof(pr.Name));
890   if (!GetPoolDbr(ua, &pr)) { return false; }
891 
892   SetPooldbrFromPoolres(&pr, pool, POOL_OP_UPDATE); /* update */
893   SetPooldbrReferences(ua->jcr, ua->db, &pr, pool);
894 
895   id = ua->db->UpdatePoolRecord(ua->jcr, &pr);
896   if (id <= 0) {
897     ua->ErrorMsg(_("UpdatePoolRecord returned %d. ERR=%s\n"), id,
898                  ua->db->strerror());
899   }
900   ua->db->FillQuery(query, BareosDb::SQL_QUERY::list_pool,
901                     edit_int64(pr.PoolId, ed1));
902   ua->db->ListSqlQuery(ua->jcr, query.c_str(), ua->send, HORZ_LIST, true);
903   ua->InfoMsg(_("Pool DB record updated from resource.\n"));
904 
905   return true;
906 }
907 
908 /**
909  * Update a Job record -- allows to change the fields in a Job record.
910  */
UpdateJob(UaContext * ua)911 static bool UpdateJob(UaContext* ua)
912 {
913   int i;
914   char ed1[50], ed2[50], ed3[50], ed4[50];
915   PoolMem cmd(PM_MESSAGE);
916   JobDbRecord jr;
917   ClientDbRecord cr;
918   utime_t StartTime;
919   char* client_name = NULL;
920   char* job_name = NULL;
921   char* start_time = NULL;
922   char job_type = '\0';
923   DBId_t fileset_id = 0;
924   const char* kw[] = {NT_("starttime"), /* 0 */
925                       NT_("client"),    /* 1 */
926                       NT_("filesetid"), /* 2 */
927                       NT_("jobname"),   /* 3 */
928                       NT_("jobtype"),   /* 4 */
929                       NULL};
930 
931   Dmsg1(200, "cmd=%s\n", ua->cmd);
932   i = FindArgWithValue(ua, NT_("jobid"));
933   if (i < 0) {
934     ua->ErrorMsg(_("Expect JobId keyword, not found.\n"));
935     return false;
936   }
937   jr.JobId = str_to_int64(ua->argv[i]);
938   if (!ua->db->GetJobRecord(ua->jcr, &jr)) {
939     ua->ErrorMsg("%s", ua->db->strerror());
940     return false;
941   }
942 
943   for (i = 0; kw[i]; i++) {
944     int j;
945     if ((j = FindArgWithValue(ua, kw[i])) >= 0) {
946       switch (i) {
947         case 0: /* Start time */
948           start_time = ua->argv[j];
949           break;
950         case 1: /* Client name */
951           client_name = ua->argv[j];
952           break;
953         case 2: /* Fileset id */
954           fileset_id = str_to_uint64(ua->argv[j]);
955           break;
956         case 3: /* Job name */
957           job_name = ua->argv[j];
958           break;
959         case 4: /* Job Type */
960           job_type = ua->argv[j][0];
961           break;
962       }
963     }
964   }
965   if (!client_name && !start_time && !fileset_id && !job_name && !job_type) {
966     ua->ErrorMsg(_(
967         "Neither Client, StartTime, Filesetid, JobType nor Name specified.\n"));
968     return false;
969   }
970   if (client_name) {
971     if (!GetClientDbr(ua, &cr)) { return false; }
972     jr.ClientId = cr.ClientId;
973   }
974   if (fileset_id) { jr.FileSetId = fileset_id; }
975   if (job_name) { bstrncpy(jr.Name, job_name, MAX_NAME_LENGTH); }
976   if (job_type) { jr.JobType = job_type; }
977   if (start_time) {
978     utime_t delta_start;
979 
980     StartTime = StrToUtime(start_time);
981     if (StartTime == 0) {
982       ua->ErrorMsg(_("Improper date format: %s\n"), ua->argv[i]);
983       return false;
984     }
985     delta_start = StartTime - jr.StartTime;
986     Dmsg3(200, "ST=%lld jr.ST=%lld delta=%lld\n", StartTime,
987           (utime_t)jr.StartTime, delta_start);
988     jr.StartTime = (time_t)StartTime;
989     jr.SchedTime += (time_t)delta_start;
990     jr.EndTime += (time_t)delta_start;
991     jr.JobTDate += delta_start;
992     /* Convert to DB times */
993     bstrutime(jr.cStartTime, sizeof(jr.cStartTime), jr.StartTime);
994     bstrutime(jr.cSchedTime, sizeof(jr.cSchedTime), jr.SchedTime);
995     bstrutime(jr.cEndTime, sizeof(jr.cEndTime), jr.EndTime);
996   }
997   Mmsg(cmd,
998        "UPDATE Job SET Name='%s', ClientId=%s,StartTime='%s',SchedTime='%s',"
999        "EndTime='%s',JobTDate=%s, FileSetId='%s', Type='%c' WHERE JobId=%s",
1000        jr.Name, edit_int64(jr.ClientId, ed1), jr.cStartTime, jr.cSchedTime,
1001        jr.cEndTime, edit_uint64(jr.JobTDate, ed2),
1002        edit_uint64(jr.FileSetId, ed3), jr.JobType, edit_int64(jr.JobId, ed4));
1003   if (!ua->db->SqlQuery(cmd.c_str())) {
1004     ua->ErrorMsg("%s", ua->db->strerror());
1005     return false;
1006   }
1007   return true;
1008 }
1009 
1010 /**
1011  * Update Slots corresponding to Volumes in autochanger
1012  */
UpdateSlots(UaContext * ua)1013 static void UpdateSlots(UaContext* ua)
1014 {
1015   UnifiedStorageResource store;
1016   vol_list_t* vl;
1017   changer_vol_list_t* vol_list = NULL;
1018   MediaDbRecord mr;
1019   char* slot_list;
1020   bool scan;
1021   slot_number_t max_slots;
1022   drive_number_t drive = kInvalidDriveNumber;
1023   int Enabled = VOL_ENABLED;
1024   bool have_enabled;
1025   int i;
1026 
1027 
1028   if (!OpenClientDb(ua)) { return; }
1029   store.store = get_storage_resource(ua, true, true);
1030   if (!store.store) { return; }
1031   PmStrcpy(store.store_source, _("command line"));
1032   SetWstorage(ua->jcr, &store);
1033 
1034   scan = FindArg(ua, NT_("scan")) >= 0;
1035   if (scan) { drive = GetStorageDrive(ua, store.store); }
1036   if ((i = FindArgWithValue(ua, NT_("Enabled"))) >= 0) {
1037     Enabled = GetEnabled(ua, ua->argv[i]);
1038     if (Enabled < 0) { return; }
1039     have_enabled = true;
1040   } else {
1041     have_enabled = false;
1042   }
1043 
1044   max_slots = GetNumSlots(ua, ua->jcr->impl->res.write_storage);
1045   Dmsg1(100, "max_slots=%d\n", max_slots);
1046   if (max_slots <= 0) {
1047     ua->WarningMsg(_("No slots in changer to scan.\n"));
1048     return;
1049   }
1050 
1051   slot_list = (char*)malloc(NbytesForBits(max_slots));
1052   ClearAllBits(max_slots, slot_list);
1053   if (!GetUserSlotList(ua, slot_list, "slots", max_slots)) {
1054     free(slot_list);
1055     return;
1056   }
1057 
1058   vol_list = get_vol_list_from_storage(ua, store.store, false, scan, false);
1059   if (!vol_list) {
1060     ua->WarningMsg(_("No Volumes found to update, or no barcodes.\n"));
1061     goto bail_out;
1062   }
1063 
1064   /*
1065    * First zap out any InChanger with StorageId=0
1066    */
1067   ua->db->SqlQuery("UPDATE Media SET InChanger=0 WHERE StorageId=0");
1068 
1069   /*
1070    * Walk through the list updating the media records
1071    */
1072   foreach_dlist (vl, vol_list->contents) {
1073     if (vl->bareos_slot_number > max_slots) {
1074       ua->WarningMsg(_("Slot %d greater than max %d ignored.\n"),
1075                      vl->bareos_slot_number, max_slots);
1076       continue;
1077     }
1078     /*
1079      * Check if user wants us to look at this slot
1080      */
1081     if (!BitIsSet(vl->bareos_slot_number - 1, slot_list)) {
1082       Dmsg1(100, "Skipping slot=%d\n", vl->bareos_slot_number);
1083       continue;
1084     }
1085     /*
1086      * If scanning, we read the label rather than the barcode
1087      */
1088     if (scan) {
1089       if (vl->VolName) {
1090         free(vl->VolName);
1091         vl->VolName = NULL;
1092       }
1093       vl->VolName = get_volume_name_from_SD(ua, vl->bareos_slot_number, drive);
1094       Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName,
1095             vl->bareos_slot_number);
1096     }
1097     ClearBit(vl->bareos_slot_number - 1, slot_list); /* clear Slot */
1098     SetStorageidInMr(store.store, &mr);
1099     mr.Slot = vl->bareos_slot_number;
1100     mr.InChanger = 1;
1101     mr.MediaId = 0; /* Get by VolumeName */
1102     if (vl->VolName) {
1103       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1104     } else {
1105       mr.VolumeName[0] = 0;
1106     }
1107     SetStorageidInMr(store.store, &mr);
1108 
1109     Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
1110           mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1111     DbLock(ua->db);
1112     /*
1113      * Set InChanger to zero for this Slot
1114      */
1115     ua->db->MakeInchangerUnique(ua->jcr, &mr);
1116     DbUnlock(ua->db);
1117     Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
1118           mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1119 
1120     if (!vl->VolName) {
1121       Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n",
1122             vl->bareos_slot_number);
1123       ua->InfoMsg(_("No VolName for Slot=%d InChanger set to zero.\n"),
1124                   vl->bareos_slot_number);
1125       continue;
1126     }
1127 
1128     DbLock(ua->db);
1129     Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
1130           mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1131     if (ua->db->GetMediaRecord(ua->jcr, &mr)) {
1132       Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
1133             mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1134       /*
1135        * If Slot, Inchanger, and StorageId have changed, update the Media record
1136        */
1137       if (mr.Slot != vl->bareos_slot_number || !mr.InChanger ||
1138           mr.StorageId != store.store->StorageId) {
1139         mr.Slot = vl->bareos_slot_number;
1140         mr.InChanger = 1;
1141         if (have_enabled) { mr.Enabled = Enabled; }
1142         SetStorageidInMr(store.store, &mr);
1143         if (!ua->db->UpdateMediaRecord(ua->jcr, &mr)) {
1144           ua->ErrorMsg("%s", ua->db->strerror());
1145         } else {
1146           ua->InfoMsg(_("Catalog record for Volume \"%s\" updated to reference "
1147                         "slot %d.\n"),
1148                       mr.VolumeName, mr.Slot);
1149         }
1150       } else {
1151         ua->InfoMsg(_("Catalog record for Volume \"%s\" is up to date.\n"),
1152                     mr.VolumeName);
1153       }
1154     } else {
1155       ua->WarningMsg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger "
1156                        "set to zero.\n"),
1157                      mr.VolumeName, vl->bareos_slot_number);
1158     }
1159     DbUnlock(ua->db);
1160   }
1161   {
1162     MediaDbRecord mr;
1163     mr.InChanger = 1;
1164     SetStorageidInMr(store.store, &mr);
1165 
1166     /*
1167      * Any slot not visited gets it Inchanger flag reset.
1168      */
1169     DbLock(ua->db);
1170     for (i = 1; i <= max_slots; i++) {
1171       if (BitIsSet(i - 1, slot_list)) {
1172         /*
1173          * Set InChanger to zero for this Slot
1174          */
1175         mr.Slot = i;
1176         ua->db->MakeInchangerUnique(ua->jcr, &mr);
1177       }
1178     }
1179     DbUnlock(ua->db);
1180   }
1181 bail_out:
1182   if (vol_list) { StorageReleaseVolList(store.store, vol_list); }
1183   free(slot_list);
1184   CloseSdBsock(ua);
1185 
1186   return;
1187 }
1188 
1189 /**
1190  * Update Slots corresponding to Volumes in autochanger.
1191  * We only update any new volume location of slots marked in
1192  * the given slot_list. If you want to do funky stuff
1193  * run an "update slots" with the options you want. This
1194  * is a simple function which syncs the info from the
1195  * vol_list to the database for each slot marked in
1196  * the slot_list.
1197  *
1198  * The vol_list passed here needs to be from an "autochanger listall" cmd.
1199  */
UpdateSlotsFromVolList(UaContext * ua,StorageResource * store,changer_vol_list_t * vol_list,char * slot_list)1200 void UpdateSlotsFromVolList(UaContext* ua,
1201                             StorageResource* store,
1202                             changer_vol_list_t* vol_list,
1203                             char* slot_list)
1204 {
1205   vol_list_t* vl;
1206 
1207   if (!OpenClientDb(ua)) { return; }
1208 
1209   /*
1210    * Walk through the list updating the media records
1211    */
1212   foreach_dlist (vl, vol_list->contents) {
1213     /*
1214      * We are only interested in normal slots.
1215      */
1216     switch (vl->slot_type) {
1217       case slot_type_t::kSlotTypeStorage:
1218         break;
1219       default:
1220         continue;
1221     }
1222 
1223     /*
1224      * Only update entries of slots marked in the slot_list.
1225      */
1226     if (!BitIsSet(vl->bareos_slot_number - 1, slot_list)) { continue; }
1227 
1228     /*
1229      * Set InChanger to zero for this Slot
1230      */
1231     MediaDbRecord mr;
1232     mr.Slot = vl->bareos_slot_number;
1233     mr.InChanger = 1;
1234     mr.MediaId = 0; /* Get by VolumeName */
1235     if (vl->VolName) {
1236       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1237     } else {
1238       mr.VolumeName[0] = 0;
1239     }
1240     SetStorageidInMr(store, &mr);
1241 
1242     Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
1243           mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1244     DbLock(ua->db);
1245 
1246     /*
1247      * Set InChanger to zero for this Slot
1248      */
1249     ua->db->MakeInchangerUnique(ua->jcr, &mr);
1250 
1251     DbUnlock(ua->db);
1252     Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
1253           mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1254 
1255     /*
1256      * See if there is anything in the slot.
1257      */
1258     switch (vl->slot_status) {
1259       case slot_status_t::kSlotStatusFull:
1260         if (!vl->VolName) {
1261           Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n",
1262                 vl->bareos_slot_number);
1263           continue;
1264         }
1265         break;
1266       default:
1267         continue;
1268     }
1269 
1270     /*
1271      * There is something in the slot and it has a VolumeName so we can check
1272      * the database and perform an update if needed.
1273      */
1274     DbLock(ua->db);
1275     Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
1276           mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1277     if (ua->db->GetMediaRecord(ua->jcr, &mr)) {
1278       Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
1279             mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1280       /* If Slot, Inchanger, and StorageId have changed, update the Media record
1281        */
1282       if (mr.Slot != vl->bareos_slot_number || !mr.InChanger ||
1283           mr.StorageId != store->StorageId) {
1284         mr.Slot = vl->bareos_slot_number;
1285         mr.InChanger = 1;
1286         SetStorageidInMr(store, &mr);
1287         if (!ua->db->UpdateMediaRecord(ua->jcr, &mr)) {
1288           ua->ErrorMsg("%s", ua->db->strerror());
1289         }
1290       }
1291     } else {
1292       ua->WarningMsg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger "
1293                        "set to zero.\n"),
1294                      mr.VolumeName, vl->bareos_slot_number);
1295     }
1296     DbUnlock(ua->db);
1297   }
1298   return;
1299 }
1300 
1301 /**
1302  * Set the inchanger flag to zero for each slot marked in
1303  * the given slot_list.
1304  *
1305  * The vol_list passed here needs to be from an "autochanger listall" cmd.
1306  */
UpdateInchangerForExport(UaContext * ua,StorageResource * store,changer_vol_list_t * vol_list,char * slot_list)1307 void UpdateInchangerForExport(UaContext* ua,
1308                               StorageResource* store,
1309                               changer_vol_list_t* vol_list,
1310                               char* slot_list)
1311 {
1312   vol_list_t* vl;
1313 
1314   if (!OpenClientDb(ua)) { return; }
1315 
1316   /*
1317    * Walk through the list updating the media records
1318    */
1319   foreach_dlist (vl, vol_list->contents) {
1320     /*
1321      * We are only interested in normal slots.
1322      */
1323     switch (vl->slot_type) {
1324       case slot_type_t::kSlotTypeStorage:
1325         break;
1326       default:
1327         continue;
1328     }
1329 
1330     /*
1331      * Only update entries of slots marked in the slot_list.
1332      */
1333     if (!BitIsSet(vl->bareos_slot_number - 1, slot_list)) { continue; }
1334 
1335     /*
1336      * Set InChanger to zero for this Slot
1337      */
1338     MediaDbRecord mr;
1339     mr.Slot = vl->bareos_slot_number;
1340     mr.InChanger = 1;
1341     mr.MediaId = 0; /* Get by VolumeName */
1342     if (vl->VolName) {
1343       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1344     } else {
1345       mr.VolumeName[0] = 0;
1346     }
1347     SetStorageidInMr(store, &mr);
1348 
1349     Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
1350           mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1351     DbLock(ua->db);
1352 
1353     /*
1354      * Set InChanger to zero for this Slot
1355      */
1356     ua->db->MakeInchangerUnique(ua->jcr, &mr);
1357 
1358     DbUnlock(ua->db);
1359     Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
1360           mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
1361   }
1362   return;
1363 }
1364 } /* namespace directordaemon */
1365