2 * Drive reservation functions for Storage Daemon
6 * Split from job.c and acquire.c June 2005
12 Copyright (C) 2000-2005 Kern Sibbald
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License
16 version 2 as amended with additional clauses defined in the
17 file LICENSE in the main source directory.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 the file LICENSE for additional details.
30 * Use Device command from Director
31 * He tells is what Device Name to use, the Media Type,
32 * the Pool Name, and the Pool Type.
34 * Ensure that the device exists and is opened, then store
35 * the media and pool info in the JCR. This class is used
36 * only temporarily in this file.
42 char name[MAX_NAME_LENGTH];
43 char media_type[MAX_NAME_LENGTH];
44 char pool_name[MAX_NAME_LENGTH];
45 char pool_type[MAX_NAME_LENGTH];
56 bool PreferMountedVols;
59 static dlist *vol_list = NULL;
60 static pthread_mutex_t vol_list_lock = PTHREAD_MUTEX_INITIALIZER;
61 static pthread_mutex_t search_lock = PTHREAD_MUTEX_INITIALIZER;
63 /* Forward referenced functions */
64 static int can_reserve_drive(DCR *dcr, bool PerferMountedVols);
65 static int search_res_for_device(RCTX &rctx);
66 static int reserve_device(RCTX &rctx);
67 static bool reserve_device_for_read(DCR *dcr);
68 static bool reserve_device_for_append(DCR *dcr, bool PreferMountedVols);
69 static bool use_storage_cmd(JCR *jcr);
70 bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx);
72 /* Requests from the Director daemon */
73 static char use_storage[] = "use storage=%127s media_type=%127s "
74 "pool_name=%127s pool_type=%127s append=%d copy=%d stripe=%d\n";
75 static char use_device[] = "use device=%127s\n";
77 /* Responses sent to Director daemon */
78 static char OK_device[] = "3000 OK use device device=%s\n";
79 static char NO_device[] = "3924 Device \"%s\" not in SD Device resources.\n";
80 static char BAD_use[] = "3913 Bad use command: %s\n";
82 bool use_cmd(JCR *jcr)
85 * Get the device, media, and pool information
87 if (!use_storage_cmd(jcr)) {
88 set_jcr_job_status(jcr, JS_ErrorTerminated);
89 memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
95 static int my_compare(void *item1, void *item2)
97 return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name);
102 * Put a new Volume entry in the Volume list. This
103 * effectively reserves the volume so that it will
104 * not be mounted again.
106 * Return: VOLRES entry on success
107 * NULL if the Volume is already in the list
109 VOLRES *new_volume(DCR *dcr, const char *VolumeName)
112 vol = (VOLRES *)malloc(sizeof(VOLRES));
113 memset(vol, 0, sizeof(VOLRES));
114 vol->vol_name = bstrdup(VolumeName);
118 nvol = (VOLRES *)vol_list->binary_insert(vol, my_compare);
124 nvol->dev = dcr->dev;
132 * Search for a Volume name in the Volume list.
134 * Returns: VOLRES entry on success
135 * NULL if the Volume is not in the list
137 VOLRES *find_volume(const char *VolumeName)
140 vol.vol_name = bstrdup(VolumeName);
142 fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
149 * Free a Volume from the Volume list
151 * Returns: true if the Volume found and removed from the list
152 * false if the Volume is not in the list
154 bool free_volume(DEVICE *dev)
158 if (dev->VolHdr.VolumeName[0] == 0) {
161 vol.vol_name = bstrdup(dev->VolHdr.VolumeName);
163 fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
165 vol_list->remove(fvol);
166 free(fvol->vol_name);
171 dev->VolHdr.VolumeName[0] = 0;
175 /* Free volume reserved by this dcr but not attached to a dev */
176 void free_unused_volume(DCR *dcr)
180 for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
181 if (vol->dcr == dcr && (vol->dev == NULL ||
182 strcmp(vol->vol_name, vol->dev->VolHdr.VolumeName) != 0)) {
183 vol_list->remove(vol);
193 * List Volumes -- this should be moved to status.c
195 void list_volumes(BSOCK *user)
198 for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
199 bnet_fsend(user, "%s\n", vol->vol_name);
203 /* Create the Volume list */
204 void create_volume_list()
206 VOLRES *dummy = NULL;
207 if (vol_list == NULL) {
208 vol_list = New(dlist(dummy, &dummy->link));
212 /* Release all Volumes from the list */
213 void free_volume_list()
219 for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
220 Dmsg3(000, "Unreleased Volume=%s dcr=0x%x dev=0x%x\n", vol->vol_name,
227 bool is_volume_in_use(DCR *dcr)
229 VOLRES *vol = find_volume(dcr->VolumeName);
231 return false; /* vol not in list */
233 if (!vol->dev) { /* vol not attached to device */
236 if (dcr->dev == vol->dev) { /* same device OK */
239 if (!vol->dev->is_busy()) {
246 static bool use_storage_cmd(JCR *jcr)
248 POOL_MEM store_name, dev_name, media_type, pool_name, pool_type;
249 BSOCK *dir = jcr->dir_bsock;
259 memset(&rctx, 0, sizeof(RCTX));
262 * If there are multiple devices, the director sends us
263 * use_device for each device that it wants to use.
265 Dmsg1(100, "<dird: %s", dir->msg);
266 jcr->dirstore = New(alist(10, not_owned_by_alist));
268 ok = sscanf(dir->msg, use_storage, store_name.c_str(),
269 media_type.c_str(), pool_name.c_str(),
270 pool_type.c_str(), &append, &Copy, &Stripe) == 7;
274 unbash_spaces(store_name);
275 unbash_spaces(media_type);
276 unbash_spaces(pool_name);
277 unbash_spaces(pool_type);
278 store = new DIRSTORE;
279 jcr->dirstore->append(store);
280 memset(store, 0, sizeof(DIRSTORE));
281 store->device = New(alist(10));
282 bstrncpy(store->name, store_name, sizeof(store->name));
283 bstrncpy(store->media_type, media_type, sizeof(store->media_type));
284 bstrncpy(store->pool_name, pool_name, sizeof(store->pool_name));
285 bstrncpy(store->pool_type, pool_type, sizeof(store->pool_type));
286 store->append = append;
288 /* Now get all devices */
289 while (bnet_recv(dir) >= 0) {
290 ok = sscanf(dir->msg, use_device, dev_name.c_str()) == 1;
294 unbash_spaces(dev_name);
295 store->device->append(bstrdup(dev_name.c_str()));
297 } while (ok && bnet_recv(dir) >= 0);
300 /* This loop is debug code and can be removed */
301 /* ***FIXME**** remove after 1.38 release */
303 foreach_alist(store, jcr->dirstore) {
304 Dmsg4(100, "Storage=%s media_type=%s pool=%s pool_type=%s\n",
305 store->name, store->media_type, store->pool_name,
307 foreach_alist(device_name, store->device) {
308 Dmsg1(100, " Device=%s\n", device_name);
314 * At this point, we have a list of all the Director's Storage
315 * resources indicated for this Job, which include Pool, PoolType,
316 * storage name, and Media type.
317 * Then for each of the Storage resources, we have a list of
318 * device names that were given.
320 * Wiffle through them and find one that can do the backup.
324 * Make up to two passes. The first with PreferMountedVols possibly
325 * set to true. In that case, we look only for an available
326 * drive with something mounted. If that fails, then we
327 * do a second pass with PerferMountedVols set false.
329 rctx.PreferMountedVols = jcr->PreferMountedVols;
330 ok = find_suitable_device_for_job(jcr, rctx);
334 if (rctx.PreferMountedVols) {
335 rctx.PreferMountedVols = false;
336 ok = find_suitable_device_for_job(jcr, rctx);
342 unbash_spaces(dir->msg);
343 pm_strcpy(jcr->errmsg, dir->msg);
344 Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
346 Jmsg(jcr, M_FATAL, 0, _("\n"
347 " Device \"%s\" with MediaType \"%s\" requested by DIR not found in SD Device resources.\n"),
348 dev_name.c_str(), media_type.c_str());
349 bnet_fsend(dir, NO_device, dev_name.c_str());
351 for (error=(char*)rctx->errors.first(); error;
352 error=(char*)rctx->errors.next()) {
353 Jmsg(jcr, M_INFO, 0, "%s", error);
356 Dmsg1(100, ">dird: %s\n", dir->msg);
358 unbash_spaces(dir->msg);
359 pm_strcpy(jcr->errmsg, dir->msg);
361 Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
363 bnet_fsend(dir, BAD_use, jcr->errmsg);
364 Dmsg1(100, ">dird: %s\n", dir->msg);
368 foreach_alist(store, jcr->dirstore) {
369 delete store->device;
372 delete jcr->dirstore;
374 for (error=(char*)rctx->errors.first(); error;
375 error=(char*)rctx->errors.next()) {
384 * Search for a device suitable for this job.
386 bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx)
394 init_jcr_device_wait_timers(jcr);
396 int need_wait = false;
399 foreach_alist(store, jcr->dirstore) {
401 foreach_alist(device_name, store->device) {
403 rctx.device_name = device_name;
404 stat = search_res_for_device(rctx);
405 if (stat == 1) { /* found available device */
409 } else if (stat == 0) { /* device busy */
412 /* otherwise error */
413 // rctx->errors.push(bstrdup(jcr->errmsg));
421 * If there is some device for which we can wait, then
422 * wait and try again until the wait time expires
424 if (!need_wait || !wait_for_device(jcr, first)) {
429 for (error=(char*)rctx->errors.first(); error;
430 error=(char*)rctx->errors.next()) {
443 * Search for a particular storage device with particular storage
444 * characteristics (MediaType).
446 static int search_res_for_device(RCTX &rctx)
448 AUTOCHANGER *changer;
449 BSOCK *dir = rctx.jcr->dir_bsock;
453 Dmsg1(100, "Search res for %s\n", rctx.device_name);
454 foreach_res(rctx.device, R_DEVICE) {
455 Dmsg1(100, "Try res=%s\n", rctx.device->hdr.name);
456 /* Find resource, and make sure we were able to open it */
457 if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0) {
458 stat = reserve_device(rctx);
462 Dmsg1(220, "Got: %s", dir->msg);
463 bash_spaces(rctx.device_name);
464 ok = bnet_fsend(dir, OK_device, rctx.device_name);
465 Dmsg1(100, ">dird: %s\n", dir->msg);
469 foreach_res(changer, R_AUTOCHANGER) {
470 Dmsg1(100, "Try changer res=%s\n", changer->hdr.name);
471 /* Find resource, and make sure we were able to open it */
472 if (fnmatch(rctx.device_name, changer->hdr.name, 0) == 0) {
473 /* Try each device in this AutoChanger */
474 foreach_alist(rctx.device, changer->device) {
475 Dmsg1(100, "Try changer device %s\n", rctx.device->hdr.name);
476 stat = reserve_device(rctx);
477 if (stat == -1) { /* hard error */
480 if (stat == 0) { /* must wait, try next one */
484 Dmsg1(100, "Device %s opened.\n", rctx.device_name);
485 pm_strcpy(dev_name, rctx.device->hdr.name);
486 bash_spaces(dev_name);
487 ok = bnet_fsend(dir, OK_device, dev_name.c_str()); /* Return real device name */
488 Dmsg1(100, ">dird: %s\n", dir->msg);
493 return 0; /* nothing found */
497 * Try to reserve a specific device.
499 * Returns: 1 -- OK, have DCR
503 static int reserve_device(RCTX &rctx)
507 const int name_len = MAX_NAME_LENGTH;
509 /* Make sure MediaType is OK */
510 if (strcmp(rctx.device->media_type, rctx.store->media_type) != 0) {
514 if (!rctx.device->dev) {
515 rctx.device->dev = init_dev(rctx.jcr, rctx.device);
517 if (!rctx.device->dev) {
518 if (rctx.device->changer_res) {
519 Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
520 " Device \"%s\" in changer \"%s\" requested by DIR could not be opened or does not exist.\n"),
521 rctx.device->hdr.name, rctx.device_name);
523 Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
524 " Device \"%s\" requested by DIR could not be opened or does not exist.\n"),
527 return -1; /* no use waiting */
529 Dmsg1(100, "Found device %s\n", rctx.device->hdr.name);
530 dcr = new_dcr(rctx.jcr, rctx.device->dev);
532 BSOCK *dir = rctx.jcr->dir_bsock;
533 bnet_fsend(dir, _("3926 Could not get dcr for device: %s\n"), rctx.device_name);
534 Dmsg1(100, ">dird: %s\n", dir->msg);
538 bstrncpy(dcr->pool_name, rctx.store->pool_name, name_len);
539 bstrncpy(dcr->pool_type, rctx.store->pool_type, name_len);
540 bstrncpy(dcr->media_type, rctx.store->media_type, name_len);
541 bstrncpy(dcr->dev_name, rctx.device_name, name_len);
542 if (rctx.store->append == SD_APPEND) {
543 ok = reserve_device_for_append(dcr, rctx.PreferMountedVols);
544 Dmsg3(200, "dev_name=%s mediatype=%s ok=%d\n", dcr->dev_name, dcr->media_type, ok);
546 ok = reserve_device_for_read(dcr);
549 free_dcr(rctx.jcr->dcr);
556 * We "reserve" the drive by setting the ST_READ bit. No one else
557 * should touch the drive until that is cleared.
558 * This allows the DIR to "reserve" the device before actually
561 static bool reserve_device_for_read(DCR *dcr)
563 DEVICE *dev = dcr->dev;
569 dev->block(BST_DOING_ACQUIRE);
571 if (is_device_unmounted(dev)) {
572 Dmsg1(200, "Device %s is BLOCKED due to user unmount.\n", dev->print_name());
573 Mmsg(jcr->errmsg, _("Device %s is BLOCKED due to user unmount.\n"),
578 if (dev->is_busy()) {
579 Dmsg4(200, "Device %s is busy ST_READ=%d num_writers=%d reserved=%d.\n", dev->print_name(),
580 dev->state & ST_READ?1:0, dev->num_writers, dev->reserved_device);
581 Mmsg1(jcr->errmsg, _("Device %s is busy.\n"),
597 * We reserve the device for appending by incrementing the
598 * reserved_device. We do virtually all the same work that
599 * is done in acquire_device_for_append(), but we do
600 * not attempt to mount the device. This routine allows
601 * the DIR to reserve multiple devices before *really*
602 * starting the job. It also permits the SD to refuse
603 * certain devices (not up, ...).
605 * Note, in reserving a device, if the device is for the
606 * same pool and the same pool type, then it is acceptable.
607 * The Media Type has already been checked. If we are
608 * the first tor reserve the device, we put the pool
609 * name and pool type in the device record.
611 static bool reserve_device_for_append(DCR *dcr, bool PreferMountedVols)
614 DEVICE *dev = dcr->dev;
619 dev->block(BST_DOING_ACQUIRE);
621 if (dev->can_read()) {
622 Mmsg1(jcr->errmsg, _("Device %s is busy reading.\n"), dev->print_name());
623 Dmsg1(100, "%s", jcr->errmsg);
627 if (is_device_unmounted(dev)) {
628 Mmsg(jcr->errmsg, _("Device %s is BLOCKED due to user unmount.\n"), dev->print_name());
629 Dmsg1(100, "%s", jcr->errmsg);
633 Dmsg1(190, "reserve_append device is %s\n", dev->is_tape()?"tape":"disk");
635 if (can_reserve_drive(dcr, PreferMountedVols) != 1) {
636 Mmsg1(jcr->errmsg, _("Device %s is busy writing on another Volume.\n"), dev->print_name());
637 Dmsg1(100, "%s", jcr->errmsg);
641 dev->reserved_device++;
642 Dmsg1(200, "Inc reserve=%d\n", dev->reserved_device);
643 dcr->reserved_device = true;
652 * Returns: 1 if drive can be reserved
653 * 0 if we should wait
656 static int can_reserve_drive(DCR *dcr, bool PreferMountedVols)
658 DEVICE *dev = dcr->dev;
661 if (PreferMountedVols && !dev->VolHdr.VolumeName[0] && dev->is_tape()) {
662 return 0; /* No volume mounted */
666 * Handle the case that the drive is not yet in append mode
668 if (!dev->can_append() && dev->num_writers == 0) {
669 /* Now check if there are any reservations on the drive */
670 if (dev->reserved_device) {
671 /* Yes, now check if we want the same Pool and pool type */
672 if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
673 strcmp(dev->pool_type, dcr->pool_type) == 0) {
674 /* OK, compatible device */
676 /* Drive not suitable for us */
680 /* Device is available but not yet reserved, reserve it for us */
681 bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
682 bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
684 return 1; /* reserve drive */
688 * Check if device in append mode with no writers (i.e. available)
690 if (dev->can_append() && dev->num_writers == 0) {
691 /* Device is available but not yet reserved, reserve it for us */
692 bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
693 bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
697 * Check if the device is in append mode with writers (i.e.
698 * available if pool is the same).
700 if (dev->can_append() || dev->num_writers > 0) {
701 Dmsg0(190, "device already in append.\n");
702 /* Yes, now check if we want the same Pool and pool type */
703 if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
704 strcmp(dev->pool_type, dcr->pool_type) == 0) {
705 /* OK, compatible device */
708 /* Drive not suitable for us */
709 Jmsg(jcr, M_WARNING, 0, _("Wanted Pool \"%s\", but device %s is using Pool \"%s\" .\n"),
710 dcr->pool_name, dev->print_name(), dev->pool_name);
714 Pmsg0(000, _("Logic error!!!! Should not get here.\n"));
715 Jmsg0(jcr, M_FATAL, 0, _("Logic error!!!! Should not get here.\n"));
716 return -1; /* error, should not get here */