sndrsyncd.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <thread.h>
#include <locale.h>
#include <langinfo.h>
#include <libintl.h>
#include <stdarg.h>
#include "rdcadm.h"
#define UPDATE "update"
#define NOUPDATE "noupdate"
#define MAXHOSTS 1024
typedef struct host_list_s {
int numhosts;
int configured[MAXHOSTS];
} host_list_t;
extern char *basename(char *);
int rdc_maxsets;
char *program;
static int clustered = 0;
void *wait_sync_event();
void *wait_link_down(void *host);
void remove_from_hostlist(char *host);
void sync_start(char *master);
void sync_complete(char *master);
void cleanup_hostlist();
void group_start(char *group);
void group_complete(char *group);
void
init_host_list(void)
{
gettext("host list not initialized, cannot run"));
}
}
/* ARGSUSED */
#ifdef lint
void
#else
int
#endif
int argc;
char **argv;
{
int size;
int i;
int first = 0;
char *required;
(void) textdomain("rdc");
ustatus = spcs_s_ucreate();
if (rc < 0) {
gettext("unable to determine the current "
/* NOTREACHED */
gettext("incorrect Solaris release (requires %s)\n"),
required);
/* NOTREACHED */
}
clustered = cfg_iscluster();
if (clustered < 0) {
}
if (rdc_maxsets == -1) {
gettext("%s: unable to get maxsets value from kernel"),
program);
gettext("unable to get maxsets value from kernel"));
}
gettext("%s: unable to allocate %ld bytes"),
}
/*
* Fork off a child that becomes the daemon.
*/
exit(0);
else if (pid < 0) {
gettext("%s: cannot fork: %s"),
}
/*
* In child - become daemon.
*/
for (i = 0; i < 3; i++)
(void) close(i);
(void) dup(0);
(void) dup(0);
(void) close(0);
(void) setpgrp();
(void) textdomain("rdc");
/* launch a thread to wait for sync start and sync stop events */
gettext("%s: unable to create thread wait_sync_event"),
program);
gettext("%s unable to create thread wait_sync_event"),
program);
} else {
#ifdef DEBUG
#endif
;
}
for (;;) {
if (!first) {
first++;
(void) sleep(15);
} else
(void) sleep(MAIN_SLEEP);
!= SPCS_S_OK) {
gettext("%s: status ioctl"),
program);
continue;
}
/*
* Check all enabled sets to see if a new remote host has
* appeared.
*/
for (i = 0; i < rdc_maxsets; i++) {
continue;
/* spawn a new thread for each new host found */
/*
* right now, we could be here before
* the database did the write for this set
* I could check the lock on the database
* but I am just going to give up some time here
* instead. Why do the allocations etc, etc
* if the set is enabled in the kernel and not
* in the config, we know that this set has the
* lock. Why bother adding more contention to
* the lock.
* this is a daemon, afterall. its got time
*/
(void) sleep(CFG_WAIT_SLEEP);
gettext("%s: new host found (%s) starting "
"its autosync thread"), program,
if (trc != 0) {
"%s create new autosync "
"thread failed"), program);
"%s create new autosync "
"thread failed"), program);
}
}
}
}
/* NOTREACHED */
}
/*
* The kernel wakes up this function every time it detects the link to the
* specified host has dropped.
*/
void *
wait_link_down(void *thehost)
{
if (host)
ustatus = spcs_s_ucreate();
/* Never give up */
for (;;) {
#ifdef DEBUG
gettext("%s: awaiting link down ioctl for %s"),
#endif
!= SPCS_S_OK) {
gettext("%s: link down ioctl"),
program);
continue;
}
#ifdef DEBUG
gettext("%s: received link down ioctl for %s"),
#endif
}
/* LINTED */
}
/*
* Called when the link to the specified host has dropped.
* For all Remote Mirror sets using the link that have autosync on,
* issue rdcadm -u commands until they complete successfully.
*/
void
{
int sets = 0;
int syncs_done = 0;
char cmd[256];
rdc_config_t parms = { 0 };
int i;
int setnumber;
int numfound = 0;
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int size;
int first = 0;
int death = 0;
int cfglocked = 0;
ustatus = spcs_s_ucreate();
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
size = sizeof (int) * rdc_maxsets;
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
/*
* Get all sndr entries with shost matching tohost, and save the
* details in an array.
*/
for (i = 0; i < rdc_maxsets; i++) {
setnumber = i + 1;
if (!cfglocked) {
LOCKCFG();
gettext("%s: error opening config"),
program);
gettext("error opening config"));
UNLOCKCFG();
goto done;
}
gettext("%s: error locking config"),
program);
goto done;
}
}
cfglocked = 1;
if (numfound == 0) /* no matching hosts */
break;
}
continue;
numfound++;
/* Got a matching entry */
break;
break;
break;
sizeof (rdc_set_t));
/*
* release cfg before diving into the kernel
* this prevents a possible deadlock when doing
* a reverse sync whick will wake up the sync_event
* thread which will try and iiadm -c and hang
* because we still have the cfg_lock. the timed
* wait cv in the kernel will fail the sync and things
* will undeadlock.
*/
cfglocked = 0;
UNLOCKCFG();
continue;
}
continue;
}
/* Found a suitable set with autosync on, in logging mode */
sets++;
}
if (cfg) {
UNLOCKCFG();
}
if (sets == 0) {
#ifdef DEBUG
gettext("%s: no sets requiring autosync found for %s"),
#endif
if (death) {
gettext("%s: autosync thread stopping for %s "
}
goto done;
}
/* Keep issuing rdcadm -u commands until they have all completed */
for (;;) {
if (!first)
first++;
else
(void) sleep(RESYNC_SLEEP);
/* Issue rdcadm -u commands for all remaining sets */
for (i = 0; i < sets; i++) {
if (sync_done[i])
continue;
/*
* Need to check if autosync was turned off for a set
* while we were sleeping. We could have the case where
* an update sync failed and autosync was disabled
* while we were sleeping and didn't detect the disable.
* See BugID 4814213.
*/
ustatus) < 0) {
"status not available for %s:%s, stopping "
sync_done[i] = 1;
syncs_done++;
continue;
}
#ifdef DEBUG
#endif
sync_done[i] = 1;
syncs_done++;
continue;
}
gettext("%s: issuing update sync for %s:%s"),
}
/* Issue rdcadm -w commands to wait for updates to finish */
for (i = 0; i < sets; i++) {
if (sync_done[i])
continue;
gettext("%s: issuing wait for %s:%s"),
ustatus) < 0) {
gettext("%s: status not available for "
"%s:%s, stopping this autosync attempt"),
sync_done[i] = 1;
syncs_done++;
continue;
}
/* Check if completed OK, failed or autosync off */
sync_done[i] = 1;
syncs_done++;
}
}
if (syncs_done == sets)
break; /* All completed OK */
}
done:
if (cfg) {
UNLOCKCFG();
}
if (sync_done)
if (rdc_set)
if (death) { /* bye bye */
/*
* if perhaps we lost some race, lets remove this entry from
* the list. Then, if something did go wrong, and we did kill
* a valid thread, it will be detected on the next go around
* of the thread who is looking for new hosts to spawn threads
*/
thr_exit(0);
}
(void) sleep(RESYNC_SLEEP);
}
/*
* Wait for notification by the kernel of a sync start or a sync completed OK
*/
void *
{
char master[NSC_MAXPATH];
char group[NSC_MAXPATH];
int state;
ustatus = spcs_s_ucreate();
master[0] = '\0';
group[0] = '\0';
/* Never give up */
for (;;) {
/* Kernel tells us which volume and group the event is for */
ustatus);
gettext("%s: update ioctl"),
program);
continue;
}
master[0] = '\0';
continue;
}
/*
* If target is mounted at the start of a sync or reverse sync,
* return a negative ack.
*/
gettext("%s: %s has a file system mounted"),
gettext("%s has a file system mounted"),
master);
continue;
}
switch (state) {
case RDC_SYNC_START:
if (group[0])
else
break;
case RDC_SYNC_DONE:
if (group[0])
else
break;
default:
break;
}
}
/* LINTED */
}
/*
* A sync has completed OK to a volume not belonging to a group.
* Set the state of the ndr_ii config entry to "update".
*/
void
sync_complete(char *master)
{
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int i;
int setnumber;
int sev;
LOCKCFG();
gettext("%s: error opening config"),
program);
UNLOCKCFG();
return;
}
gettext("%s: error locking config"),
program);
UNLOCKCFG();
return;
}
/* get ndr_ii entries until a match is found */
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
/* Found the matching entry */
/*
* Set state to "update" so that starting another sync will
* cause a new Point-in-Time Copy snapshot to be taken.
*/
(cfg_commit(cfg) < 0)) {
gettext("%s: unable to update \"%s\" "
"in configuration storage: %s"),
gettext("unable to update \"%s\" "
"in configuration storage: %s"),
}
break;
}
UNLOCKCFG();
}
/*
* Starting a sync to the specified master volume.
* Check the ndr_ii config entries to see if a Point-in-Time Copy
* snapshot should be taken.
*/
void
sync_start(char *master)
{
char cmd[256];
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int i;
int setnumber;
int found;
int sev;
char shadow[NSC_MAXPATH];
char bitmap[NSC_MAXPATH];
LOCKCFG();
gettext("%s: error opening config"),
program);
gettext("error opening config"));
UNLOCKCFG();
return;
}
gettext("%s: error locking config"),
program);
UNLOCKCFG();
return;
}
found = 0;
/* get ndr_ii entries until a match is found */
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
/* Got a matching entry */
break;
break;
break;
/*
* If an PIT snapshot has already been taken, and syncing did
* not complete, the state will be "noupdate", to indicate we
* should not take another one at this point.
*/
found = 1;
break;
}
if (!found) {
UNLOCKCFG();
return;
}
found = 0;
/* get ii entries until a match is found */
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
/* Matching shadow found, so ii already enabled */
found = 1;
break;
}
if (found) {
/* Already PIT enabled, so just take a snapshot */
/* Get cluster tag of matching entry */
ctag = "-C local";
else
ctag = "";
} else {
/*
* If clustered, need to enable PIT Copy sets in the same
* cluster as the Remote Mirror set
*/
if (clustered) {
/* Find a RM set with master as the local volume */
for (i = 0; i < rdc_maxsets; i++) {
setnumber = i + 1;
CFG_MAX_BUF) < 0)
break;
if (self_check(buf))
else
CFG_MAX_BUF) < 0)
break;
continue;
/* Get cluster tag of matching entry */
CFG_MAX_BUF) < 0)
break;
else
break;
}
}
/* Not already enabled, so enable a dependent */
if (ctag) {
} else
}
gettext("Point-in-Time Copy snapshot failed for %s %s %s."
" Please check validity of ndr_ii entry"),
UNLOCKCFG();
return;
}
/*
* PIT Copy enable or update was fine, so update the ndr_ii entry
* to "noupdate", to prevent invalid point in time copies.
*/
gettext("%s: error opening config"),
program);
gettext("error opening config"));
UNLOCKCFG();
return;
}
gettext("%s: error locking config"),
program);
UNLOCKCFG();
return;
}
/* get ndr_ii entries until a match is found */
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
/* Found the matching entry */
gettext("%s: unable to update \"%s\" "
"in configuration storage: %s"),
gettext("unable to update \"%s\" "
"in configuration storage: %s"),
}
break;
}
UNLOCKCFG();
}
void
{
int i, j, k;
int found = 0;
for (j = 0; (j < rdc_maxsets) && !found; j++) {
continue;
if ((!host_list->configured[i]) ||
return;
}
found++;
}
if (j == rdc_maxsets) {
/*
* this set is not in the kernel, so remove from list
*/
if (exhost) {
}
k = i;
k++;
}
&host_list->configured[i],
(MAXHOSTS - i + 1) * sizeof (int));
}
}
}
/*
* explicity remove a host from the host list
* also update the configured array
* called in rdc_sync, just before exiting a thread.
*/
void
remove_from_hostlist(char *host)
{
int i, k;
char *exhost;
/* why bother? */
return;
if (exhost) {
}
k = i;
k++;
}
&host_list->configured[i],
(MAXHOSTS - i + 1) * sizeof (int));
}
}
}
/*
* Check to see if this host isn't in our list, so needs a new rdcsyncd proc
*/
int
{
int i;
int new;
if (self_check(host)) {
return (0);
}
new = 1;
for (i = 0; i < MAXHOSTS; i++) {
if (host_list->configured[i] == 0) {
break;
}
new = 0;
break;
}
}
if (i == MAXHOSTS)
new = 0;
return (new);
}
/*
* Look for a matching volume name in our remembered list.
*/
int
{
int i;
char *vol;
for (i = 0; i < volumes; i++) {
vol = volume_list[i];
return (1);
}
}
return (0);
}
/*
* A sync has completed to a group. We can only update the ndr_ii entries
* if all the members of the group have completed their syncs OK.
* It would be bad to allow some members of the group to have PIT Copy snapshots
* taken and others not, as they need to be consistent.
*/
void
group_complete(char *group)
{
rdc_config_t parms = { 0 };
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int i;
int setnumber;
int found;
int replicating = 0;
char primary[NSC_MAXPATH];
char secondary[NSC_MAXPATH];
char phost[MAX_RDC_HOST_SIZE];
char shost[MAX_RDC_HOST_SIZE];
int sev;
char *local_file;
int size;
ustatus = spcs_s_ucreate();
size = sizeof (char *) * rdc_maxsets;
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
/*
* If all members of this group are replicating
* set ii_ndr state to "update". Otherwise leave them alone.
*/
LOCKCFG();
gettext("%s: error opening lconfig"),
program);
UNLOCKCFG();
goto done;
}
gettext("%s: error locking config"),
program);
goto done;
}
found = 0;
/* get all RM entries, with a matching group, that are replicating */
for (i = 0; i < rdc_maxsets; i++) {
setnumber = i + 1;
break;
continue;
/* Found a matching entry */
break;
break;
sizeof (secondary)) < 0)
break;
break;
continue;
}
/* We found a matching set */
found++;
if (self_check(phost))
else
gettext("%s: unable to allocate %ld bytes"),
gettext("unable to allocate %ld bytes"),
size);
goto done;
}
/* We remember all replicating sets */
replicating++;
} else
break; /* Not all replicating, so done */
}
if (found != replicating)
goto done;
/* All replicating, so update ndr_ii state fields */
gettext("%s: error locking lconfig"),
program);
goto done;
}
/*
* Search through the ndr_ii entries for entries
* that match the saved secondary volume names.
* Set state to "update".
*/
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
}
/* Got a matching entry */
(cfg_commit(cfg) < 0)) {
gettext("%s: unable to update \"%s\" "
"in configuration storage: %s"),
gettext("unable to update \"%s\" "
"in configuration storage: %s"),
}
}
done:
if (cfg) {
UNLOCKCFG();
}
if (volumes) {
for (i = 0; i < replicating; i++)
}
}
/*
* Sync started to a member of a group.
* If all members of the group are in ndr_ii state "update" then take an PIT
* snapshot on all of them. This will provide a consistent point-in-time
* copy until whatever syncs take place are all completed.
*/
void
group_start(char *group)
{
char cmd[256];
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int i;
int j;
int setnumber;
int found;
int sndr_sets = 0;
int update_needed = 0;
int sev;
int commit = 0;
int size;
size = sizeof (char *) * rdc_maxsets;
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
LOCKCFG();
gettext("%s: error opening config"),
program);
gettext("error opening config"));
UNLOCKCFG();
goto done;
}
gettext("%s: error locking config"),
program);
goto done;
}
/* Now get all Remote Mirror entries with a matching group */
for (i = 0; i < rdc_maxsets; i++) {
setnumber = i + 1;
break;
continue;
/* Found a matching entry */
break;
if (self_check(buf)) {
} else {
}
break;
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
sndr_sets++;
/* Get cluster tag of matching entry */
}
}
/*
* Search through the ndr_ii entries for entries
* that match the saved local volume names and are in "update" state.
*/
update_needed = 0;
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
/* Got a matching entry */
break;
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
break;
}
gettext("%s: unable to allocate %ld bytes"),
goto done;
}
break;
}
break;
}
}
if (update_needed != sndr_sets) {
#ifdef DEBUG
gettext("%s: group sync: no Point-in-Time Copy snapshot "
#endif
goto done;
}
/* All RM sets in the group have an ndr_ii entry in "update" state */
/* Issue PIT Copy snapshot commands for all sets in the group */
for (j = 0; j < sndr_sets; j++) {
found = 0;
/* get ii entries until a match is found */
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
/* Matching shadow found, so ii already enabled */
found = 1;
break;
}
if (commit)
if (cfg_commit(cfg) < 0)
if (found) {
} else {
if (ctag) {
bitmaps[j]);
} else
}
gettext("%s: group sync: Point-in-Time Copy"
" snapshot failed for %s"),
goto done;
}
gettext("%s: error opening config"),
program);
gettext("error opening config"));
goto done;
}
gettext("%s: error locking config"),
program);
goto done;
}
commit = 0;
/* PIT enable or update was fine, so update the ndr_ii entry */
/* get ndr_ii entries until a match is found */
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
/* Found the matching entry */
gettext("%s: unable to update \"%s\" "
"in configuration storage: %s"),
gettext("unable to update \"%s\" "
"in configuration storage: %s"),
} else
commit = 1;
break;
}
}
if (commit)
if (cfg_commit(cfg) < 0)
gettext("%s: group sync: Point-in-Time Copy snapshots completed "
done:
if (ctag)
if (cfg) {
UNLOCKCFG();
}
if (masters) {
for (i = 0; i < sndr_sets; i++) {
if (masters[i])
}
}
if (shadows) {
for (i = 0; i < update_needed; i++) {
if (shadows[i])
}
}
if (bitmaps) {
for (i = 0; i < update_needed; i++) {
if (bitmaps[i])
}
}
}