/** \brief Generates the continuum of servers (each server as many points on a circle). * \param key Shared memory key for storing the newly created continuum. * \param filename Server definition file, which will be parsed to create this continuum. * \return 0 on failure, 1 on success. */ static int ketama_create_continuum( key_t key, char* filename ) { int shmid; int* data; /* Pointer to shmem location */ unsigned int numservers = 0; unsigned long memory; serverinfo* slist; slist = read_server_definitions( filename, &numservers, &memory ); /* Check numservers first; if it is zero then there is no error message * and we need to set one. */ if ( numservers < 1 ) { sprintf( k_error, "No valid server definitions in file %s", filename ); return 0; } else if ( slist == 0 ) { /* read_server_definitions must've set error message. */ return 0; } #ifdef DEBUG syslog( LOG_INFO, "Server definitions read: %u servers, total memory: %lu.\n", numservers, memory ); #endif /* Continuum will hold one mcs for each point on the circle: */ mcs continuum[ numservers * 160 ]; unsigned int i, k, cont = 0; for( i = 0; i < numservers; i++ ) { float pct = (float)slist[i].memory / (float)memory; unsigned int ks = floorf( pct * 40.0 * (float)numservers ); #ifdef DEBUG int hpct = floorf( pct * 100.0 ); syslog( LOG_INFO, "Server no. %d: %s (mem: %lu = %u%% or %d of %d)\n", i, slist[i].addr, slist[i].memory, hpct, ks, numservers * 40 ); #endif for( k = 0; k < ks; k++ ) { /* 40 hashes, 4 numbers per hash = 160 points per server */ char ss[30]; unsigned char digest[16]; sprintf( ss, "%s-%d", slist[i].addr, k ); ketama_md5_digest( ss, digest ); /* Use successive 4-bytes from hash as numbers * for the points on the circle: */ int h; for( h = 0; h < 4; h++ ) { continuum[cont].point = ( digest[3+h*4] << 24 ) | ( digest[2+h*4] << 16 ) | ( digest[1+h*4] << 8 ) | digest[h*4]; memcpy( continuum[cont].ip, slist[i].addr, 22 ); cont++; } } } free( slist ); /* Sorts in ascending order of "point" */ qsort( (void*) &continuum, cont, sizeof( mcs ), (compfn)ketama_compare ); /* Add data to shmmem */ shmid = shmget( key, MC_SHMSIZE, 0644 | IPC_CREAT ); data = shmat( shmid, (void *)0, 0 ); if ( data == (void *)(-1) ) { strcpy( k_error, "Can't open shmmem for writing." ); return 0; } time_t modtime = file_modtime( filename ); int nump = cont; memcpy( data, &nump, sizeof( int ) ); memcpy( data + 1, &modtime, sizeof( time_t ) ); memcpy( data + 1 + sizeof( void* ), &continuum, sizeof( mcs ) * nump ); /* We detatch here because we will re-attach in read-only * mode to actually use it. */ if ( shmdt( data ) == -1 ) strcpy( k_error, "Error detatching from shared memory!" ); return 1; }
/** \brief Generates the continuum of servers (each server as many points on a circle). * \param key Shared memory key for storing the newly created continuum. * \param filename Server definition file, which will be parsed to create this continuum. * \param contptr The value of this pointer will contain the retrieved continuum resource. * \return 0 on failure, 1 on success. */ static int ketama_create_continuum(key_t key, char* filename, ketama_continuum* contptr) { int shmid, sem_set_id; int numservers = 0; unsigned long memory; serverinfo* slist; continuum* data; // if filename is not a user-specific key, init a shared memory segment from the pathname, if it's valid if (strncmp(filename, "key:", 4) != 0) { // get the list of servers from the provided file slist = read_server_definitions( filename, &numservers, &memory ); // Check numservers first; if it is zero then there is no error message and we need to set one. if ( numservers < 1 ) { snprintf( k_error, sizeof(k_error), "Ketama: No valid server definitions in file %s", filename ); syslog1( LOG_INFO, k_error ); return 0; } else if ( slist == NULL ) { // read_server_definitions must've set error message. return 0; } // log the sucessful file read syslog( LOG_INFO, "Ketama: Server definitions read: %u servers, total memory: %lu.\n", numservers, memory ); time_t fmodtime = file_modtime( filename ); // attempt to load the continuum and log an error if this fails if ( !load_continuum( key, slist, numservers, memory, fmodtime ) ) { snprintf( k_error, sizeof(k_error), "Ketama: ketama_create_continuum failed to load the continuum.\n" ); syslog1( LOG_INFO, k_error ); return 0; } } // attempt to obtain the shared memory ID assigned to this key, and create a segment if it doesn't exist shmid = shmget( key, MC_SHMSIZE, 0644 | IPC_CREAT ); if ( shmid == -1 ) { snprintf( k_error, sizeof(k_error), "Ketama: ketama_create_continuum failed to get valid shared memory segment with errno: %d.\n", errno ); syslog1( LOG_INFO, k_error ); return 0; } // lock the semaphore to prevent other writing to the shared memory segment sem_set_id = ketama_sem_init( key ); ketama_sem_safely_lock( sem_set_id ); // create an attachment in virtual memory to the shared memory segment, and failout if that an error is returned data = (continuum*) shmat( shmid, (void *)0, 0 ); if ( data == (void *)(-1) ) { snprintf( k_error, sizeof(k_error), "Ketama: ketama_create_continuum failed to attach writable shared memory segment with errno: %d.\n", errno ); syslog1( LOG_INFO, k_error ); ketama_sem_unlock( sem_set_id ); return 0; } // if filename is not a path, init an empty shared memory segment, otherwise just record the filename if (strncmp(filename, "key:", 4) == 0) { data->fmodtime = 0; data->cont_version = 1; data->cont_modtime = time(NULL); } snprintf(data->cont_filename, sizeof(data->cont_filename), "%s", filename); // attempt to detach the shared memory segment and throw an error if one is received if (ketama_shmdt(data) == -1) { snprintf( k_error, sizeof(k_error), "Ketama: ketama_create_continuum failed to detatch from shared memory with errno: %d.\n", errno ); syslog1( LOG_INFO, k_error ); } // unlock the semaphore ketama_sem_unlock( sem_set_id ); // rerun ketama_roller with the roller flag on to validate the loaded continuum return ketama_roller( contptr, filename, 1); }