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)
113 Dmsg1(400, "new_volume %s\n", VolumeName);
114 vol = (VOLRES *)malloc(sizeof(VOLRES));
115 memset(vol, 0, sizeof(VOLRES));
116 vol->vol_name = bstrdup(VolumeName);
120 nvol = (VOLRES *)vol_list->binary_insert(vol, my_compare);
126 nvol->dev = dcr->dev;
134 * Search for a Volume name in the Volume list.
136 * Returns: VOLRES entry on success
137 * NULL if the Volume is not in the list
139 VOLRES *find_volume(const char *VolumeName)
142 vol.vol_name = bstrdup(VolumeName);
144 fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
151 * Free a Volume from the Volume list
153 * Returns: true if the Volume found and removed from the list
154 * false if the Volume is not in the list
156 bool free_volume(DEVICE *dev)
160 if (dev->VolHdr.VolumeName[0] == 0) {
163 Dmsg1(400, "free_volume %s\n", dev->VolHdr.VolumeName);
164 vol.vol_name = bstrdup(dev->VolHdr.VolumeName);
166 fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
168 vol_list->remove(fvol);
169 free(fvol->vol_name);
174 dev->VolHdr.VolumeName[0] = 0;
178 /* Free volume reserved by this dcr but not attached to a dev */
179 void free_unused_volume(DCR *dcr)
183 for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
184 if (vol->dcr == dcr && (vol->dev == NULL ||
185 strcmp(vol->vol_name, vol->dev->VolHdr.VolumeName) != 0)) {
186 vol_list->remove(vol);
196 * List Volumes -- this should be moved to status.c
198 void list_volumes(BSOCK *user)
201 for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
202 bnet_fsend(user, "%s\n", vol->vol_name);
206 /* Create the Volume list */
207 void create_volume_list()
209 VOLRES *dummy = NULL;
210 if (vol_list == NULL) {
211 vol_list = New(dlist(dummy, &dummy->link));
215 /* Release all Volumes from the list */
216 void free_volume_list()
222 for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
223 Dmsg3(000, "Unreleased Volume=%s dcr=0x%x dev=0x%x\n", vol->vol_name,
230 bool is_volume_in_use(DCR *dcr)
232 VOLRES *vol = find_volume(dcr->VolumeName);
234 return false; /* vol not in list */
236 if (!vol->dev) { /* vol not attached to device */
239 if (dcr->dev == vol->dev) { /* same device OK */
242 if (!vol->dev->is_busy()) {
249 static bool use_storage_cmd(JCR *jcr)
251 POOL_MEM store_name, dev_name, media_type, pool_name, pool_type;
252 BSOCK *dir = jcr->dir_bsock;
262 memset(&rctx, 0, sizeof(RCTX));
265 * If there are multiple devices, the director sends us
266 * use_device for each device that it wants to use.
268 Dmsg1(100, "<dird: %s", dir->msg);
269 jcr->dirstore = New(alist(10, not_owned_by_alist));
271 ok = sscanf(dir->msg, use_storage, store_name.c_str(),
272 media_type.c_str(), pool_name.c_str(),
273 pool_type.c_str(), &append, &Copy, &Stripe) == 7;
277 unbash_spaces(store_name);
278 unbash_spaces(media_type);
279 unbash_spaces(pool_name);
280 unbash_spaces(pool_type);
281 store = new DIRSTORE;
282 jcr->dirstore->append(store);
283 memset(store, 0, sizeof(DIRSTORE));
284 store->device = New(alist(10));
285 bstrncpy(store->name, store_name, sizeof(store->name));
286 bstrncpy(store->media_type, media_type, sizeof(store->media_type));
287 bstrncpy(store->pool_name, pool_name, sizeof(store->pool_name));
288 bstrncpy(store->pool_type, pool_type, sizeof(store->pool_type));
289 store->append = append;
291 /* Now get all devices */
292 while (bnet_recv(dir) >= 0) {
293 ok = sscanf(dir->msg, use_device, dev_name.c_str()) == 1;
297 unbash_spaces(dev_name);
298 store->device->append(bstrdup(dev_name.c_str()));
300 } while (ok && bnet_recv(dir) >= 0);
303 /* This loop is debug code and can be removed */
304 /* ***FIXME**** remove after 1.38 release */
306 foreach_alist(store, jcr->dirstore) {
307 Dmsg4(100, "Storage=%s media_type=%s pool=%s pool_type=%s\n",
308 store->name, store->media_type, store->pool_name,
310 foreach_alist(device_name, store->device) {
311 Dmsg1(100, " Device=%s\n", device_name);
317 * At this point, we have a list of all the Director's Storage
318 * resources indicated for this Job, which include Pool, PoolType,
319 * storage name, and Media type.
320 * Then for each of the Storage resources, we have a list of
321 * device names that were given.
323 * Wiffle through them and find one that can do the backup.
327 * Make up to two passes. The first with PreferMountedVols possibly
328 * set to true. In that case, we look only for an available
329 * drive with something mounted. If that fails, then we
330 * do a second pass with PerferMountedVols set false.
332 rctx.PreferMountedVols = jcr->PreferMountedVols;
333 ok = find_suitable_device_for_job(jcr, rctx);
337 if (rctx.PreferMountedVols) {
338 rctx.PreferMountedVols = false;
339 ok = find_suitable_device_for_job(jcr, rctx);
345 unbash_spaces(dir->msg);
346 pm_strcpy(jcr->errmsg, dir->msg);
347 Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
349 Jmsg(jcr, M_FATAL, 0, _("\n"
350 " Device \"%s\" with MediaType \"%s\" requested by DIR not found in SD Device resources.\n"),
351 dev_name.c_str(), media_type.c_str());
352 bnet_fsend(dir, NO_device, dev_name.c_str());
354 for (error=(char*)rctx->errors.first(); error;
355 error=(char*)rctx->errors.next()) {
356 Jmsg(jcr, M_INFO, 0, "%s", error);
359 Dmsg1(100, ">dird: %s", dir->msg);
361 unbash_spaces(dir->msg);
362 pm_strcpy(jcr->errmsg, dir->msg);
364 Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
366 bnet_fsend(dir, BAD_use, jcr->errmsg);
367 Dmsg1(100, ">dird: %s", dir->msg);
371 foreach_alist(store, jcr->dirstore) {
372 delete store->device;
375 delete jcr->dirstore;
377 for (error=(char*)rctx->errors.first(); error;
378 error=(char*)rctx->errors.next()) {
387 * Search for a device suitable for this job.
389 bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx)
397 init_jcr_device_wait_timers(jcr);
399 int need_wait = false;
402 foreach_alist(store, jcr->dirstore) {
404 foreach_alist(device_name, store->device) {
406 rctx.device_name = device_name;
407 stat = search_res_for_device(rctx);
408 if (stat == 1) { /* found available device */
412 } else if (stat == 0) { /* device busy */
415 /* otherwise error */
416 // rctx->errors.push(bstrdup(jcr->errmsg));
424 * If there is some device for which we can wait, then
425 * wait and try again until the wait time expires
427 if (!need_wait || !wait_for_device(jcr, first)) {
432 for (error=(char*)rctx->errors.first(); error;
433 error=(char*)rctx->errors.next()) {
446 * Search for a particular storage device with particular storage
447 * characteristics (MediaType).
449 static int search_res_for_device(RCTX &rctx)
451 AUTOCHANGER *changer;
452 BSOCK *dir = rctx.jcr->dir_bsock;
456 Dmsg1(100, "Search res for %s\n", rctx.device_name);
457 foreach_res(rctx.device, R_DEVICE) {
458 Dmsg1(100, "Try res=%s\n", rctx.device->hdr.name);
459 /* Find resource, and make sure we were able to open it */
460 if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0) {
461 stat = reserve_device(rctx);
465 Dmsg1(220, "Got: %s", dir->msg);
466 bash_spaces(rctx.device_name);
467 ok = bnet_fsend(dir, OK_device, rctx.device_name);
468 Dmsg1(100, ">dird dev: %s", dir->msg);
472 foreach_res(changer, R_AUTOCHANGER) {
473 Dmsg1(100, "Try changer res=%s\n", changer->hdr.name);
474 /* Find resource, and make sure we were able to open it */
475 if (fnmatch(rctx.device_name, changer->hdr.name, 0) == 0) {
476 /* Try each device in this AutoChanger */
477 foreach_alist(rctx.device, changer->device) {
478 Dmsg1(100, "Try changer device %s\n", rctx.device->hdr.name);
479 stat = reserve_device(rctx);
480 if (stat == -1) { /* hard error */
483 if (stat == 0) { /* must wait, try next one */
487 Dmsg1(100, "Device %s opened.\n", rctx.device_name);
488 pm_strcpy(dev_name, rctx.device->hdr.name);
489 bash_spaces(dev_name);
490 ok = bnet_fsend(dir, OK_device, dev_name.c_str()); /* Return real device name */
491 Dmsg1(100, ">dird changer: %s", dir->msg);
496 return 0; /* nothing found */
500 * Try to reserve a specific device.
502 * Returns: 1 -- OK, have DCR
506 static int reserve_device(RCTX &rctx)
510 const int name_len = MAX_NAME_LENGTH;
512 /* Make sure MediaType is OK */
513 if (strcmp(rctx.device->media_type, rctx.store->media_type) != 0) {
517 if (!rctx.device->dev) {
518 rctx.device->dev = init_dev(rctx.jcr, rctx.device);
520 if (!rctx.device->dev) {
521 if (rctx.device->changer_res) {
522 Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
523 " Device \"%s\" in changer \"%s\" requested by DIR could not be opened or does not exist.\n"),
524 rctx.device->hdr.name, rctx.device_name);
526 Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
527 " Device \"%s\" requested by DIR could not be opened or does not exist.\n"),
530 return -1; /* no use waiting */
532 Dmsg1(100, "Found device %s\n", rctx.device->hdr.name);
533 dcr = new_dcr(rctx.jcr, rctx.device->dev);
535 BSOCK *dir = rctx.jcr->dir_bsock;
536 bnet_fsend(dir, _("3926 Could not get dcr for device: %s\n"), rctx.device_name);
537 Dmsg1(100, ">dird: %s", dir->msg);
541 bstrncpy(dcr->pool_name, rctx.store->pool_name, name_len);
542 bstrncpy(dcr->pool_type, rctx.store->pool_type, name_len);
543 bstrncpy(dcr->media_type, rctx.store->media_type, name_len);
544 bstrncpy(dcr->dev_name, rctx.device_name, name_len);
545 if (rctx.store->append == SD_APPEND) {
546 ok = reserve_device_for_append(dcr, rctx.PreferMountedVols);
547 Dmsg3(200, "dev_name=%s mediatype=%s ok=%d\n", dcr->dev_name, dcr->media_type, ok);
549 ok = reserve_device_for_read(dcr);
552 free_dcr(rctx.jcr->dcr);
559 * We "reserve" the drive by setting the ST_READ bit. No one else
560 * should touch the drive until that is cleared.
561 * This allows the DIR to "reserve" the device before actually
564 static bool reserve_device_for_read(DCR *dcr)
566 DEVICE *dev = dcr->dev;
572 dev->block(BST_DOING_ACQUIRE);
574 if (is_device_unmounted(dev)) {
575 Dmsg1(200, "Device %s is BLOCKED due to user unmount.\n", dev->print_name());
576 Mmsg(jcr->errmsg, _("Device %s is BLOCKED due to user unmount.\n"),
581 if (dev->is_busy()) {
582 Dmsg4(200, "Device %s is busy ST_READ=%d num_writers=%d reserved=%d.\n", dev->print_name(),
583 dev->state & ST_READ?1:0, dev->num_writers, dev->reserved_device);
584 Mmsg1(jcr->errmsg, _("Device %s is busy.\n"),
600 * We reserve the device for appending by incrementing the
601 * reserved_device. We do virtually all the same work that
602 * is done in acquire_device_for_append(), but we do
603 * not attempt to mount the device. This routine allows
604 * the DIR to reserve multiple devices before *really*
605 * starting the job. It also permits the SD to refuse
606 * certain devices (not up, ...).
608 * Note, in reserving a device, if the device is for the
609 * same pool and the same pool type, then it is acceptable.
610 * The Media Type has already been checked. If we are
611 * the first tor reserve the device, we put the pool
612 * name and pool type in the device record.
614 static bool reserve_device_for_append(DCR *dcr, bool PreferMountedVols)
617 DEVICE *dev = dcr->dev;
622 dev->block(BST_DOING_ACQUIRE);
624 if (dev->can_read()) {
625 Mmsg1(jcr->errmsg, _("Device %s is busy reading.\n"), dev->print_name());
626 Dmsg1(100, "%s", jcr->errmsg);
630 if (is_device_unmounted(dev)) {
631 Mmsg(jcr->errmsg, _("Device %s is BLOCKED due to user unmount.\n"), dev->print_name());
632 Dmsg1(100, "%s", jcr->errmsg);
636 Dmsg1(190, "reserve_append device is %s\n", dev->is_tape()?"tape":"disk");
638 if (can_reserve_drive(dcr, PreferMountedVols) != 1) {
639 Mmsg1(jcr->errmsg, _("Device %s is busy writing on another Volume.\n"), dev->print_name());
640 Dmsg1(100, "%s", jcr->errmsg);
644 dev->reserved_device++;
645 Dmsg1(200, "Inc reserve=%d\n", dev->reserved_device);
646 dcr->reserved_device = true;
655 * Returns: 1 if drive can be reserved
656 * 0 if we should wait
659 static int can_reserve_drive(DCR *dcr, bool PreferMountedVols)
661 DEVICE *dev = dcr->dev;
664 if (PreferMountedVols && !dev->VolHdr.VolumeName[0] && dev->is_tape()) {
665 return 0; /* No volume mounted */
669 * Handle the case that there are no writers
671 if (dev->num_writers == 0) {
672 /* Now check if there are any reservations on the drive */
673 if (dev->reserved_device) {
674 /* Yes, now check if we want the same Pool and pool type */
675 if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
676 strcmp(dev->pool_type, dcr->pool_type) == 0) {
677 /* OK, compatible device */
679 /* Drive not suitable for us */
682 } else if (dev->can_append()) {
683 /* Device in append mode, check if changing pool */
684 if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
685 strcmp(dev->pool_type, dcr->pool_type) == 0) {
686 /* OK, compatible device */
688 /* Changing pool, unload old tape if any in drive */
689 unload_autochanger(dcr, 0);
692 /* Device is available but not yet reserved, reserve it for us */
693 bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
694 bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
695 return 1; /* reserve drive */
699 * Check if the device is in append mode with writers (i.e.
700 * available if pool is the same).
702 if (dev->can_append() || dev->num_writers > 0) {
703 Dmsg0(190, "device already in append.\n");
704 /* Yes, now check if we want the same Pool and pool type */
705 if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
706 strcmp(dev->pool_type, dcr->pool_type) == 0) {
707 /* OK, compatible device */
710 /* Drive not suitable for us */
711 Jmsg(jcr, M_WARNING, 0, _("Wanted Pool \"%s\", but device %s is using Pool \"%s\" .\n"),
712 dcr->pool_name, dev->print_name(), dev->pool_name);
716 Pmsg0(000, _("Logic error!!!! Should not get here.\n"));
717 Jmsg0(jcr, M_FATAL, 0, _("Logic error!!!! Should not get here.\n"));
718 return -1; /* error, should not get here */